diff --git a/.github/workflows/Dockerfile.tools b/.github/workflows/Dockerfile.tools index df5892618a7..5d365fc56d9 100644 --- a/.github/workflows/Dockerfile.tools +++ b/.github/workflows/Dockerfile.tools @@ -1,4 +1,4 @@ -FROM ghcr.io/openwrt/buildbot/buildworker-v3.8.0:v2 +FROM ghcr.io/openwrt/buildbot/buildworker-v3.8.0:v6 COPY --chown=buildbot staging_dir/host /prebuilt_tools/staging_dir/host COPY --chown=buildbot build_dir/host /prebuilt_tools/build_dir/host diff --git a/.github/workflows/build-tools.yml b/.github/workflows/build-tools.yml index 9b865f2608f..0629b1b62ad 100644 --- a/.github/workflows/build-tools.yml +++ b/.github/workflows/build-tools.yml @@ -13,7 +13,7 @@ jobs: build: name: Build tools runs-on: ubuntu-latest - container: ghcr.io/openwrt/buildbot/buildworker-v3.8.0:v2 + container: ghcr.io/openwrt/buildbot/buildworker-v3.8.0:v6 steps: - name: Checkout diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78f856962e6..367a43383bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,14 @@ on: type: boolean upload_external_toolchain: type: boolean + use_ccache_cache: + type: boolean + default: true + ccache_type: + type: string + default: kernel + upload_ccache_cache: + type: boolean permissions: contents: read @@ -64,7 +72,6 @@ jobs: runs-on: ubuntu-latest outputs: owner_lc: ${{ steps.lower_owner.outputs.owner_lc }} - ccache_hash: ${{ steps.ccache_hash.outputs.ccache_hash }} container_tag: ${{ steps.determine_tools_container.outputs.container_tag }} container_name: ${{ steps.determine_tools_container.outputs.container_name }} @@ -84,13 +91,6 @@ jobs: echo "owner_lc=$OWNER_LC" >> $GITHUB_OUTPUT - - name: Generate ccache hash - id: ccache_hash - run: | - CCACHE_HASH=$(md5sum include/kernel-* | awk '{ print $1 }' \ - | md5sum | awk '{ print $1 }') - echo "ccache_hash=$CCACHE_HASH" >> $GITHUB_OUTPUT - # Per branch tools container tag # By default stick to latest # For official test targetting openwrt stable branch @@ -144,6 +144,7 @@ jobs: permissions: contents: read packages: read + actions: write steps: - name: Checkout master directory @@ -233,6 +234,20 @@ jobs: echo "TOOLCHAIN_FILE=$TOOLCHAIN_FILE" >> "$GITHUB_ENV" echo "TOOLCHAIN_PATH=$TOOLCHAIN_PATH" >> "$GITHUB_ENV" + - name: Download and extract ccache cache from s3 + id: restore-ccache-cache-s3 + if: inputs.use_ccache_cache == true + working-directory: openwrt + run: | + ENDPOINT=https://storage.googleapis.com + BUCKET=openwrt-ci-cache + CCACHE_TAR=ccache-${{ inputs.ccache_type }}-${{ inputs.target }}-${{ inputs.subtarget }}.tar + + if curl -o /dev/null -s --head --fail $ENDPOINT/$BUCKET/$CCACHE_TAR; then + wget -O - $ENDPOINT/$BUCKET/$CCACHE_TAR | tar -xf - + echo "cache-hit=true" >> $GITHUB_OUTPUT + fi + - name: Fix permission run: | chown -R buildbot:buildbot openwrt @@ -255,21 +270,33 @@ jobs: ./scripts/feeds update -a ./scripts/feeds install -a - - name: Cache ccache - uses: actions/cache@v3 + - name: Restore ccache cache + id: restore-ccache-cache + if: inputs.use_ccache_cache == true && steps.restore-ccache-cache-s3.outputs.cache-hit != 'true' + uses: actions/cache/restore@v3 with: path: openwrt/.ccache - key: ccache-kernel-${{ inputs.target }}/${{ inputs.subtarget }}-${{ needs.setup_build.outputs.ccache_hash }} + key: ccache-${{ inputs.ccache_type }}-${{ inputs.target }}/${{ inputs.subtarget }}-${{ hashFiles('openwrt/include/kernel-**') }} restore-keys: | - ccache-kernel-${{ inputs.target }}/${{ inputs.subtarget }}- + ccache-${{ inputs.ccache_type }}-${{ inputs.target }}/${{ inputs.subtarget }}- + + - name: Import GPG keys + shell: su buildbot -c "sh -e {0}" + if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type != 'internal' && steps.parse-toolchain.outputs.toolchain-type != 'external_container' + run: gpg --receive-keys 0xCD84BCED626471F1 0x1D53D1877742E911 0xCD54E82DADB3684D - name: Download external toolchain/sdk if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type != 'internal' && steps.parse-toolchain.outputs.toolchain-type != 'external_container' shell: su buildbot -c "sh -e {0}" working-directory: openwrt run: | - wget -O - https://downloads.cdn.openwrt.org/${{ env.TOOLCHAIN_PATH }}/targets/${{ inputs.target }}/${{ inputs.subtarget }}/${{ env.TOOLCHAIN_FILE }}.tar.xz \ - | tar --xz -xf - + wget https://downloads.cdn.openwrt.org/${{ env.TOOLCHAIN_PATH }}/targets/${{ inputs.target }}/${{ inputs.subtarget }}/${{ env.TOOLCHAIN_FILE }}.tar.xz + wget https://downloads.cdn.openwrt.org/${{ env.TOOLCHAIN_PATH }}/targets/${{ inputs.target }}/${{ inputs.subtarget }}/sha256sums.asc + wget https://downloads.cdn.openwrt.org/${{ env.TOOLCHAIN_PATH }}/targets/${{ inputs.target }}/${{ inputs.subtarget }}/sha256sums + gpg --with-fingerprint --verify sha256sums.asc + sha256sum --check --ignore-missing sha256sums + tar --xz -xf ${{ env.TOOLCHAIN_FILE }}.tar.xz + rm ${{ env.TOOLCHAIN_FILE }}.tar.xz sha256sums - name: Configure testing kernel if: inputs.testing == true @@ -301,6 +328,24 @@ jobs: echo CONFIG_TARGET_PER_DEVICE_ROOTFS=y >> .config echo CONFIG_TARGET_ALL_PROFILES=y >> .config + # ccache for some reason have problem detecting compiler type + # with external toolchain. This cause the complete malfunction + # of ccache with the result of tons of unsupported compiler + # option error. + # To fix this force compiler type to gcc. + - name: Configure ccache and apply fixes + if: inputs.use_ccache_cache == true + shell: su buildbot -c "sh -e {0}" + working-directory: openwrt + env: + SYSTEM_CCACHE_CONF: staging_dir/host/etc/ccache.conf + run: | + touch $SYSTEM_CCACHE_CONF + + echo compiler_type=gcc >> $SYSTEM_CCACHE_CONF + + echo CONFIG_CCACHE=y >> .config + - name: Configure external toolchain in container if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type == 'external_container' shell: su buildbot -c "sh -e {0}" @@ -308,7 +353,6 @@ jobs: run: | echo CONFIG_DEVEL=y >> .config echo CONFIG_AUTOREMOVE=y >> .config - echo CONFIG_CCACHE=y >> .config ./scripts/ext-toolchain.sh \ --toolchain /external-toolchain/$(ls /external-toolchain/ | grep openwrt-toolchain)/toolchain-* \ @@ -322,7 +366,6 @@ jobs: run: | echo CONFIG_DEVEL=y >> .config echo CONFIG_AUTOREMOVE=y >> .config - echo CONFIG_CCACHE=y >> .config ./scripts/ext-toolchain.sh \ --toolchain ${{ env.TOOLCHAIN_FILE }}/toolchain-* \ @@ -364,7 +407,6 @@ jobs: run: | echo CONFIG_DEVEL=y >> .config echo CONFIG_AUTOREMOVE=y >> .config - echo CONFIG_CCACHE=y >> .config ./scripts/ext-toolchain.sh \ --toolchain ${{ env.TOOLCHAIN_FILE }}/staging_dir/toolchain-* \ @@ -378,7 +420,6 @@ jobs: run: | echo CONFIG_DEVEL=y >> .config echo CONFIG_AUTOREMOVE=y >> .config - echo CONFIG_CCACHE=y >> .config echo "CONFIG_TARGET_${{ inputs.target }}=y" >> .config echo "CONFIG_TARGET_${{ inputs.target }}_${{ inputs.subtarget }}=y" >> .config @@ -482,6 +523,41 @@ jobs: name: ${{ inputs.target }}-${{ inputs.subtarget }}-logs path: "openwrt/logs" + - name: Delete already present ccache cache + if: steps.restore-ccache-cache.outputs.cache-hit == 'true' && inputs.use_ccache_cache == true && + github.event_name == 'push' && steps.restore-ccache-cache-s3.outputs.cache-hit != 'true' + uses: octokit/request-action@v2.x + with: + route: DELETE /repos/{repository}/actions/caches?key={key} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INPUT_REPOSITORY: ${{ github.repository }} + INPUT_KEY: ${{ steps.restore-ccache-cache.outputs.cache-primary-key }} + + - name: Save ccache cache + if: inputs.use_ccache_cache == true && github.event_name == 'push' && + steps.restore-ccache-cache-s3.outputs.cache-hit != 'true' + uses: actions/cache/save@v3 + with: + path: openwrt/.ccache + key: ${{ steps.restore-ccache-cache.outputs.cache-primary-key }} + + - name: Archive ccache + if: inputs.use_ccache_cache == true && github.event_name == 'push' && + inputs.upload_ccache_cache == true + shell: su buildbot -c "sh -e {0}" + working-directory: openwrt + run: tar -cf ccache-${{ inputs.ccache_type }}-${{ inputs.target }}-${{ inputs.subtarget }}.tar .ccache + + - name: Upload ccache cache + if: inputs.use_ccache_cache == true && github.event_name == 'push' && + inputs.upload_ccache_cache == true + uses: actions/upload-artifact@v3 + with: + name: ${{ inputs.target }}-${{ inputs.subtarget }}-ccache-cache + path: openwrt/ccache-${{ inputs.ccache_type }}-${{ inputs.target }}-${{ inputs.subtarget }}.tar + retention-days: 1 + - name: Find external toolchain name id: get-toolchain-name if: inputs.upload_external_toolchain == true diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 10355456321..72f39a729bb 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -15,6 +15,7 @@ jobs: permissions: contents: read packages: read + actions: write uses: ./.github/workflows/build.yml with: container_name: toolchain @@ -22,6 +23,7 @@ jobs: subtarget: 64 build_full: true include_feeds: true + use_ccache_cache: false coverity_compiler_template_list: >- x86_64-openwrt-linux-gcc x86_64-openwrt-linux-musl-gcc diff --git a/.github/workflows/kernel.yml b/.github/workflows/kernel.yml index 6be7ef28c2f..3aa46f5cd7c 100644 --- a/.github/workflows/kernel.yml +++ b/.github/workflows/kernel.yml @@ -54,10 +54,12 @@ jobs: TARGETS_SUBTARGETS="$(echo "$ALL_TARGETS" | sort -u -t '/' -k1 | awk '{ print $1 }')" TARGETS="$(echo "$ALL_TARGETS" | sort -u -t '/' -k1,1 | awk '{ print $1 }')" - # On testing non-specific target, skip testing each subtarget - if echo "$CHANGED_FILES" | grep -v -q target/linux || - echo "$CHANGED_FILES" | grep -q target/linux/generic; then - TARGETS_SUBTARGETS=$TARGETS + # On testing non-specific target, skip testing each subtarget if we are testing pr + if [ ${{ github.event_name }} != 'push' ]; then + if echo "$CHANGED_FILES" | grep -v -q target/linux || + echo "$CHANGED_FILES" | grep -q target/linux/generic; then + TARGETS_SUBTARGETS=$TARGETS + fi fi JSON_TARGETS_SUBTARGETS='[' @@ -105,6 +107,7 @@ jobs: permissions: contents: read packages: read + actions: write strategy: fail-fast: False matrix: @@ -116,6 +119,7 @@ jobs: subtarget: ${{ matrix.subtarget }} build_kernel: true build_all_kmods: true + upload_ccache_cache: ${{ github.repository_owner == 'openwrt' }} check-kernel-patches: name: Check Kernel patches @@ -132,3 +136,20 @@ jobs: target: ${{ matrix.target }} subtarget: ${{ matrix.subtarget }} + upload-ccache-cache-in-s3: + if: github.event_name == 'push' && github.repository_owner == 'openwrt' + name: Upload ccache cache to s3 + needs: [determine_targets, build] + strategy: + fail-fast: False + matrix: + include: ${{fromJson(needs.determine_targets.outputs.targets_subtargets)}} + secrets: + s3_access_key: ${{ secrets.GCS_S3_ACCESS_KEY }} + s3_secret_key: ${{ secrets.GCS_S3_SECRET_KEY }} + uses: ./.github/workflows/upload-file-s3.yml + with: + endpoint: https://storage.googleapis.com + bucket: openwrt-ci-cache + download_id: ${{ matrix.target }}-${{ matrix.subtarget }}-ccache-cache + filename: ccache-kernel-${{ matrix.target }}-${{ matrix.subtarget }}.tar diff --git a/.github/workflows/label-kernel.yml b/.github/workflows/label-kernel.yml index 243d285e289..eab79cf0c0c 100644 --- a/.github/workflows/label-kernel.yml +++ b/.github/workflows/label-kernel.yml @@ -12,17 +12,84 @@ jobs: name: Set target runs-on: ubuntu-latest outputs: - target: ${{ steps.set_target.outputs.target }} - subtarget: ${{ steps.set_target.outputs.subtarget }} + targets_subtargets: ${{ steps.set_target.outputs.targets_subtargets }} + targets: ${{ steps.set_target.outputs.targets }} steps: - - name: Set target - id: set_target + - name: Checkout + uses: actions/checkout@v3 + + - name: Parse label + id: parse_label env: CI_EVENT_LABEL_NAME: ${{ github.event.label.name }} run: | - echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/target="\1"/p' | tee --append $GITHUB_OUTPUT - echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/subtarget="\2"/p' | tee --append $GITHUB_OUTPUT + echo "$CI_EVENT_LABEL_NAME" | sed -n 's/ci:kernel:\([^:]*\):\([^:]*\):*\([^:]*\)$/target=\1/p' | tee --append $GITHUB_OUTPUT + echo "$CI_EVENT_LABEL_NAME" | sed -n 's/ci:kernel:\([^:]*\):\([^:]*\):*\([^:]*\)$/subtarget=\2/p' | tee --append $GITHUB_OUTPUT + echo "$CI_EVENT_LABEL_NAME" | sed -n 's/ci:kernel:\([^:]*\):\([^:]*\):*\([^:]*\)$/testing=\3/p' | tee --append $GITHUB_OUTPUT + + - name: Set targets + id: set_target + run: | + ALL_TARGETS="$(perl ./scripts/dump-target-info.pl kernels 2>/dev/null)" + + TARGETS_SUBTARGETS="$(echo "$ALL_TARGETS" | sort -u -t '/' -k1)" + TARGETS="$(echo "$ALL_TARGETS" | sort -u -t '/' -k1,1)" + + [ "${{ steps.parse_label.outputs.subtarget }}" = "first" ] && TARGETS_SUBTARGETS=$TARGETS + + JSON_TARGETS_SUBTARGETS='[' + FIRST=1 + while IFS= read -r line; do + TARGET_SUBTARGET=$(echo $line | cut -d " " -f 1) + TARGET=$(echo $TARGET_SUBTARGET | cut -d "/" -f 1) + SUBTARGET=$(echo $TARGET_SUBTARGET | cut -d "/" -f 2) + + [ "${{ steps.parse_label.outputs.target }}" != "all" ] && [ "${{ steps.parse_label.outputs.target }}" != "$TARGET" ] && continue + [ "${{ steps.parse_label.outputs.subtarget }}" != "all" ] && [ "${{ steps.parse_label.outputs.subtarget }}" != "first" ] && + [ "${{ steps.parse_label.outputs.subtarget }}" != $SUBTARGET ] && continue + if [ "${{ steps.parse_label.outputs.testing }}" = "testing" ]; then + TESTING_KERNEL_VER=$(echo $line | cut -d " " -f 3) + [ -z "$TESTING_KERNEL_VER" ] && continue + fi + + TUPLE='{"target":"'"$TARGET"'","subtarget":"'"$SUBTARGET"'","testing":"'"$TESTING_KERNEL_VER"'"}' + [[ $FIRST -ne 1 ]] && JSON_TARGETS_SUBTARGETS="$JSON_TARGETS_SUBTARGETS"',' + JSON_TARGETS_SUBTARGETS="$JSON_TARGETS_SUBTARGETS""$TUPLE" + FIRST=0 + done <<< "$TARGETS_SUBTARGETS" + JSON_TARGETS_SUBTARGETS="$JSON_TARGETS_SUBTARGETS"']' + + JSON_TARGETS='[' + FIRST=1 + while IFS= read -r line; do + TARGET_SUBTARGET=$(echo $line | cut -d " " -f 1) + TARGET=$(echo $TARGET_SUBTARGET | cut -d "/" -f 1) + SUBTARGET=$(echo $TARGET_SUBTARGET | cut -d "/" -f 2) + + [ "${{ steps.parse_label.outputs.target }}" != "all" ] && [ "${{ steps.parse_label.outputs.target }}" != $TARGET ] && continue + if [ "${{ steps.parse_label.outputs.testing }}" = "testing" ]; then + TESTING_KERNEL_VER=$(echo $line | cut -d " " -f 3) + [ -z "$TESTING_KERNEL_VER" ] && continue + fi + + TUPLE='{"target":"'"$TARGET"'","subtarget":"'"$SUBTARGET"'","testing":"'"$TESTING_KERNEL_VER"'"}' + [[ $FIRST -ne 1 ]] && JSON_TARGETS="$JSON_TARGETS"',' + JSON_TARGETS="$JSON_TARGETS""$TUPLE" + FIRST=0 + done <<< "$TARGETS" + JSON_TARGETS="$JSON_TARGETS"']' + + echo -e "\n---- targets to build ----\n" + echo "$JSON_TARGETS_SUBTARGETS" + echo -e "\n---- targets to build ----\n" + + echo -e "\n---- targets to check patch ----\n" + echo "$JSON_TARGETS" + echo -e "\n---- targets to check patch ----\n" + + echo "targets_subtargets=$JSON_TARGETS_SUBTARGETS" >> $GITHUB_OUTPUT + echo "targets=$JSON_TARGETS" >> $GITHUB_OUTPUT build_kernel: name: Build Kernel with external toolchain @@ -30,10 +97,17 @@ jobs: permissions: contents: read packages: read + actions: write uses: ./.github/workflows/build.yml + strategy: + fail-fast: False + matrix: + include: ${{fromJson(needs.set_target.outputs.targets_subtargets)}} with: - target: ${{ needs.set_target.outputs.target }} - subtarget: ${{ needs.set_target.outputs.subtarget }} + container_name: toolchain + target: ${{ matrix.target }} + subtarget: ${{ matrix.subtarget }} + testing: ${{ matrix.testing != '' && true }} build_kernel: true build_all_kmods: true @@ -43,7 +117,13 @@ jobs: permissions: contents: read packages: read + actions: write + strategy: + fail-fast: False + matrix: + include: ${{fromJson(needs.set_target.outputs.targets)}} uses: ./.github/workflows/check-kernel-patches.yml with: - target: ${{ needs.set_target.outputs.target }} - subtarget: ${{ needs.set_target.outputs.subtarget }} + target: ${{ matrix.target }} + subtarget: ${{ matrix.subtarget }} + testing: ${{ matrix.testing != '' && true }} diff --git a/.github/workflows/label-target.yml b/.github/workflows/label-target.yml index 3a624598b1e..610aab026aa 100644 --- a/.github/workflows/label-target.yml +++ b/.github/workflows/label-target.yml @@ -21,8 +21,8 @@ jobs: env: CI_EVENT_LABEL_NAME: ${{ github.event.label.name }} run: | - echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/target="\1"/p' | tee --append $GITHUB_OUTPUT - echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/subtarget="\2"/p' | tee --append $GITHUB_OUTPUT + echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/target=\1/p' | tee --append $GITHUB_OUTPUT + echo "$CI_EVENT_LABEL_NAME" | sed -n 's/.*:\(.*\):\(.*\)$/subtarget=\2/p' | tee --append $GITHUB_OUTPUT build_target: name: Build target @@ -30,8 +30,10 @@ jobs: permissions: contents: read packages: read + actions: write uses: ./.github/workflows/build.yml with: + container_name: toolchain target: ${{ needs.set_target.outputs.target }} subtarget: ${{ needs.set_target.outputs.subtarget }} build_full: true diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index d4d3565cd70..fe2898cacdb 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -35,6 +35,7 @@ jobs: permissions: contents: read packages: read + actions: write strategy: fail-fast: False matrix: @@ -52,4 +53,28 @@ jobs: build_all_kmods: true build_all_modules: true build_full: true + ccache_type: packages + upload_ccache_cache: ${{ github.repository_owner == 'openwrt' }} + + upload-ccache-cache-in-s3: + if: github.event_name == 'push' && github.repository_owner == 'openwrt' + name: Upload ccache cache to s3 + needs: build + strategy: + fail-fast: False + matrix: + include: + - target: malta + subtarget: be + - target: x86 + subtarget: 64 + secrets: + s3_access_key: ${{ secrets.GCS_S3_ACCESS_KEY }} + s3_secret_key: ${{ secrets.GCS_S3_SECRET_KEY }} + uses: ./.github/workflows/upload-file-s3.yml + with: + endpoint: https://storage.googleapis.com + bucket: openwrt-ci-cache + download_id: ${{ matrix.target }}-${{ matrix.subtarget }}-ccache-cache + filename: ccache-packages-${{ matrix.target }}-${{ matrix.subtarget }}.tar diff --git a/.github/workflows/push-containers.yml b/.github/workflows/push-containers.yml index eb6ffd30c1e..1678c5ad4fe 100644 --- a/.github/workflows/push-containers.yml +++ b/.github/workflows/push-containers.yml @@ -153,6 +153,7 @@ jobs: permissions: contents: read packages: read + actions: write strategy: fail-fast: False matrix: diff --git a/.github/workflows/toolchain.yml b/.github/workflows/toolchain.yml index 54e31069e6f..4f4d78f4f8a 100644 --- a/.github/workflows/toolchain.yml +++ b/.github/workflows/toolchain.yml @@ -61,6 +61,7 @@ jobs: permissions: contents: read packages: read + actions: write strategy: fail-fast: False matrix: diff --git a/.github/workflows/upload-file-s3.yml b/.github/workflows/upload-file-s3.yml new file mode 100644 index 00000000000..6bcb172468d --- /dev/null +++ b/.github/workflows/upload-file-s3.yml @@ -0,0 +1,46 @@ +name: Upload File to S3 + +on: + workflow_call: + secrets: + s3_access_key: + s3_secret_key: + inputs: + endpoint: + required: true + type: string + bucket: + required: true + type: string + download_id: + required: true + type: string + filename: + required: true + type: string + +jobs: + upload-file-in-s3: + name: Upload file in S3 + runs-on: ubuntu-latest + + steps: + - name: Install minio + run: | + curl https://dl.min.io/client/mc/release/linux-amd64/mc \ + --create-dirs \ + -o $GITHUB_WORKSPACE/minio-binaries/mc + + chmod +x $GITHUB_WORKSPACE/minio-binaries/mc + echo $GITHUB_WORKSPACE/minio-binaries/ >> $GITHUB_PATH + + - name: Setup minio + run: mc alias set s3 ${{ inputs.endpoint }} ${{ secrets.s3_access_key }} ${{ secrets.s3_secret_key }} + + - name: Download file + uses: actions/download-artifact@v3 + with: + name: ${{ inputs.download_id }} + + - name: Upload file to s3 + run: mc cp ${{ inputs.filename }} s3/${{ inputs.bucket }}/ diff --git a/config/Config-images.in b/config/Config-images.in index 8c4616f37ca..d92c31ec5fc 100644 --- a/config/Config-images.in +++ b/config/Config-images.in @@ -204,11 +204,12 @@ menu "Target Images" config GRUB_EFI_IMAGES bool "Build GRUB EFI images (Linux x86 or x86_64 host only)" - depends on TARGET_x86 + depends on TARGET_x86 || TARGET_armvirt depends on TARGET_ROOTFS_EXT4FS || TARGET_ROOTFS_JFFS2 || TARGET_ROOTFS_SQUASHFS - select PACKAGE_grub2 - select PACKAGE_grub2-efi - select PACKAGE_grub2-bios-setup + select PACKAGE_grub2 if TARGET_x86 + select PACKAGE_grub2-efi if TARGET_x86 + select PACKAGE_grub2-bios-setup if TARGET_x86 + select PACKAGE_grub2-efi-arm if TARGET_armvirt select PACKAGE_kmod-fs-vfat default y @@ -266,7 +267,7 @@ menu "Target Images" config VMDK_IMAGES bool "Build VMware image files (VMDK)" - depends on TARGET_x86 + depends on TARGET_x86 || TARGET_armvirt depends on GRUB_IMAGES || GRUB_EFI_IMAGES select PACKAGE_kmod-e1000 @@ -291,6 +292,7 @@ menu "Target Images" depends on USES_BOOT_PART default 8 if TARGET_apm821xx_sata default 64 if TARGET_bcm27xx + default 128 if TARGET_armvirt default 16 config TARGET_ROOTFS_PARTSIZE diff --git a/config/Config-kernel.in b/config/Config-kernel.in index 6c5e6a967a5..7de0d17b5ea 100644 --- a/config/Config-kernel.in +++ b/config/Config-kernel.in @@ -397,6 +397,17 @@ config KERNEL_DEBUG_INFO_REDUCED DEBUG_INFO build and compile times are reduced too. Only works with newer gcc versions. +config KERNEL_FRAME_WARN + int + range 0 8192 + default 1280 if KERNEL_KASAN && !ARCH_64BIT + default 1024 if !ARCH_64BIT + default 2048 if ARCH_64BIT + help + Tell the compiler to warn at build time for stack frames larger than this. + Setting this too low will cause a lot of warnings. + Setting it to 0 disables the warning. + # KERNEL_DEBUG_LL symbols must have the default value set as otherwise # KConfig wont evaluate them unless KERNEL_EARLY_PRINTK is selected # which means that buildroot wont override the DEBUG_LL symbols in target diff --git a/include/host-build.mk b/include/host-build.mk index b7fb0a737f1..7485f91e422 100644 --- a/include/host-build.mk +++ b/include/host-build.mk @@ -131,6 +131,7 @@ define Host/Exports/Default $(1) : export STAGING_PREFIX=$$(HOST_BUILD_PREFIX) $(1) : export PKG_CONFIG_PATH=$$(STAGING_DIR_HOST)/lib/pkgconfig:$$(HOST_BUILD_PREFIX)/lib/pkgconfig $(1) : export PKG_CONFIG_LIBDIR=$$(HOST_BUILD_PREFIX)/lib/pkgconfig + $(1) : export GIT_CEILING_DIRECTORIES=$$(BUILD_DIR_HOST) $(if $(HOST_CONFIG_SITE),$(1) : export CONFIG_SITE:=$(HOST_CONFIG_SITE)) $(if $(IS_PACKAGE_BUILD),$(1) : export PATH=$$(TARGET_PATH_PKG)) endef diff --git a/include/kernel-5.15 b/include/kernel-5.15 index ddb9b7a875d..4c18bc35c22 100644 --- a/include/kernel-5.15 +++ b/include/kernel-5.15 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.15 = .112 -LINUX_KERNEL_HASH-5.15.112 = b0e8ebdba38e0c369f64ec4dc2a69ec59ffe87a64b82f940dbdf8e752b6ad651 +LINUX_VERSION-5.15 = .114 +LINUX_KERNEL_HASH-5.15.114 = e981ea5d219f77735bf5a3f7e84a8af578df8ac3e1c4ff1b0649e2b0795277d2 diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 1edb01df278..958ed9be0c0 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -13,6 +13,15 @@ endif include $(KERNEL_DETAILS_FILE) +ifdef KERNEL_TESTING_PATCHVER + KERNEL_TESTING_DETAILS_FILE=$(INCLUDE_DIR)/kernel-$(KERNEL_TESTING_PATCHVER) + ifeq ($(wildcard $(KERNEL_TESTING_DETAILS_FILE)),) + $(error Missing kernel version/hash file for $(KERNEL_TESTING_PATCHVER). Please create $(KERNEL_TESTING_DETAILS_FILE)) + endif + + include $(KERNEL_TESTING_DETAILS_FILE) +endif + remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/include/kernel.mk b/include/kernel.mk index c657bf5d82e..b1ae42534dc 100644 --- a/include/kernel.mk +++ b/include/kernel.mk @@ -90,6 +90,8 @@ else ifneq (,$(findstring $(ARCH) , mipsel mips64 mips64el )) LINUX_KARCH := mips else ifneq (,$(findstring $(ARCH) , powerpc64 )) LINUX_KARCH := powerpc +else ifneq (,$(findstring $(ARCH) , riscv64 )) + LINUX_KARCH := riscv else ifneq (,$(findstring $(ARCH) , sh2 sh3 sh4 )) LINUX_KARCH := sh else ifneq (,$(findstring $(ARCH) , i386 x86_64 )) diff --git a/include/package.mk b/include/package.mk index f03cb15f05d..c391d320aa2 100644 --- a/include/package.mk +++ b/include/package.mk @@ -194,6 +194,7 @@ define Build/Exports/Default $(1) : export CONFIG_SITE:=$$(CONFIG_SITE) $(1) : export PKG_CONFIG_PATH:=$$(PKG_CONFIG_PATH) $(1) : export PKG_CONFIG_LIBDIR:=$$(PKG_CONFIG_PATH) + $(1) : export GIT_CEILING_DIRECTORIES:=$$(BUILD_DIR) endef Build/Exports=$(Build/Exports/Default) diff --git a/include/prereq-build.mk b/include/prereq-build.mk index 9096e379bb0..5b63490ca3c 100644 --- a/include/prereq-build.mk +++ b/include/prereq-build.mk @@ -94,6 +94,10 @@ $(eval $(call TestHostCommand,perl-thread-queue, \ Please install the Perl Thread::Queue module, \ perl -MThread::Queue -e 1)) +$(eval $(call TestHostCommand,perl-ipc-cmd, \ + Please install the Perl IPC:Cmd module, \ + perl -MIPC::Cmd -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/include/site/riscv64 b/include/site/riscv64 new file mode 100644 index 00000000000..c5aa9c5543d --- /dev/null +++ b/include/site/riscv64 @@ -0,0 +1,30 @@ +#!/bin/sh +. $TOPDIR/include/site/linux +ac_cv_c_littleendian=${ac_cv_c_littleendian=yes} +ac_cv_c_bigendian=${ac_cv_c_bigendian=no} + +ac_cv_sizeof___int64=8 +ac_cv_sizeof_char=1 +ac_cv_sizeof_int=4 +ac_cv_sizeof_int16_t=2 +ac_cv_sizeof_int32_t=4 +ac_cv_sizeof_int64_t=8 +ac_cv_sizeof_long_int=8 +ac_cv_sizeof_long_long=8 +ac_cv_sizeof_long=8 +ac_cv_sizeof_off_t=8 +ac_cv_sizeof_short_int=2 +ac_cv_sizeof_short=2 +ac_cv_sizeof_size_t=8 +ac_cv_sizeof_ssize_t=8 +ac_cv_sizeof_u_int16_t=2 +ac_cv_sizeof_u_int32_t=4 +ac_cv_sizeof_u_int64_t=8 +ac_cv_sizeof_uint16_t=2 +ac_cv_sizeof_uint32_t=4 +ac_cv_sizeof_uint64_t=8 +ac_cv_sizeof_unsigned_int=4 +ac_cv_sizeof_unsigned_long=8 +ac_cv_sizeof_unsigned_long_long=8 +ac_cv_sizeof_unsigned_short=2 +ac_cv_sizeof_void_p=8 diff --git a/include/target.mk b/include/target.mk index 992f9553444..b5e3e7ff6fd 100644 --- a/include/target.mk +++ b/include/target.mk @@ -260,6 +260,10 @@ ifeq ($(DUMP),1) CPU_CFLAGS_arc700 = -mcpu=arc700 CPU_CFLAGS_archs = -mcpu=archs endif + ifeq ($(ARCH),riscv64) + CPU_TYPE ?= riscv64 + CPU_CFLAGS_riscv64:=-mabi=lp64d -march=rv64imafdc + endif ifneq ($(CPU_TYPE),) ifndef CPU_CFLAGS_$(CPU_TYPE) $(warning CPU_TYPE "$(CPU_TYPE)" doesn't correspond to a known type) diff --git a/package/boot/arm-trusted-firmware-mediatek/Makefile b/package/boot/arm-trusted-firmware-mediatek/Makefile index 2f879f9a57d..2098d0835f8 100644 --- a/package/boot/arm-trusted-firmware-mediatek/Makefile +++ b/package/boot/arm-trusted-firmware-mediatek/Makefile @@ -30,6 +30,7 @@ define Trusted-Firmware-A/Default DDR3_FLYBY:= DDR_TYPE:= NAND_TYPE:= + BOARD_QFN:= endef define Trusted-Firmware-A/mt7622-nor-1ddr @@ -92,6 +93,46 @@ define Trusted-Firmware-A/mt7622-sdmmc-2ddr DDR3_FLYBY:=1 endef +define Trusted-Firmware-A/mt7981-nor-ddr3 + NAME:=MediaTek MT7981 (SPI-NOR, DDR3) + BOOT_DEVICE:=nor + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 +endef + +define Trusted-Firmware-A/mt7981-emmc-ddr3 + NAME:=MediaTek MT7981 (eMMC, DDR3) + BOOT_DEVICE:=emmc + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 +endef + +define Trusted-Firmware-A/mt7981-sdmmc-ddr3 + NAME:=MediaTek MT7981 (SD card, DDR3) + BOOT_DEVICE:=sdmmc + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 +endef + +define Trusted-Firmware-A/mt7986-snand-ddr3 + NAME:=MediaTek MT7981 (SPI-NAND via SNFI, DDR3) + BOOT_DEVICE:=snand + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 +endef + +define Trusted-Firmware-A/mt7981-spim-nand-ddr3 + NAME:=MediaTek MT7981 (SPI-NAND via SPIM, DDR3) + BOOT_DEVICE:=spim-nand + BUILD_SUBTARGET:=filogic + PLAT:=mt7981 + DDR_TYPE:=ddr3 +endef + define Trusted-Firmware-A/mt7986-nor-ddr4 NAME:=MediaTek MT7986 (SPI-NOR, DDR4) BOOT_DEVICE:=nor @@ -182,6 +223,11 @@ TFA_TARGETS:= \ mt7622-emmc-2ddr \ mt7622-sdmmc-1ddr \ mt7622-sdmmc-2ddr \ + mt7981-emmc-ddr3 \ + mt7981-nor-ddr3 \ + mt7981-sdmmc-ddr3 \ + mt7986-snand-ddr3 \ + mt7981-spim-nand-ddr3 \ mt7986-emmc-ddr3 \ mt7986-nor-ddr3 \ mt7986-sdmmc-ddr3 \ @@ -197,6 +243,7 @@ TFA_MAKE_FLAGS += \ BOOT_DEVICE=$(BOOT_DEVICE) \ USE_MKIMAGE=1 MKIMAGE=$(STAGING_DIR_HOST)/bin/mkimage \ $(if $(findstring ddr4,$(DDR_TYPE)),DRAM_USE_DDR4=1) \ + $(if $(BOARD_QFN),BOARD_QFN=1,BOARD_BGA=1) \ $(if $(NAND_TYPE),NAND_TYPE=$(NAND_TYPE)) \ HAVE_DRAM_OBJ_FILE=yes \ $(if $(DDR3_FLYBY),DDR3_FLYBY=1) \ diff --git a/package/boot/grub2/Makefile b/package/boot/grub2/Makefile index 0613f46f995..50bf96360d6 100644 --- a/package/boot/grub2/Makefile +++ b/package/boot/grub2/Makefile @@ -33,14 +33,15 @@ include $(INCLUDE_DIR)/package.mk define Package/grub2/Default CATEGORY:=Boot Loaders SECTION:=boot - TITLE:=GRand Unified Bootloader ($(1)) + TITLE:=GRand Unified Bootloader ($(2)) URL:=http://www.gnu.org/software/grub/ - DEPENDS:=@TARGET_x86 - VARIANT:=$(1) + DEPENDS:=@TARGET_$(1) + VARIANT:=$(2) endef -Package/grub2=$(call Package/grub2/Default,pc) -Package/grub2-efi=$(call Package/grub2/Default,efi) +Package/grub2=$(call Package/grub2/Default,x86,pc) +Package/grub2-efi=$(call Package/grub2/Default,x86,efi) +Package/grub2-efi-arm=$(call Package/grub2/Default,armvirt,efi) define Package/grub2-editenv CATEGORY:=Utilities @@ -107,6 +108,10 @@ ifneq ($(BUILD_VARIANT),none) MAKE_PATH := grub-core endif +ifeq ($(CONFIG_arm),y) + TARGET_CFLAGS := $(filter-out -mfloat-abi=hard,$(TARGET_CFLAGS)) +endif + define Host/Configure $(SED) 's,(RANLIB),(TARGET_RANLIB),' $(HOST_BUILD_DIR)/grub-core/Makefile.in $(Host/Configure/Default) @@ -162,9 +167,31 @@ define Package/grub2-efi/install -O $(CONFIG_ARCH)-efi \ -c ./files/grub-early.cfg \ -o $(STAGING_DIR_IMAGE)/grub2/iso-boot$(if $(CONFIG_x86_64),x64,ia32).efi \ - at_keyboard boot chain configfile fat iso9660 linux ls part_msdos part_gpt reboot serial test efi_gop efi_uga + boot chain configfile fat iso9660 linux ls part_msdos part_gpt reboot serial test efi_gop efi_uga endef +define Package/grub2-efi-arm/install + $(INSTALL_DIR) $(STAGING_DIR_IMAGE)/grub2 + cp ./files/grub-early-gpt.cfg $(PKG_BUILD_DIR)/grub-early.cfg + $(STAGING_DIR_HOST)/bin/grub-mkimage \ + -d $(PKG_BUILD_DIR)/grub-core \ + -p /boot/grub \ + -O arm$(if $(CONFIG_aarch64),64,)-efi \ + -c $(PKG_BUILD_DIR)/grub-early.cfg \ + -o $(STAGING_DIR_IMAGE)/grub2/boot$(if $(CONFIG_aarch64),aa64,arm).efi \ + boot chain configfile fat linux ls part_gpt part_msdos reboot search \ + search_fs_uuid search_label serial efi_gop lsefi minicmd ext2 + $(STAGING_DIR_HOST)/bin/grub-mkimage \ + -d $(PKG_BUILD_DIR)/grub-core \ + -p /boot/grub \ + -O arm$(if $(CONFIG_aarch64),64,)-efi \ + -c ./files/grub-early.cfg \ + -o $(STAGING_DIR_IMAGE)/grub2/iso-bootaa$(if $(CONFIG_aarch64),aa64,arm).efi \ + boot chain configfile fat iso9660 linux ls lsefi minicmd part_msdos part_gpt \ + reboot serial test efi_gop +endef + + define Package/grub2-editenv/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_BUILD_DIR)/grub-editenv $(1)/usr/sbin/ @@ -178,5 +205,6 @@ endef $(eval $(call HostBuild)) $(eval $(call BuildPackage,grub2)) $(eval $(call BuildPackage,grub2-efi)) +$(eval $(call BuildPackage,grub2-efi-arm)) $(eval $(call BuildPackage,grub2-editenv)) $(eval $(call BuildPackage,grub2-bios-setup)) diff --git a/package/boot/grub2/files/grub-early-gpt.cfg b/package/boot/grub2/files/grub-early-gpt.cfg new file mode 100644 index 00000000000..c295d1f7d77 --- /dev/null +++ b/package/boot/grub2/files/grub-early-gpt.cfg @@ -0,0 +1,2 @@ +search --set=root --label kernel +configfile ($root)/efi/openwrt/grub.cfg diff --git a/package/boot/opensbi/Makefile b/package/boot/opensbi/Makefile new file mode 100644 index 00000000000..01348e50aad --- /dev/null +++ b/package/boot/opensbi/Makefile @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 OpenWrt.org +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=opensbi +PKG_RELEASE:=1.2 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL=https://github.com/riscv/opensbi +PKG_SOURCE_DATE:=2022-12-24 +PKG_SOURCE_VERSION:=6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf8 +PKG_MIRROR_HASH:=edcdd99da6c62975171981c0aa2b73a27091067da11ccd49816b5ad27d000858 + +PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION) + +PKG_TARGETS:=bin +PKG_FLAGS:=nonshared +PKG_LICENSE:=BSD-2-Clause +PKG_LICENSE_FILES:=COPYING.BSD +PKG_BUILD_PARALLEL:=1 + +PKG_MAINTAINER:=Zoltan HERPAI + +include $(INCLUDE_DIR)/package.mk + +define Package/opensbi + SECTION:=boot + CATEGORY:=Boot Loaders + DEPENDS:=@TARGET_sifiveu + URL:=https://github.com/riscv/opensbi/blob/master/README.md + VARIANT:=$(subst _,/,$(subst opensbi_,,$(1))) + TITLE:=OpenSBI generic + OPENSBI_IMAGE:= + PLAT:= +endef + +define Package/opensbi_generic + $(Package/opensbi) + TITLE:=OpenSBI generic + OPENSBI_IMAGE:=fw_dynamic.bin + PLAT:=generic +endef + +export GCC_HONOUR_COPTS=s + +MAKE_VARS = \ + CROSS_COMPILE="$(TARGET_CROSS)" + +define Build/Compile + $(eval $(Package/opensbi_$(BUILD_VARIANT))) \ + +$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR) \ + PLATFORM=$(PLAT) +endef + +define Build/InstallDev + $(INSTALL_DIR) $(STAGING_DIR_IMAGE) + $(CP) $(PKG_BUILD_DIR)/build/platform/$(PLAT)/firmware/fw_dynamic.bin $(STAGING_DIR_IMAGE)/fw_dynamic-${BUILD_VARIANT}.bin +endef + +$(eval $(call BuildPackage,opensbi_generic)) diff --git a/package/boot/uboot-envtools/files/ipq807x b/package/boot/uboot-envtools/files/ipq807x index ca331f10322..0185c2c8e6f 100644 --- a/package/boot/uboot-envtools/files/ipq807x +++ b/package/boot/uboot-envtools/files/ipq807x @@ -14,6 +14,7 @@ netgear,wax218) [ -n "$idx" ] && \ ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x40000" "0x20000" "2" ;; +compex,wpq873|\ edgecore,eap102|\ zyxel,nbg7815) idx="$(find_mtd_index 0:appsblenv)" diff --git a/package/boot/uboot-envtools/files/mediatek_filogic b/package/boot/uboot-envtools/files/mediatek_filogic index 50b9cd0e463..b638a65a4e8 100644 --- a/package/boot/uboot-envtools/files/mediatek_filogic +++ b/package/boot/uboot-envtools/files/mediatek_filogic @@ -41,6 +41,7 @@ xiaomi,redmi-router-ax6000-stock) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x10000" "0x20000" ubootenv_add_uci_sys_config "/dev/mtd2" "0x0" "0x10000" "0x20000" ;; +qihoo,360t7|\ tplink,tl-xdr4288|\ tplink,tl-xdr6086|\ tplink,tl-xdr6088|\ diff --git a/package/boot/uboot-layerscape/files/fsl_ls1012a-frwy-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1012a-frwy-sdboot-uEnv.txt index 592158955e8..18efdc1bc34 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1012a-frwy-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1012a-frwy-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0x8fffffff initrd_high=0xffffffffffffffff sd_boot=ext4load mmc 0:1 $loadaddr fitImage;bootm $loadaddr diff --git a/package/boot/uboot-layerscape/files/fsl_ls1012a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1012a-rdb-uEnv.txt index 8d57d51ece7..0898354e774 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1012a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1012a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 qspi_boot=sf probe 0:0;sf read $fdtaddr f00000 100000;sf read $loadaddr 1000000 1000000;bootm $loadaddr - $fdtaddr bootargs=root=/dev/mtdblock8 rootfstype=squashfs,jffs2 noinitrd earlycon=uart8250,mmio,0x21c0500 console=ttyS0,115200 mtdparts=1550000.spi:1m(bl2),4m(fip),1m(u-boot-env),4m(reserved-1),3m(pfe),2m(reserved-2),1m(dtb),16m(kernel),32m(rootfs),49m@0xf00000(firmware) diff --git a/package/boot/uboot-layerscape/files/fsl_ls1021a-iot-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1021a-iot-sdboot-uEnv.txt index ae0d6a6f96b..f8ec2c10fe8 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1021a-iot-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1021a-iot-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0x8fffffff initrd_high=0xffffffff sd_boot=ext4load mmc 0:1 ${loadaddr} fitImage;bootm ${loadaddr} diff --git a/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-sdboot-uEnv.txt index 55620d67257..d7f67e78ec8 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0x8fffffff initrd_high=0xffffffff sd_boot=ext4load mmc 0:1 $loadaddr fitImage;bootm $loadaddr diff --git a/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-uEnv.txt index 6d4647b1fd6..26fb3e9207b 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1021a-twr-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 nor_boot=cp.b 60f00000 $fdtaddr 100000;cp.b 61000000 $loadaddr 1000000;bootm $loadaddr - $fdtaddr bootargs=root=/dev/mtdblock6 rootfstype=squashfs,jffs2 noinitrd earlycon=uart8250,mmio,0x21c0500 console=ttyS0,115200 mtdparts=60000000.nor:1m(rcw),2m(u-boot),1m(u-boot-env),11m(reserved-1),1m(dtb),16m(kernel),32m(rootfs),49m@0xf00000(firmware) cma=64M@0x0-0xb0000000 diff --git a/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-sdboot-uEnv.txt index 3c39c2263de..591210eea73 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0x8fffffff initrd_high=0xffffffffffffffff hwconfig=fsl_ddr:bank_intlv=auto diff --git a/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-uEnv.txt index acb61ca00a3..c601cab92f8 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1043a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto nor_boot=cp.b 60f00000 $fdtaddr 100000;cp.b 61000000 $loadaddr 1000000;bootm $loadaddr - $fdtaddr diff --git a/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-sdboot-uEnv.txt index 303157cb800..e69332efacb 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto sd_boot=ext4load mmc 0:1 ${loadaddr} fitImage;bootm ${loadaddr} diff --git a/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-uEnv.txt index 5461bba9b66..ad5cc77841d 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1046a-frwy-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto qspi_boot=sf probe 0:0;sf read $fdtaddr f00000 100000;sf read $loadaddr 1000000 1000000;bootm $loadaddr - $fdtaddr diff --git a/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-sdboot-uEnv.txt index 3c39c2263de..591210eea73 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0x8fffffff initrd_high=0xffffffffffffffff hwconfig=fsl_ddr:bank_intlv=auto diff --git a/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-uEnv.txt index e52e48802bc..dab8faaf132 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1046a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto qspi_boot=sf probe 0:0;sf read $fdtaddr f00000 100000;sf read $loadaddr 1000000 1000000;bootm $loadaddr - $fdtaddr diff --git a/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-sdboot-uEnv.txt index f616cb13986..5b235378448 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0xa0000000 initrd_high=0xffffffffffffffff hwconfig=fsl_ddr:bank_intlv=auto diff --git a/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-uEnv.txt index 210c18721df..a15717bcb80 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls1088a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0xa0000000 initrd_high=0xffffffffffffffff hwconfig=fsl_ddr:bank_intlv=auto diff --git a/package/boot/uboot-layerscape/files/fsl_ls2088a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_ls2088a-rdb-uEnv.txt index 946029d1f16..646aed44278 100644 --- a/package/boot/uboot-layerscape/files/fsl_ls2088a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_ls2088a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 fdt_high=0xa0000000 initrd_high=0xffffffffffffffff hwconfig=fsl_ddr:bank_intlv=auto diff --git a/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-sdboot-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-sdboot-uEnv.txt index d1373199150..2d8043d7b1b 100644 --- a/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-sdboot-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-sdboot-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto mc_init=mmc read 80000000 5000 1800;mmc read 80300000 7000 800;fsl_mc start mc 80000000 80300000;mmc read 80400000 6800 800;fsl_mc apply dpl 80400000 diff --git a/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-uEnv.txt b/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-uEnv.txt index 41fde35c88c..16f905afc68 100644 --- a/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-uEnv.txt +++ b/package/boot/uboot-layerscape/files/fsl_lx2160a-rdb-uEnv.txt @@ -1,5 +1,5 @@ fdtaddr=0x8f000000 -loadaddr=0x81000000 +loadaddr=0x88000000 bootm_size=0x10000000 hwconfig=fsl_ddr:bank_intlv=auto mc_init=sf probe 0:0;sf read 80000000 a00000 300000;sf read 80300000 e00000 100000;fsl_mc start mc 80000000 80300000;sf read 80400000 d00000 100000;fsl_mc apply dpl 80400000 diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile index 12c305617b3..8ea05b9159e 100644 --- a/package/boot/uboot-mediatek/Makefile +++ b/package/boot/uboot-mediatek/Makefile @@ -176,6 +176,18 @@ define U-Boot/mt7629_rfb UBOOT_CONFIG:=mt7629_rfb endef +define U-Boot/mt7981_qihoo_360t7 + NAME:=Qihoo 360T7 + BUILD_SUBTARGET:=filogic + BUILD_DEVICES:=qihoo_360t7 + UBOOT_CONFIG:=mt7981_qihoo-360t7 + UBOOT_IMAGE:=u-boot.fip + BL2_BOOTDEV:=spim-nand + BL2_SOC:=mt7981 + BL2_DDRTYPE:=ddr3 + DEPENDS:=+trusted-firmware-a-mt7981-spim-nand-ddr3 +endef + define U-Boot/mt7986_rfb NAME:=MT7986 Reference Board BUILD_SUBTARGET:=filogic @@ -301,6 +313,7 @@ UBOOT_TARGETS := \ mt7628_rfb \ ravpower_rp-wd009 \ mt7629_rfb \ + mt7981_qihoo_360t7 \ mt7986_bananapi_bpi-r3-emmc \ mt7986_bananapi_bpi-r3-sdmmc \ mt7986_bananapi_bpi-r3-snand \ diff --git a/package/boot/uboot-mediatek/patches/052-mt7981-enable-pstore.patch b/package/boot/uboot-mediatek/patches/052-mt7981-enable-pstore.patch new file mode 100644 index 00000000000..9bfea8f7379 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/052-mt7981-enable-pstore.patch @@ -0,0 +1,38 @@ +--- a/arch/arm/dts/mt7981.dtsi ++++ b/arch/arm/dts/mt7981.dtsi +@@ -32,6 +32,35 @@ + }; + }; + ++ psci { ++ compatible = "arm,psci-0.2"; ++ method = "smc"; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* 64 KiB reserved for ramoops/pstore */ ++ ramoops@42ff0000 { ++ 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; ++ }; ++ ++ wmcpu_emi: wmcpu-reserved@4fc00000 { ++ no-map; ++ reg = <0 0x4fc00000 0 0x00100000>; ++ }; ++ }; ++ + gpt_clk: gpt_dummy20m { + compatible = "fixed-clock"; + clock-frequency = <13000000>; diff --git a/package/boot/uboot-mediatek/patches/304-mt7981-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/304-mt7981-generic-reset-button-ignore-env.patch new file mode 100644 index 00000000000..fc443349829 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/304-mt7981-generic-reset-button-ignore-env.patch @@ -0,0 +1,43 @@ +--- a/board/mediatek/mt7981/mt7981_rfb.c ++++ b/board/mediatek/mt7981/mt7981_rfb.c +@@ -6,9 +6,16 @@ + + #include + #include ++#include ++#include + #include + #include + #include ++#include ++ ++#ifndef CONFIG_RESET_BUTTON_LABEL ++#define CONFIG_RESET_BUTTON_LABEL "reset" ++#endif + + #include + #include +@@ -24,7 +31,22 @@ int board_init(void) + + int board_late_init(void) + { +- gd->env_valid = 1; //to load environment variable from persistent store ++ struct udevice *dev; ++ ++ gd->env_valid = ENV_VALID; ++ if (!button_get_by_label(CONFIG_RESET_BUTTON_LABEL, &dev)) { ++ puts("reset button found\n"); ++#ifdef CONFIG_RESET_BUTTON_SETTLE_DELAY ++ if (CONFIG_RESET_BUTTON_SETTLE_DELAY > 0) { ++ button_get_state(dev); ++ mdelay(CONFIG_RESET_BUTTON_SETTLE_DELAY); ++ } ++#endif ++ if (button_get_state(dev) == BUTTON_ON) { ++ puts("button pushed, resetting environment\n"); ++ gd->env_valid = ENV_INVALID; ++ } ++ } + env_relocate(); + return 0; + } diff --git a/package/boot/uboot-mediatek/patches/433-add-qihoo_360t7.patch b/package/boot/uboot-mediatek/patches/433-add-qihoo_360t7.patch new file mode 100644 index 00000000000..2be7e18c6ec --- /dev/null +++ b/package/boot/uboot-mediatek/patches/433-add-qihoo_360t7.patch @@ -0,0 +1,420 @@ +--- /dev/null ++++ b/configs/mt7981_qihoo-360t7_defconfig +@@ -0,0 +1,175 @@ ++CONFIG_ARM=y ++CONFIG_POSITION_INDEPENDENT=y ++CONFIG_ARCH_MEDIATEK=y ++CONFIG_TARGET_MT7981=y ++CONFIG_TEXT_BASE=0x41e00000 ++CONFIG_SYS_MALLOC_F_LEN=0x4000 ++CONFIG_SYS_HAS_NONCACHED_MEMORY=y ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEFAULT_DEVICE_TREE="mt7981_qihoo-360t7" ++CONFIG_DEFAULT_ENV_FILE="qihoo-360t7_env" ++CONFIG_DEFAULT_FDT_FILE="mediatek/mt7981_qihoo-360t7.dtb" ++CONFIG_OF_LIBFDT_OVERLAY=y ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=40000000 ++CONFIG_DEBUG_UART=y ++CONFIG_SYS_LOAD_ADDR=0x46000000 ++CONFIG_SMBIOS_PRODUCT_NAME="" ++CONFIG_AUTOBOOT_KEYED=y ++CONFIG_BOOTDELAY=30 ++CONFIG_AUTOBOOT_MENU_SHOW=y ++CONFIG_CFB_CONSOLE_ANSI=y ++CONFIG_BOARD_LATE_INIT=y ++CONFIG_BUTTON=y ++CONFIG_BUTTON_GPIO=y ++CONFIG_GPIO_HOG=y ++CONFIG_CMD_ENV_FLAGS=y ++CONFIG_FIT=y ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_LED=y ++CONFIG_LED_BLINK=y ++CONFIG_LED_GPIO=y ++CONFIG_LOGLEVEL=7 ++CONFIG_LOG=y ++CONFIG_SYS_PROMPT="MT7981> " ++CONFIG_CMD_BOOTMENU=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_BUTTON=y ++CONFIG_CMD_CACHE=y ++CONFIG_CMD_CDP=y ++CONFIG_CMD_CPU=y ++CONFIG_CMD_DHCP=y ++CONFIG_CMD_DM=y ++CONFIG_CMD_DNS=y ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ENV_READMEM=y ++CONFIG_CMD_ERASEENV=y ++CONFIG_CMD_EXT4=y ++CONFIG_CMD_FAT=y ++CONFIG_CMD_FDT=y ++CONFIG_CMD_FS_GENERIC=y ++CONFIG_CMD_FS_UUID=y ++CONFIG_CMD_GPIO=y ++CONFIG_CMD_GPT=y ++CONFIG_CMD_HASH=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_LED=y ++CONFIG_CMD_LICENSE=y ++CONFIG_CMD_LINK_LOCAL=y ++# CONFIG_CMD_MBR is not set ++CONFIG_CMD_PCI=y ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 ++CONFIG_CMD_SF_TEST=y ++CONFIG_CMD_PING=y ++CONFIG_CMD_PXE=y ++CONFIG_CMD_PWM=y ++CONFIG_CMD_SMC=y ++CONFIG_CMD_TFTPBOOT=y ++CONFIG_CMD_TFTPSRV=y ++CONFIG_CMD_UBI=y ++CONFIG_CMD_UBI_RENAME=y ++CONFIG_CMD_UBIFS=y ++CONFIG_CMD_ASKENV=y ++CONFIG_CMD_PART=y ++CONFIG_CMD_RARP=y ++CONFIG_CMD_SETEXPR=y ++CONFIG_CMD_SLEEP=y ++CONFIG_CMD_SNTP=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_STRINGS=y ++CONFIG_CMD_UUID=y ++CONFIG_DISPLAY_CPUINFO=y ++CONFIG_DM_MTD=y ++CONFIG_DM_REGULATOR=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_DM_REGULATOR_GPIO=y ++CONFIG_DM_PWM=y ++CONFIG_PWM_MTK=y ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y ++CONFIG_SYS_RELOC_GD_ENV_ADDR=y ++CONFIG_VERSION_VARIABLE=y ++CONFIG_PARTITION_UUIDS=y ++CONFIG_NETCONSOLE=y ++CONFIG_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_CLK=y ++CONFIG_DM_GPIO=y ++CONFIG_DM_SCSI=y ++CONFIG_AHCI=y ++CONFIG_AHCI_PCI=y ++CONFIG_SCSI_AHCI=y ++CONFIG_SCSI=y ++CONFIG_CMD_SCSI=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y ++CONFIG_PHY_FIXED=y ++CONFIG_MTK_AHCI=y ++CONFIG_DM_ETH=y ++CONFIG_MEDIATEK_ETH=y ++CONFIG_PCI=y ++# CONFIG_MMC is not set ++# CONFIG_DM_MMC is not set ++CONFIG_MTD=y ++CONFIG_MTD_UBI_FASTMAP=y ++CONFIG_DM_PCI=y ++CONFIG_PCIE_MEDIATEK=y ++CONFIG_PINCTRL=y ++CONFIG_PINCONF=y ++CONFIG_PINCTRL_MT7622=y ++CONFIG_POWER_DOMAIN=y ++CONFIG_PRE_CONSOLE_BUFFER=y ++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00 ++CONFIG_MTK_POWER_DOMAIN=y ++CONFIG_RAM=y ++CONFIG_DM_SERIAL=y ++CONFIG_MTK_SERIAL=y ++CONFIG_SPI=y ++CONFIG_DM_SPI=y ++CONFIG_MTK_SPI_NAND=y ++CONFIG_MTK_SPI_NAND_MTD=y ++CONFIG_SYSRESET_WATCHDOG=y ++CONFIG_WDT_MTK=y ++CONFIG_LZO=y ++CONFIG_ZSTD=y ++CONFIG_HEXDUMP=y ++CONFIG_RANDOM_UUID=y ++CONFIG_REGEX=y ++CONFIG_OF_EMBED=y ++CONFIG_ENV_OVERWRITE=y ++CONFIG_ENV_IS_IN_UBI=y ++CONFIG_ENV_UBI_PART="ubi" ++CONFIG_ENV_SIZE=0x1f000 ++CONFIG_ENV_SIZE_REDUND=0x1f000 ++CONFIG_ENV_UBI_VOLUME="ubootenv" ++CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2" ++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_CLK=y ++CONFIG_PHY_FIXED=y ++CONFIG_DM_ETH=y ++CONFIG_MEDIATEK_ETH=y ++CONFIG_PINCTRL=y ++CONFIG_PINCONF=y ++CONFIG_PINCTRL_MT7981=y ++CONFIG_POWER_DOMAIN=y ++CONFIG_MTK_POWER_DOMAIN=y ++CONFIG_DM_REGULATOR=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_DM_SERIAL=y ++CONFIG_MTK_SERIAL=y ++CONFIG_HEXDUMP=y ++CONFIG_USE_DEFAULT_ENV_FILE=y ++CONFIG_MTD_SPI_NAND=y ++CONFIG_MTK_SPIM=y ++CONFIG_CMD_MTD=y ++CONFIG_CMD_NAND=y ++CONFIG_CMD_NAND_TRIMFFS=y ++CONFIG_LMB_MAX_REGIONS=64 ++CONFIG_USE_IPADDR=y ++CONFIG_IPADDR="192.168.1.1" ++CONFIG_USE_SERVERIP=y ++CONFIG_SERVERIP="192.168.1.254" +--- /dev/null ++++ b/arch/arm/dts/mt7981_qihoo-360t7.dts +@@ -0,0 +1,180 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2022 MediaTek Inc. ++ * Author: Sam Shih ++ */ ++ ++/dts-v1/; ++#include "mt7981.dtsi" ++#include ++#include ++ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ model = "Qihoo 360T7"; ++ compatible = "mediatek,mt7981", "mediatek,mt7981-rfb"; ++ ++ chosen { ++ stdout-path = &uart0; ++ tick-timer = &timer0; ++ }; ++ ++ keys { ++ compatible = "gpio-keys"; ++ ++ factory { ++ label = "reset"; ++ linux,code = ; ++ gpios = <&gpio 1 GPIO_ACTIVE_LOW>; ++ }; ++ ++ wps { ++ label = "wps"; ++ linux,code = ; ++ gpios = <&gpio 0 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ status_red { ++ label = "red:status"; ++ gpios = <&gpio 3 GPIO_ACTIVE_LOW>; ++ }; ++ ++ status_green { ++ label = "green:status"; ++ gpios = <&gpio 7 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; ++ ++&uart0 { ++ mediatek,force-highspeed; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "disabled"; ++}; ++ ++ð { ++ status = "okay"; ++ mediatek,gmac-id = <0>; ++ phy-mode = "sgmii"; ++ mediatek,switch = "mt7531"; ++ reset-gpios = <&gpio 39 GPIO_ACTIVE_HIGH>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++}; ++ ++&pinctrl { ++ spi_flash_pins: spi0-pins-func-1 { ++ mux { ++ function = "flash"; ++ groups = "spi0", "spi0_wp_hold"; ++ }; ++ ++ conf-pu { ++ pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; ++ drive-strength = ; ++ bias-pull-up = ; ++ }; ++ ++ conf-pd { ++ pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; ++ drive-strength = ; ++ bias-pull-down = ; ++ }; ++ }; ++ ++ spic_pins: spi1-pins-func-1 { ++ mux { ++ function = "spi"; ++ groups = "spi1_1"; ++ }; ++ }; ++ ++ uart1_pins: spi1-pins-func-3 { ++ mux { ++ function = "uart"; ++ groups = "uart1_2"; ++ }; ++ }; ++ ++ pwm_pins: pwm0-pins-func-1 { ++ mux { ++ function = "pwm"; ++ groups = "pwm0_1", "pwm1_0"; ++ }; ++ }; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++}; ++ ++&spi0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi_flash_pins>; ++ status = "okay"; ++ must_tx; ++ enhance_timing; ++ dma_ext; ++ ipm_design; ++ support_quad; ++ tick_dly = <2>; ++ sample_sel = <0>; ++ ++ spi_nand@0 { ++ compatible = "spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <52000000>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "bl2"; ++ reg = <0x0 0x100000>; ++ }; ++ ++ partition@100000 { ++ label = "orig-env"; ++ reg = <0x100000 0x80000>; ++ }; ++ ++ partition@160000 { ++ label = "factory"; ++ reg = <0x180000 0x200000>; ++ }; ++ ++ partition@380000 { ++ label = "fip"; ++ reg = <0x380000 0x200000>; ++ }; ++ ++ partition@580000 { ++ label = "ubi"; ++ reg = <0x580000 0x6c00000>; ++ }; ++ }; ++ }; ++}; ++ ++&watchdog { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/qihoo-360t7_env +@@ -0,0 +1,56 @@ ++ipaddr=192.168.1.1 ++serverip=192.168.1.254 ++loadaddr=0x46000000 ++console=earlycon=uart8250,mmio32,0x11002000 console=ttyS0 ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_ubi ; fi ++bootconf=config-1 ++bootdelay=0 ++bootfile=openwrt-mediatek-filogic-qihoo_360t7-initramfs-recovery.itb ++bootfile_bl2=openwrt-mediatek-filogic-qihoo_360t7-preloader.bin ++bootfile_fip=openwrt-mediatek-filogic-qihoo_360t7-bl31-uboot.fip ++bootfile_upg=openwrt-mediatek-filogic-qihoo_360t7-squashfs-sysupgrade.itb ++bootled_pwr=green:status ++bootled_rec=red:status ++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60 ++bootmenu_default=0 ++bootmenu_delay=0 ++bootmenu_title= ( ( ( OpenWrt ) ) ) ++bootmenu_0=Initialize environment.=run _firstboot ++bootmenu_0d=Run default boot command.=run boot_default ++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return ++bootmenu_2=Boot production system from NAND.=run boot_production ; run bootmenu_confirm_return ++bootmenu_3=Boot recovery system from NAND.=run boot_recovery ; run bootmenu_confirm_return ++bootmenu_4=Load production system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_production ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return ++bootmenu_5=Load recovery system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_recovery ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return ++bootmenu_6=Load BL31+U-Boot FIP via TFTP then write to NAND.=run boot_tftp_write_fip ; run bootmenu_confirm_return ++bootmenu_7=Load BL2 preloader via TFTP then write to NAND.=run boot_tftp_write_bl2 ; run bootmenu_confirm_return ++bootmenu_8=Reboot.=reset ++bootmenu_9=Reset all settings to factory defaults.=run reset_factory ; reset ++boot_first=if button reset ; then led $bootled_rec on ; run boot_tftp_recovery ; setenv flag_recover 1 ; run boot_default ; fi ; bootmenu ++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_forever ++boot_production=led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off ++boot_recovery=led $bootled_rec on ; run ubi_read_recovery && bootm $loadaddr#$bootconf ; led $bootled_rec off ++boot_ubi=run boot_production ; run boot_recovery ; run boot_tftp_forever ++boot_tftp_forever=led $bootled_rec on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done ++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && env exists replacevol && iminfo $loadaddr && run ubi_write_production ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi ++boot_tftp_recovery=tftpboot $loadaddr $bootfile && env exists replacevol && iminfo $loadaddr && run ubi_write_recovery ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi ++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr#$bootconf ++boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run mtd_write_fip && run reset_factory ++boot_tftp_write_bl2=tftpboot $loadaddr $bootfile_bl2 && run mtd_write_bl2 ++part_default=production ++part_recovery=recovery ++reset_factory=ubi part ubi ; mw $loadaddr 0x0 0x800 ; ubi write $loadaddr ubootenv 0x800 ; ubi write $loadaddr ubootenv2 0x800 ++mtd_write_fip=mtd erase fip && mtd write fip $loadaddr ++mtd_write_bl2=mtd erase bl2 && mtd write bl2 $loadaddr ++ubi_create_env=ubi check ubootenv || ubi create ubootenv 0x100000 dynamic 0 || run ubi_format ; ubi check ubootenv2 || ubi create ubootenv2 0x100000 dynamic 1 || run ubi_format ++ubi_format=ubi detach ; mtd erase ubi && ubi part ubi ; reset ++ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi ++ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs ++ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery ++ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data ++ubi_write_production=ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic 2 && ubi write $loadaddr fit $filesize ++ubi_write_recovery=ubi check recovery && ubi remove recovery ; run ubi_remove_rootfs ; ubi create recovery $filesize dynamic 3 && ubi write $loadaddr recovery $filesize ++_init_env=setenv _init_env ; run ubi_create_env ; saveenv ; saveenv ++_firstboot=setenv _firstboot ; run _switch_to_menu ; 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 ++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver" diff --git a/package/boot/uboot-rockchip/Makefile b/package/boot/uboot-rockchip/Makefile index bdd1f5d5d88..59b8be84cfb 100644 --- a/package/boot/uboot-rockchip/Makefile +++ b/package/boot/uboot-rockchip/Makefile @@ -45,6 +45,20 @@ define U-Boot/nanopi-r2s-rk3328 friendlyarm_nanopi-r2s endef +define U-Boot/orangepi-r1-plus-rk3328 + $(U-Boot/rk3328/Default) + NAME:=Orange Pi R1 Plus + BUILD_DEVICES:= \ + xunlong_orangepi-r1-plus +endef + +define U-Boot/orangepi-r1-plus-lts-rk3328 + $(U-Boot/rk3328/Default) + NAME:=Orange Pi R1 Plus LTS + BUILD_DEVICES:= \ + xunlong_orangepi-r1-plus-lts +endef + define U-Boot/roc-cc-rk3328 $(U-Boot/rk3328/Default) NAME:=ROC-RK3328-CC @@ -87,6 +101,8 @@ UBOOT_TARGETS := \ rockpro64-rk3399 \ nanopi-r2c-rk3328 \ nanopi-r2s-rk3328 \ + orangepi-r1-plus-rk3328 \ + orangepi-r1-plus-lts-rk3328 \ roc-cc-rk3328 UBOOT_CONFIGURE_VARS += USE_PRIVATE_LIBGCC=yes diff --git a/package/boot/uboot-rockchip/patches/103-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus.patch b/package/boot/uboot-rockchip/patches/103-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus.patch new file mode 100644 index 00000000000..ff17559c2fe --- /dev/null +++ b/package/boot/uboot-rockchip/patches/103-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus.patch @@ -0,0 +1,564 @@ +From ff312af37d5f263f181468639aab83f645d331f1 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Sat, 20 May 2023 18:50:38 +0800 +Subject: [PATCH] rockchip: rk3328: Add support for Orange Pi R1 Plus + +Orange Pi R1 Plus is a Rockchip RK3328 based SBC by Xunlong. + +This device is similar to the NanoPi R2S, and has a 16MB +SPI NOR (mx25l12805d). The reset button is changed to +directly reset the power supply, another detail is that +both network ports have independent MAC addresses. + +The device tree and description are taken from kernel v6.3-rc1. + +Reviewed-by: Kever Yang +Signed-off-by: Tianling Shen +--- + arch/arm/dts/Makefile | 1 + + .../dts/rk3328-orangepi-r1-plus-u-boot.dtsi | 46 +++ + arch/arm/dts/rk3328-orangepi-r1-plus.dts | 373 ++++++++++++++++++ + board/rockchip/evb_rk3328/MAINTAINERS | 6 + + configs/orangepi-r1-plus-rk3328_defconfig | 114 ++++++ + 5 files changed, 540 insertions(+) + create mode 100644 arch/arm/dts/rk3328-orangepi-r1-plus-u-boot.dtsi + create mode 100644 arch/arm/dts/rk3328-orangepi-r1-plus.dts + create mode 100644 configs/orangepi-r1-plus-rk3328_defconfig + +--- a/arch/arm/dts/Makefile ++++ b/arch/arm/dts/Makefile +@@ -110,6 +110,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3328) += \ + rk3328-evb.dtb \ + rk3328-nanopi-r2c.dtb \ + rk3328-nanopi-r2s.dtb \ ++ rk3328-orangepi-r1-plus.dtb \ + rk3328-roc-cc.dtb \ + rk3328-rock64.dtb \ + rk3328-rock-pi-e.dtb +--- /dev/null ++++ b/arch/arm/dts/rk3328-orangepi-r1-plus-u-boot.dtsi +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * (C) Copyright 2018-2019 Rockchip Electronics Co., Ltd ++ * (C) Copyright 2020 David Bauer ++ */ ++ ++#include "rk3328-u-boot.dtsi" ++#include "rk3328-sdram-ddr4-666.dtsi" ++/ { ++ chosen { ++ u-boot,spl-boot-order = "same-as-spl", &sdmmc, &emmc; ++ }; ++}; ++ ++&gpio0 { ++ u-boot,dm-spl; ++}; ++ ++&pinctrl { ++ u-boot,dm-spl; ++}; ++ ++&sdmmc0m1_gpio { ++ u-boot,dm-spl; ++}; ++ ++&pcfg_pull_up_4ma { ++ u-boot,dm-spl; ++}; ++ ++/* Need this and all the pinctrl/gpio stuff above to set pinmux */ ++&vcc_sd { ++ u-boot,dm-spl; ++}; ++ ++&gmac2io { ++ snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ snps,reset-active-low; ++ snps,reset-delays-us = <0 10000 50000>; ++}; ++ ++&spi0 { ++ spi_flash: spiflash@0 { ++ u-boot,dm-pre-reloc; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/dts/rk3328-orangepi-r1-plus.dts +@@ -0,0 +1,359 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Based on rk3328-nanopi-r2s.dts, which is: ++ * Copyright (c) 2020 David Bauer ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include "rk3328.dtsi" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus"; ++ compatible = "xunlong,orangepi-r1-plus", "rockchip,rk3328"; ++ ++ aliases { ++ mmc0 = &sdmmc; ++ }; ++ ++ chosen { ++ stdout-path = "serial2:1500000n8"; ++ }; ++ ++ gmac_clk: gmac-clock { ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_clkin"; ++ #clock-cells = <0>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-0 = <&lan_led_pin>, <&sys_led_pin>, <&wan_led_pin>; ++ pinctrl-names = "default"; ++ ++ led-0 { ++ function = LED_FUNCTION_LAN; ++ color = ; ++ gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-1 { ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led-2 { ++ function = LED_FUNCTION_WAN; ++ color = ; ++ gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ ++ vcc_sd: sdmmc-regulator { ++ compatible = "regulator-fixed"; ++ gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&sdmmc0m1_gpio>; ++ pinctrl-names = "default"; ++ regulator-name = "vcc_sd"; ++ regulator-boot-on; ++ vin-supply = <&vcc_io>; ++ }; ++ ++ vcc_sys: vcc-sys-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ vdd_5v_lan: vdd-5v-lan-regulator { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&lan_vdd_pin>; ++ pinctrl-names = "default"; ++ regulator-name = "vdd_5v_lan"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc_sys>; ++ }; ++}; ++ ++&cpu0 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu1 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu2 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu3 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&display_subsystem { ++ status = "disabled"; ++}; ++ ++&gmac2io { ++ assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; ++ assigned-clock-parents = <&gmac_clk>, <&gmac_clk>; ++ clock_in_out = "input"; ++ phy-handle = <&rtl8211e>; ++ phy-mode = "rgmii"; ++ phy-supply = <&vcc_io>; ++ pinctrl-0 = <&rgmiim1_pins>; ++ pinctrl-names = "default"; ++ snps,aal; ++ rx_delay = <0x18>; ++ tx_delay = <0x24>; ++ status = "okay"; ++ ++ mdio { ++ compatible = "snps,dwmac-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rtl8211e: ethernet-phy@1 { ++ reg = <1>; ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; ++ ++&i2c1 { ++ status = "okay"; ++ ++ rk805: pmic@18 { ++ compatible = "rockchip,rk805"; ++ reg = <0x18>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <24 IRQ_TYPE_LEVEL_LOW>; ++ #clock-cells = <1>; ++ clock-output-names = "xin32k", "rk805-clkout2"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ pinctrl-0 = <&pmic_int_l>; ++ pinctrl-names = "default"; ++ rockchip,system-power-controller; ++ wakeup-source; ++ ++ vcc1-supply = <&vcc_sys>; ++ vcc2-supply = <&vcc_sys>; ++ vcc3-supply = <&vcc_sys>; ++ vcc4-supply = <&vcc_sys>; ++ vcc5-supply = <&vcc_io>; ++ vcc6-supply = <&vcc_sys>; ++ ++ regulators { ++ vdd_log: DCDC_REG1 { ++ regulator-name = "vdd_log"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ ++ vdd_arm: DCDC_REG2 { ++ regulator-name = "vdd_arm"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <950000>; ++ }; ++ }; ++ ++ vcc_ddr: DCDC_REG3 { ++ regulator-name = "vcc_ddr"; ++ regulator-always-on; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ vcc_io: DCDC_REG4 { ++ regulator-name = "vcc_io"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3300000>; ++ }; ++ }; ++ ++ vcc_18: LDO_REG1 { ++ regulator-name = "vcc_18"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vcc18_emmc: LDO_REG2 { ++ regulator-name = "vcc18_emmc"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vdd_10: LDO_REG3 { ++ regulator-name = "vdd_10"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&io_domains { ++ pmuio-supply = <&vcc_io>; ++ vccio1-supply = <&vcc_io>; ++ vccio2-supply = <&vcc18_emmc>; ++ vccio3-supply = <&vcc_io>; ++ vccio4-supply = <&vcc_io>; ++ vccio5-supply = <&vcc_io>; ++ vccio6-supply = <&vcc_io>; ++ status = "okay"; ++}; ++ ++&pinctrl { ++ gmac2io { ++ eth_phy_reset_pin: eth-phy-reset-pin { ++ rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ leds { ++ lan_led_pin: lan-led-pin { ++ rockchip,pins = <2 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ sys_led_pin: sys-led-pin { ++ rockchip,pins = <3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ wan_led_pin: wan-led-pin { ++ rockchip,pins = <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ lan { ++ lan_vdd_pin: lan-vdd-pin { ++ rockchip,pins = <2 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ pmic { ++ pmic_int_l: pmic-int-l { ++ rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++}; ++ ++&pwm2 { ++ status = "okay"; ++}; ++ ++&sdmmc { ++ bus-width = <4>; ++ cap-sd-highspeed; ++ disable-wp; ++ pinctrl-0 = <&sdmmc0_clk>, <&sdmmc0_cmd>, <&sdmmc0_dectn>, <&sdmmc0_bus4>; ++ pinctrl-names = "default"; ++ vmmc-supply = <&vcc_sd>; ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++}; ++ ++&tsadc { ++ rockchip,hw-tshut-mode = <0>; ++ rockchip,hw-tshut-polarity = <0>; ++ status = "okay"; ++}; ++ ++&u2phy { ++ status = "okay"; ++}; ++ ++&u2phy_host { ++ status = "okay"; ++}; ++ ++&u2phy_otg { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&usb20_otg { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; +--- a/board/rockchip/evb_rk3328/MAINTAINERS ++++ b/board/rockchip/evb_rk3328/MAINTAINERS +@@ -18,6 +18,12 @@ F: configs/nanopi-r2s-rk3328_defcon + F: arch/arm/dts/rk3328-nanopi-r2s-u-boot.dtsi + F: arch/arm/dts/rk3328-nanopi-r2s.dts + ++ORANGEPI-R1-PLUS-RK3328 ++M: Tianling Shen ++S: Maintained ++F: configs/orangepi-r1-plus-rk3328_defconfig ++F: arch/arm/dts/rk3328-orangepi-r1-plus-u-boot.dtsi ++ + ROC-RK3328-CC + M: Loic Devulder + M: Chen-Yu Tsai +--- /dev/null ++++ b/configs/orangepi-r1-plus-rk3328_defconfig +@@ -0,0 +1,98 @@ ++CONFIG_ARM=y ++CONFIG_ARCH_ROCKCHIP=y ++CONFIG_SYS_TEXT_BASE=0x00200000 ++CONFIG_SPL_GPIO_SUPPORT=y ++CONFIG_ENV_OFFSET=0x3F8000 ++CONFIG_ROCKCHIP_RK3328=y ++CONFIG_TPL_ROCKCHIP_COMMON_BOARD=y ++CONFIG_TPL_LIBCOMMON_SUPPORT=y ++CONFIG_TPL_LIBGENERIC_SUPPORT=y ++CONFIG_SPL_DRIVERS_MISC_SUPPORT=y ++CONFIG_SPL_STACK_R_ADDR=0x600000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0xFF130000 ++CONFIG_DEBUG_UART_CLOCK=24000000 ++CONFIG_SYSINFO=y ++CONFIG_DEBUG_UART=y ++CONFIG_TPL_SYS_MALLOC_F_LEN=0x800 ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_VERBOSE=y ++CONFIG_SPL_LOAD_FIT=y ++CONFIG_DEFAULT_FDT_FILE="rockchip/rk3328-orangepi-r1-plus.dtb" ++CONFIG_MISC_INIT_R=y ++# CONFIG_DISPLAY_CPUINFO is not set ++CONFIG_DISPLAY_BOARDINFO_LATE=y ++# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set ++CONFIG_TPL_SYS_MALLOC_SIMPLE=y ++CONFIG_SPL_STACK_R=y ++CONFIG_SPL_I2C_SUPPORT=y ++CONFIG_SPL_POWER_SUPPORT=y ++CONFIG_SPL_ATF=y ++CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y ++CONFIG_CMD_BOOTZ=y ++CONFIG_CMD_GPT=y ++CONFIG_CMD_MMC=y ++CONFIG_CMD_USB=y ++# CONFIG_CMD_SETEXPR is not set ++CONFIG_CMD_TIME=y ++CONFIG_SPL_OF_CONTROL=y ++CONFIG_TPL_OF_CONTROL=y ++CONFIG_DEFAULT_DEVICE_TREE="rk3328-orangepi-r1-plus" ++CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" ++CONFIG_TPL_OF_PLATDATA=y ++CONFIG_ENV_IS_IN_MMC=y ++CONFIG_SYS_RELOC_GD_ENV_ADDR=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_TPL_DM=y ++CONFIG_REGMAP=y ++CONFIG_SPL_REGMAP=y ++CONFIG_TPL_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_SPL_SYSCON=y ++CONFIG_TPL_SYSCON=y ++CONFIG_CLK=y ++CONFIG_SPL_CLK=y ++CONFIG_FASTBOOT_BUF_ADDR=0x800800 ++CONFIG_FASTBOOT_CMD_OEM_FORMAT=y ++CONFIG_ROCKCHIP_GPIO=y ++CONFIG_SYS_I2C_ROCKCHIP=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_ROCKCHIP=y ++CONFIG_SF_DEFAULT_SPEED=20000000 ++CONFIG_DM_ETH=y ++CONFIG_ETH_DESIGNWARE=y ++CONFIG_GMAC_ROCKCHIP=y ++CONFIG_PINCTRL=y ++CONFIG_SPL_PINCTRL=y ++CONFIG_DM_PMIC=y ++CONFIG_PMIC_RK8XX=y ++CONFIG_SPL_DM_REGULATOR=y ++CONFIG_REGULATOR_PWM=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_SPL_DM_REGULATOR_FIXED=y ++CONFIG_REGULATOR_RK8XX=y ++CONFIG_PWM_ROCKCHIP=y ++CONFIG_RAM=y ++CONFIG_SPL_RAM=y ++CONFIG_TPL_RAM=y ++CONFIG_DM_RESET=y ++CONFIG_BAUDRATE=1500000 ++CONFIG_DEBUG_UART_SHIFT=2 ++CONFIG_SYSRESET=y ++# CONFIG_TPL_SYSRESET is not set ++CONFIG_USB=y ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_DWC3=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_GENERIC=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_GENERIC=y ++CONFIG_USB_DWC2=y ++CONFIG_USB_DWC3=y ++# CONFIG_USB_DWC3_GADGET is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_DWC2_OTG=y ++CONFIG_SPL_TINY_MEMSET=y ++CONFIG_TPL_TINY_MEMSET=y ++CONFIG_ERRNO_STR=y diff --git a/package/boot/uboot-rockchip/patches/104-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus-LT.patch b/package/boot/uboot-rockchip/patches/104-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus-LT.patch new file mode 100644 index 00000000000..05d569e7203 --- /dev/null +++ b/package/boot/uboot-rockchip/patches/104-rockchip-rk3328-Add-support-for-Orange-Pi-R1-Plus-LT.patch @@ -0,0 +1,242 @@ +From 7a9326a96098bc63d2b60538f657c3a533415276 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Sat, 20 May 2023 18:52:14 +0800 +Subject: [PATCH] rockchip: rk3328: Add support for Orange Pi R1 Plus LTS + +The OrangePi R1 Plus LTS is a minor variant of OrangePi R1 Plus with +the on-board NIC chip changed from rtl8211e to yt8531c, and RAM type +changed from DDR4 to LPDDR3. + +The device tree is taken from kernel v6.4-rc1. + +Signed-off-by: Tianling Shen + +--- + arch/arm/dts/Makefile | 1 + + .../rk3328-orangepi-r1-plus-lts-u-boot.dtsi | 46 +++++++ + arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts | 40 ++++++ + board/rockchip/evb_rk3328/MAINTAINERS | 6 + + configs/orangepi-r1-plus-lts-rk3328_defconfig | 114 ++++++++++++++++++ + 5 files changed, 207 insertions(+) + create mode 100644 arch/arm/dts/rk3328-orangepi-r1-plus-lts-u-boot.dtsi + create mode 100644 arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts + create mode 100644 configs/orangepi-r1-plus-lts-rk3328_defconfig + +--- a/arch/arm/dts/Makefile ++++ b/arch/arm/dts/Makefile +@@ -111,6 +111,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3328) += \ + rk3328-nanopi-r2c.dtb \ + rk3328-nanopi-r2s.dtb \ + rk3328-orangepi-r1-plus.dtb \ ++ rk3328-orangepi-r1-plus-lts.dtb \ + rk3328-roc-cc.dtb \ + rk3328-rock64.dtb \ + rk3328-rock-pi-e.dtb +--- /dev/null ++++ b/arch/arm/dts/rk3328-orangepi-r1-plus-lts-u-boot.dtsi +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * (C) Copyright 2018-2019 Rockchip Electronics Co., Ltd ++ * (C) Copyright 2020 David Bauer ++ */ ++ ++#include "rk3328-u-boot.dtsi" ++#include "rk3328-sdram-lpddr3-666.dtsi" ++/ { ++ chosen { ++ u-boot,spl-boot-order = "same-as-spl", &sdmmc, &emmc; ++ }; ++}; ++ ++&gpio0 { ++ u-boot,dm-spl; ++}; ++ ++&pinctrl { ++ u-boot,dm-spl; ++}; ++ ++&sdmmc0m1_gpio { ++ u-boot,dm-spl; ++}; ++ ++&pcfg_pull_up_4ma { ++ u-boot,dm-spl; ++}; ++ ++/* Need this and all the pinctrl/gpio stuff above to set pinmux */ ++&vcc_sd { ++ u-boot,dm-spl; ++}; ++ ++&gmac2io { ++ snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ snps,reset-active-low; ++ snps,reset-delays-us = <0 10000 50000>; ++}; ++ ++&spi0 { ++ spi_flash: spiflash@0 { ++ u-boot,dm-pre-reloc; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later OR MIT ++/* ++ * Copyright (c) 2016 Xunlong Software. Co., Ltd. ++ * (http://www.orangepi.org) ++ * ++ * Copyright (c) 2021-2023 Tianling Shen ++ */ ++ ++/dts-v1/; ++#include "rk3328-orangepi-r1-plus.dts" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus LTS"; ++ compatible = "xunlong,orangepi-r1-plus-lts", "rockchip,rk3328"; ++}; ++ ++&gmac2io { ++ phy-handle = <&yt8531c>; ++ tx_delay = <0x19>; ++ rx_delay = <0x05>; ++ ++ mdio { ++ /delete-node/ ethernet-phy@1; ++ ++ yt8531c: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ ++ motorcomm,clk-out-frequency-hz = <125000000>; ++ motorcomm,keep-pll-enabled; ++ motorcomm,auto-sleep-disabled; ++ ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <15000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; +--- a/board/rockchip/evb_rk3328/MAINTAINERS ++++ b/board/rockchip/evb_rk3328/MAINTAINERS +@@ -24,6 +24,12 @@ S: Maintained + F: configs/orangepi-r1-plus-rk3328_defconfig + F: arch/arm/dts/rk3328-orangepi-r1-plus-u-boot.dtsi + ++ORANGEPI-R1-PLUS-LTS-RK3328 ++M: Tianling Shen ++S: Maintained ++F: configs/orangepi-r1-plus-lts-rk3328_defconfig ++F: arch/arm/dts/rk3328-orangepi-r1-plus-lts-u-boot.dtsi ++ + ROC-RK3328-CC + M: Loic Devulder + M: Chen-Yu Tsai +--- /dev/null ++++ b/configs/orangepi-r1-plus-lts-rk3328_defconfig +@@ -0,0 +1,98 @@ ++CONFIG_ARM=y ++CONFIG_ARCH_ROCKCHIP=y ++CONFIG_SYS_TEXT_BASE=0x00200000 ++CONFIG_SPL_GPIO_SUPPORT=y ++CONFIG_ENV_OFFSET=0x3F8000 ++CONFIG_ROCKCHIP_RK3328=y ++CONFIG_TPL_ROCKCHIP_COMMON_BOARD=y ++CONFIG_TPL_LIBCOMMON_SUPPORT=y ++CONFIG_TPL_LIBGENERIC_SUPPORT=y ++CONFIG_SPL_DRIVERS_MISC_SUPPORT=y ++CONFIG_SPL_STACK_R_ADDR=0x600000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_DEBUG_UART_BASE=0xFF130000 ++CONFIG_DEBUG_UART_CLOCK=24000000 ++CONFIG_SYSINFO=y ++CONFIG_DEBUG_UART=y ++CONFIG_TPL_SYS_MALLOC_F_LEN=0x800 ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_VERBOSE=y ++CONFIG_SPL_LOAD_FIT=y ++CONFIG_DEFAULT_FDT_FILE="rockchip/rk3328-orangepi-r1-plus-lts.dtb" ++CONFIG_MISC_INIT_R=y ++# CONFIG_DISPLAY_CPUINFO is not set ++CONFIG_DISPLAY_BOARDINFO_LATE=y ++# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set ++CONFIG_TPL_SYS_MALLOC_SIMPLE=y ++CONFIG_SPL_STACK_R=y ++CONFIG_SPL_I2C_SUPPORT=y ++CONFIG_SPL_POWER_SUPPORT=y ++CONFIG_SPL_ATF=y ++CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y ++CONFIG_CMD_BOOTZ=y ++CONFIG_CMD_GPT=y ++CONFIG_CMD_MMC=y ++CONFIG_CMD_USB=y ++# CONFIG_CMD_SETEXPR is not set ++CONFIG_CMD_TIME=y ++CONFIG_SPL_OF_CONTROL=y ++CONFIG_TPL_OF_CONTROL=y ++CONFIG_DEFAULT_DEVICE_TREE="rk3328-orangepi-r1-plus-lts" ++CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" ++CONFIG_TPL_OF_PLATDATA=y ++CONFIG_ENV_IS_IN_MMC=y ++CONFIG_SYS_RELOC_GD_ENV_ADDR=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_TPL_DM=y ++CONFIG_REGMAP=y ++CONFIG_SPL_REGMAP=y ++CONFIG_TPL_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_SPL_SYSCON=y ++CONFIG_TPL_SYSCON=y ++CONFIG_CLK=y ++CONFIG_SPL_CLK=y ++CONFIG_FASTBOOT_BUF_ADDR=0x800800 ++CONFIG_FASTBOOT_CMD_OEM_FORMAT=y ++CONFIG_ROCKCHIP_GPIO=y ++CONFIG_SYS_I2C_ROCKCHIP=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_ROCKCHIP=y ++CONFIG_SF_DEFAULT_SPEED=20000000 ++CONFIG_DM_ETH=y ++CONFIG_ETH_DESIGNWARE=y ++CONFIG_GMAC_ROCKCHIP=y ++CONFIG_PINCTRL=y ++CONFIG_SPL_PINCTRL=y ++CONFIG_DM_PMIC=y ++CONFIG_PMIC_RK8XX=y ++CONFIG_SPL_DM_REGULATOR=y ++CONFIG_REGULATOR_PWM=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_SPL_DM_REGULATOR_FIXED=y ++CONFIG_REGULATOR_RK8XX=y ++CONFIG_PWM_ROCKCHIP=y ++CONFIG_RAM=y ++CONFIG_SPL_RAM=y ++CONFIG_TPL_RAM=y ++CONFIG_DM_RESET=y ++CONFIG_BAUDRATE=1500000 ++CONFIG_DEBUG_UART_SHIFT=2 ++CONFIG_SYSRESET=y ++# CONFIG_TPL_SYSRESET is not set ++CONFIG_USB=y ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_DWC3=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_GENERIC=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_GENERIC=y ++CONFIG_USB_DWC2=y ++CONFIG_USB_DWC3=y ++# CONFIG_USB_DWC3_GADGET is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_DWC2_OTG=y ++CONFIG_SPL_TINY_MEMSET=y ++CONFIG_TPL_TINY_MEMSET=y ++CONFIG_ERRNO_STR=y diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-decl.h b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-decl.h new file mode 100644 index 00000000000..75795aa6cc7 --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-decl.h @@ -0,0 +1,24 @@ +/* + * DO NOT MODIFY + * + * Declares externs for all device/uclass instances. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include +#include + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +extern U_BOOT_DRIVER(rockchip_rk3328_cru); +extern U_BOOT_DRIVER(rockchip_rk3328_dmc); +extern U_BOOT_DRIVER(rockchip_rk3288_dw_mshc); +extern U_BOOT_DRIVER(ns16550_serial); +extern U_BOOT_DRIVER(rockchip_rk3328_spi); +extern U_BOOT_DRIVER(rockchip_rk3328_grf); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(clk); +extern UCLASS_DRIVER(mmc); +extern UCLASS_DRIVER(ram); +extern UCLASS_DRIVER(serial); +extern UCLASS_DRIVER(syscon); diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-plat.c b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-plat.c new file mode 100644 index 00000000000..12081b19e02 --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-plat.c @@ -0,0 +1,170 @@ +/* + * DO NOT MODIFY + * + * Declares the U_BOOT_DRIVER() records and platform data. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +/* Allow use of U_BOOT_DRVINFO() in this file */ +#define DT_PLAT_C + +#include +#include +#include + +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: clock_controller_at_ff440000 rockchip_rk3328_cru + * 1: dmc rockchip_rk3328_dmc + * 2: mmc_at_ff500000 rockchip_rk3288_dw_mshc + * 3: serial_at_ff130000 ns16550_serial + * 4: spi_at_ff190000 rockchip_rk3328_spi + * 5: syscon_at_ff100000 rockchip_rk3328_grf + * --- -------------------- -------------------- + */ + +/* + * Node /clock-controller@ff440000 index 0 + * driver rockchip_rk3328_cru parent None + */ +static struct dtd_rockchip_rk3328_cru dtv_clock_controller_at_ff440000 = { + .reg = {0xff440000, 0x1000}, + .rockchip_grf = 0x38, +}; +U_BOOT_DRVINFO(clock_controller_at_ff440000) = { + .name = "rockchip_rk3328_cru", + .plat = &dtv_clock_controller_at_ff440000, + .plat_size = sizeof(dtv_clock_controller_at_ff440000), + .parent_idx = -1, +}; + +/* + * Node /dmc index 1 + * driver rockchip_rk3328_dmc parent None + */ +static struct dtd_rockchip_rk3328_dmc dtv_dmc = { + .reg = {0xff400000, 0x1000, 0xff780000, 0x3000, 0xff100000, 0x1000, 0xff440000, 0x1000, + 0xff720000, 0x1000, 0xff798000, 0x1000}, + .rockchip_sdram_params = {0x1, 0xc, 0x3, 0x1, 0x0, 0x0, 0x10, 0x10, + 0x10, 0x10, 0x0, 0x8c48a18a, 0x0, 0x21, 0x482, 0x15, + 0x21a, 0xff, 0x14d, 0x6, 0x1, 0x0, 0x0, 0x0, + 0x43041008, 0x64, 0x140023, 0xd0, 0x220002, 0xd4, 0x10000, 0xd8, + 0x703, 0xdc, 0x830004, 0xe0, 0x10000, 0xe4, 0x70003, 0xf4, + 0xf011f, 0x100, 0x6090b07, 0x104, 0x2020b, 0x108, 0x2030506, 0x10c, + 0x505000, 0x110, 0x3020204, 0x114, 0x1010303, 0x118, 0x2020003, 0x120, + 0x303, 0x138, 0x25, 0x180, 0x3c000f, 0x184, 0x900000, 0x190, + 0x7020000, 0x198, 0x5001100, 0x1a0, 0xc0400003, 0x240, 0x900090c, 0x244, + 0x101, 0x250, 0xf00, 0x490, 0x1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x4, 0xb, 0x28, 0x6, 0x2c, + 0x0, 0x30, 0x3, 0xffffffff, 0xffffffff, 0x77, 0x88, 0x79, + 0x79, 0x87, 0x97, 0x87, 0x78, 0x77, 0x78, 0x87, + 0x88, 0x87, 0x87, 0x77, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x69, 0x9, 0x77, + 0x78, 0x77, 0x78, 0x77, 0x78, 0x77, 0x78, 0x77, + 0x79, 0x9, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x69, 0x9, 0x77, 0x78, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x79, 0x9, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x69, 0x9, 0x77, 0x78, 0x77, 0x78, 0x77, + 0x78, 0x77, 0x78, 0x77, 0x79, 0x9, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x69, + 0x9, 0x77, 0x78, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x79, 0x9}, +}; +U_BOOT_DRVINFO(dmc) = { + .name = "rockchip_rk3328_dmc", + .plat = &dtv_dmc, + .plat_size = sizeof(dtv_dmc), + .parent_idx = -1, +}; + +/* + * Node /mmc@ff500000 index 2 + * driver rockchip_rk3288_dw_mshc parent None + */ +static struct dtd_rockchip_rk3288_dw_mshc dtv_mmc_at_ff500000 = { + .bus_width = 0x4, + .cap_sd_highspeed = true, + .clocks = { + {0, {317}}, + {0, {33}}, + {0, {74}}, + {0, {78}},}, + .disable_wp = true, + .fifo_depth = 0x100, + .interrupts = {0x0, 0xc, 0x4}, + .max_frequency = 0x8f0d180, + .pinctrl_0 = {0x45, 0x46, 0x47, 0x48}, + .pinctrl_names = "default", + .reg = {0xff500000, 0x4000}, + .u_boot_spl_fifo_mode = true, + .vmmc_supply = 0x49, +}; +U_BOOT_DRVINFO(mmc_at_ff500000) = { + .name = "rockchip_rk3288_dw_mshc", + .plat = &dtv_mmc_at_ff500000, + .plat_size = sizeof(dtv_mmc_at_ff500000), + .parent_idx = -1, +}; + +/* + * Node /serial@ff130000 index 3 + * driver ns16550_serial parent None + */ +static struct dtd_ns16550_serial dtv_serial_at_ff130000 = { + .clock_frequency = 0x16e3600, + .clocks = { + {0, {40}}, + {0, {212}},}, + .dma_names = {"tx", "rx"}, + .dmas = {0x10, 0x6, 0x10, 0x7}, + .interrupts = {0x0, 0x39, 0x4}, + .pinctrl_0 = 0x24, + .pinctrl_names = "default", + .reg = {0xff130000, 0x100}, + .reg_io_width = 0x4, + .reg_shift = 0x2, +}; +U_BOOT_DRVINFO(serial_at_ff130000) = { + .name = "ns16550_serial", + .plat = &dtv_serial_at_ff130000, + .plat_size = sizeof(dtv_serial_at_ff130000), + .parent_idx = -1, +}; + +/* Node /spi@ff190000 index 4 */ +static struct dtd_rockchip_rk3328_spi dtv_spi_at_ff190000 = { + .clocks = { + {0, {32}}, + {0, {209}},}, + .dma_names = {"tx", "rx"}, + .dmas = {0x10, 0x8, 0x10, 0x9}, + .interrupts = {0x0, 0x31, 0x4}, + .pinctrl_0 = {0x2c, 0x2d, 0x2e, 0x2f}, + .pinctrl_names = "default", + .reg = {0xff190000, 0x1000}, +}; +U_BOOT_DRVINFO(spi_at_ff190000) = { + .name = "rockchip_rk3328_spi", + .plat = &dtv_spi_at_ff190000, + .plat_size = sizeof(dtv_spi_at_ff190000), + .parent_idx = -1, +}; + +/* + * Node /syscon@ff100000 index 5 + * driver rockchip_rk3328_grf parent None + */ +static struct dtd_rockchip_rk3328_grf dtv_syscon_at_ff100000 = { + .reg = {0xff100000, 0x1000}, +}; +U_BOOT_DRVINFO(syscon_at_ff100000) = { + .name = "rockchip_rk3328_grf", + .plat = &dtv_syscon_at_ff100000, + .plat_size = sizeof(dtv_syscon_at_ff100000), + .parent_idx = -1, +}; + diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-structs-gen.h b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-structs-gen.h new file mode 100644 index 00000000000..d0958315310 --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-lts-rk3328/dt-structs-gen.h @@ -0,0 +1,55 @@ +/* + * DO NOT MODIFY + * + * Defines the structs used to hold devicetree data. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include +#include +struct dtd_ns16550_serial { + fdt32_t clock_frequency; + struct phandle_1_arg clocks[2]; + const char * dma_names[2]; + fdt32_t dmas[4]; + fdt32_t interrupts[3]; + fdt32_t pinctrl_0; + const char * pinctrl_names; + fdt64_t reg[2]; + fdt32_t reg_io_width; + fdt32_t reg_shift; +}; +struct dtd_rockchip_rk3288_dw_mshc { + fdt32_t bus_width; + bool cap_sd_highspeed; + struct phandle_1_arg clocks[4]; + bool disable_wp; + fdt32_t fifo_depth; + fdt32_t interrupts[3]; + fdt32_t max_frequency; + fdt32_t pinctrl_0[4]; + const char * pinctrl_names; + fdt64_t reg[2]; + bool u_boot_spl_fifo_mode; + fdt32_t vmmc_supply; +}; +struct dtd_rockchip_rk3328_cru { + fdt64_t reg[2]; + fdt32_t rockchip_grf; +}; +struct dtd_rockchip_rk3328_dmc { + fdt64_t reg[12]; + fdt32_t rockchip_sdram_params[196]; +}; +struct dtd_rockchip_rk3328_grf { + fdt64_t reg[2]; +}; +struct dtd_rockchip_rk3328_spi { + struct phandle_1_arg clocks[2]; + const char * dma_names[2]; + fdt32_t dmas[4]; + fdt32_t interrupts[3]; + fdt32_t pinctrl_0[4]; + const char * pinctrl_names; + fdt64_t reg[2]; +}; diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-decl.h b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-decl.h new file mode 100644 index 00000000000..75795aa6cc7 --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-decl.h @@ -0,0 +1,24 @@ +/* + * DO NOT MODIFY + * + * Declares externs for all device/uclass instances. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include +#include + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +extern U_BOOT_DRIVER(rockchip_rk3328_cru); +extern U_BOOT_DRIVER(rockchip_rk3328_dmc); +extern U_BOOT_DRIVER(rockchip_rk3288_dw_mshc); +extern U_BOOT_DRIVER(ns16550_serial); +extern U_BOOT_DRIVER(rockchip_rk3328_spi); +extern U_BOOT_DRIVER(rockchip_rk3328_grf); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(clk); +extern UCLASS_DRIVER(mmc); +extern UCLASS_DRIVER(ram); +extern UCLASS_DRIVER(serial); +extern UCLASS_DRIVER(syscon); diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-plat.c b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-plat.c new file mode 100644 index 00000000000..90656fc306d --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-plat.c @@ -0,0 +1,170 @@ +/* + * DO NOT MODIFY + * + * Declares the U_BOOT_DRIVER() records and platform data. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +/* Allow use of U_BOOT_DRVINFO() in this file */ +#define DT_PLAT_C + +#include +#include +#include + +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: clock_controller_at_ff440000 rockchip_rk3328_cru + * 1: dmc rockchip_rk3328_dmc + * 2: mmc_at_ff500000 rockchip_rk3288_dw_mshc + * 3: serial_at_ff130000 ns16550_serial + * 4: spi_at_ff190000 rockchip_rk3328_spi + * 5: syscon_at_ff100000 rockchip_rk3328_grf + * --- -------------------- -------------------- + */ + +/* + * Node /clock-controller@ff440000 index 0 + * driver rockchip_rk3328_cru parent None + */ +static struct dtd_rockchip_rk3328_cru dtv_clock_controller_at_ff440000 = { + .reg = {0xff440000, 0x1000}, + .rockchip_grf = 0x38, +}; +U_BOOT_DRVINFO(clock_controller_at_ff440000) = { + .name = "rockchip_rk3328_cru", + .plat = &dtv_clock_controller_at_ff440000, + .plat_size = sizeof(dtv_clock_controller_at_ff440000), + .parent_idx = -1, +}; + +/* + * Node /dmc index 1 + * driver rockchip_rk3328_dmc parent None + */ +static struct dtd_rockchip_rk3328_dmc dtv_dmc = { + .reg = {0xff400000, 0x1000, 0xff780000, 0x3000, 0xff100000, 0x1000, 0xff440000, 0x1000, + 0xff720000, 0x1000, 0xff798000, 0x1000}, + .rockchip_sdram_params = {0x1, 0xa, 0x2, 0x1, 0x0, 0x0, 0x11, 0x0, + 0x11, 0x0, 0x0, 0x94291288, 0x0, 0x27, 0x462, 0x15, + 0x242, 0xff, 0x14d, 0x0, 0x1, 0x0, 0x0, 0x0, + 0x43049010, 0x64, 0x28003b, 0xd0, 0x20053, 0xd4, 0x220000, 0xd8, + 0x100, 0xdc, 0x40000, 0xe0, 0x0, 0xe4, 0x110000, 0xe8, + 0x420, 0xec, 0x400, 0xf4, 0xf011f, 0x100, 0x9060b06, 0x104, + 0x20209, 0x108, 0x505040a, 0x10c, 0x40400c, 0x110, 0x5030206, 0x114, + 0x3030202, 0x120, 0x3030b03, 0x124, 0x20208, 0x180, 0x1000040, 0x184, + 0x0, 0x190, 0x7030003, 0x198, 0x5001100, 0x1a0, 0xc0400003, 0x240, + 0x6000604, 0x244, 0x201, 0x250, 0xf00, 0x490, 0x1, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x4, 0xc, 0x28, 0xa, 0x2c, + 0x0, 0x30, 0x9, 0xffffffff, 0xffffffff, 0x77, 0x88, 0x79, + 0x79, 0x87, 0x97, 0x87, 0x78, 0x77, 0x78, 0x87, + 0x88, 0x87, 0x87, 0x77, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x69, 0x9, 0x77, + 0x78, 0x77, 0x78, 0x77, 0x78, 0x77, 0x78, 0x77, + 0x79, 0x9, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x69, 0x9, 0x77, 0x78, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x79, 0x9, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x69, 0x9, 0x77, 0x78, 0x77, 0x78, 0x77, + 0x78, 0x77, 0x78, 0x77, 0x79, 0x9, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x69, + 0x9, 0x77, 0x78, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x79, 0x9}, +}; +U_BOOT_DRVINFO(dmc) = { + .name = "rockchip_rk3328_dmc", + .plat = &dtv_dmc, + .plat_size = sizeof(dtv_dmc), + .parent_idx = -1, +}; + +/* + * Node /mmc@ff500000 index 2 + * driver rockchip_rk3288_dw_mshc parent None + */ +static struct dtd_rockchip_rk3288_dw_mshc dtv_mmc_at_ff500000 = { + .bus_width = 0x4, + .cap_sd_highspeed = true, + .clocks = { + {0, {317}}, + {0, {33}}, + {0, {74}}, + {0, {78}},}, + .disable_wp = true, + .fifo_depth = 0x100, + .interrupts = {0x0, 0xc, 0x4}, + .max_frequency = 0x8f0d180, + .pinctrl_0 = {0x45, 0x46, 0x47, 0x48}, + .pinctrl_names = "default", + .reg = {0xff500000, 0x4000}, + .u_boot_spl_fifo_mode = true, + .vmmc_supply = 0x49, +}; +U_BOOT_DRVINFO(mmc_at_ff500000) = { + .name = "rockchip_rk3288_dw_mshc", + .plat = &dtv_mmc_at_ff500000, + .plat_size = sizeof(dtv_mmc_at_ff500000), + .parent_idx = -1, +}; + +/* + * Node /serial@ff130000 index 3 + * driver ns16550_serial parent None + */ +static struct dtd_ns16550_serial dtv_serial_at_ff130000 = { + .clock_frequency = 0x16e3600, + .clocks = { + {0, {40}}, + {0, {212}},}, + .dma_names = {"tx", "rx"}, + .dmas = {0x10, 0x6, 0x10, 0x7}, + .interrupts = {0x0, 0x39, 0x4}, + .pinctrl_0 = 0x24, + .pinctrl_names = "default", + .reg = {0xff130000, 0x100}, + .reg_io_width = 0x4, + .reg_shift = 0x2, +}; +U_BOOT_DRVINFO(serial_at_ff130000) = { + .name = "ns16550_serial", + .plat = &dtv_serial_at_ff130000, + .plat_size = sizeof(dtv_serial_at_ff130000), + .parent_idx = -1, +}; + +/* Node /spi@ff190000 index 4 */ +static struct dtd_rockchip_rk3328_spi dtv_spi_at_ff190000 = { + .clocks = { + {0, {32}}, + {0, {209}},}, + .dma_names = {"tx", "rx"}, + .dmas = {0x10, 0x8, 0x10, 0x9}, + .interrupts = {0x0, 0x31, 0x4}, + .pinctrl_0 = {0x2c, 0x2d, 0x2e, 0x2f}, + .pinctrl_names = "default", + .reg = {0xff190000, 0x1000}, +}; +U_BOOT_DRVINFO(spi_at_ff190000) = { + .name = "rockchip_rk3328_spi", + .plat = &dtv_spi_at_ff190000, + .plat_size = sizeof(dtv_spi_at_ff190000), + .parent_idx = -1, +}; + +/* + * Node /syscon@ff100000 index 5 + * driver rockchip_rk3328_grf parent None + */ +static struct dtd_rockchip_rk3328_grf dtv_syscon_at_ff100000 = { + .reg = {0xff100000, 0x1000}, +}; +U_BOOT_DRVINFO(syscon_at_ff100000) = { + .name = "rockchip_rk3328_grf", + .plat = &dtv_syscon_at_ff100000, + .plat_size = sizeof(dtv_syscon_at_ff100000), + .parent_idx = -1, +}; + diff --git a/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-structs-gen.h b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-structs-gen.h new file mode 100644 index 00000000000..d0958315310 --- /dev/null +++ b/package/boot/uboot-rockchip/src/of-platdata/orangepi-r1-plus-rk3328/dt-structs-gen.h @@ -0,0 +1,55 @@ +/* + * DO NOT MODIFY + * + * Defines the structs used to hold devicetree data. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include +#include +struct dtd_ns16550_serial { + fdt32_t clock_frequency; + struct phandle_1_arg clocks[2]; + const char * dma_names[2]; + fdt32_t dmas[4]; + fdt32_t interrupts[3]; + fdt32_t pinctrl_0; + const char * pinctrl_names; + fdt64_t reg[2]; + fdt32_t reg_io_width; + fdt32_t reg_shift; +}; +struct dtd_rockchip_rk3288_dw_mshc { + fdt32_t bus_width; + bool cap_sd_highspeed; + struct phandle_1_arg clocks[4]; + bool disable_wp; + fdt32_t fifo_depth; + fdt32_t interrupts[3]; + fdt32_t max_frequency; + fdt32_t pinctrl_0[4]; + const char * pinctrl_names; + fdt64_t reg[2]; + bool u_boot_spl_fifo_mode; + fdt32_t vmmc_supply; +}; +struct dtd_rockchip_rk3328_cru { + fdt64_t reg[2]; + fdt32_t rockchip_grf; +}; +struct dtd_rockchip_rk3328_dmc { + fdt64_t reg[12]; + fdt32_t rockchip_sdram_params[196]; +}; +struct dtd_rockchip_rk3328_grf { + fdt64_t reg[2]; +}; +struct dtd_rockchip_rk3328_spi { + struct phandle_1_arg clocks[2]; + const char * dma_names[2]; + fdt32_t dmas[4]; + fdt32_t interrupts[3]; + fdt32_t pinctrl_0[4]; + const char * pinctrl_names; + fdt64_t reg[2]; +}; diff --git a/package/boot/uboot-sifiveu/Makefile b/package/boot/uboot-sifiveu/Makefile new file mode 100644 index 00000000000..5def1c1fcb8 --- /dev/null +++ b/package/boot/uboot-sifiveu/Makefile @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 OpenWrt.org +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_RELEASE:=1 +PKG_VERSION:=2022.10 +PKG_HASH:=50b4482a505bc281ba8470c399a3c26e145e29b23500bc35c50debd7fa46bdf8 + +include $(INCLUDE_DIR)/u-boot.mk +include $(INCLUDE_DIR)/package.mk + +define U-Boot/Default + BUILD_TARGET:=sifiveu + BUILD_DEVICES=$(1) + UBOOT_IMAGE:=u-boot.itb + DTS_DIR:=arch/riscv/dts + UENV:=default + DEFAULT:=y +endef + +define U-Boot/sifive_unleashed + NAME:=SiFive Unleashed + OPENSBI:=generic + DEPENDS:=+opensbi_generic + UBOOT_DTS:=hifive-unleashed-a00.dtb + BUILD_DEVICES:=sifive_unleashed +endef + +define U-Boot/sifive_unmatched + NAME:=SiFive Unmatched + OPENSBI:=generic + DEPENDS:=+opensbi_generic + UBOOT_DTS:=hifive-unmatched-a00.dtb + BUILD_DEVICES:=sifive_unmatched +endef + +UBOOT_TARGETS := \ + sifive_unleashed \ + sifive_unmatched + +UBOOT_MAKE_FLAGS += \ + OPENSBI=$(STAGING_DIR_IMAGE)/fw_dynamic-${OPENSBI}.bin + +define Build/Configure + $(call Build/Configure/U-Boot) + sed -i 's/CONFIG_TOOLS_LIBCRYPTO=y/# CONFIG_TOOLS_LIBCRYPTO is not set/' $(PKG_BUILD_DIR)/.config +endef + +define Build/InstallDev + $(INSTALL_DIR) $(STAGING_DIR_IMAGE) + $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(UBOOT_IMAGE) $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-$(UBOOT_IMAGE) + $(INSTALL_BIN) $(PKG_BUILD_DIR)/spl/u-boot-spl.bin $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-$(UBOOT_IMAGE)-spl + $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(DTS_DIR)/$(UBOOT_DTS) $(STAGING_DIR_IMAGE)/$(UBOOT_DTS) + + mkimage -C none -A arm -T script -d uEnv-$(UENV).txt \ + $(STAGING_DIR_IMAGE)/$(BUILD_DEVICES)-boot.scr +endef + +$(eval $(call BuildPackage/U-Boot)) diff --git a/package/boot/uboot-sifiveu/patches/0002-board-sifive-spl-Initialized-the-PWM-setting-in-the-.patch b/package/boot/uboot-sifiveu/patches/0002-board-sifive-spl-Initialized-the-PWM-setting-in-the-.patch new file mode 100644 index 00000000000..27cda75326f --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0002-board-sifive-spl-Initialized-the-PWM-setting-in-the-.patch @@ -0,0 +1,104 @@ +From 725595e667cc4423347c255da8ca4c5b3aa0980a Mon Sep 17 00:00:00 2001 +From: Vincent Chen +Date: Mon, 15 Nov 2021 03:31:04 -0800 +Subject: [PATCH 2/8] board: sifive: spl: Initialized the PWM setting in the + SPL stage + +LEDs and multiple fans can be controlled by SPL. This patch ensures +that all fans have been enabled in the SPL stage. In addition, the +LED's color will be set to yellow. +--- + board/sifive/unmatched/Makefile | 1 + + board/sifive/unmatched/pwm.c | 57 +++++++++++++++++++++++++++++++++ + board/sifive/unmatched/spl.c | 2 ++ + 3 files changed, 60 insertions(+) + create mode 100644 board/sifive/unmatched/pwm.c + +diff --git a/board/sifive/unmatched/Makefile b/board/sifive/unmatched/Makefile +index 1345330089..5df01982e9 100644 +--- a/board/sifive/unmatched/Makefile ++++ b/board/sifive/unmatched/Makefile +@@ -9,3 +9,4 @@ obj-y += spl.o + else + obj-y += unmatched.o + endif ++obj-y += pwm.o +diff --git a/board/sifive/unmatched/pwm.c b/board/sifive/unmatched/pwm.c +new file mode 100644 +index 0000000000..e1cc02310a +--- /dev/null ++++ b/board/sifive/unmatched/pwm.c +@@ -0,0 +1,57 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2021, SiFive Inc ++ * ++ * Authors: ++ * Vincent Chen ++ * David Abdurachmanov ++ */ ++ ++#include ++#include ++ ++struct pwm_sifive_regs { ++ unsigned int cfg; /* PWM configuration register */ ++ unsigned int pad0; /* Reserved */ ++ unsigned int cnt; /* PWM count register */ ++ unsigned int pad1; /* Reserved */ ++ unsigned int pwms; /* Scaled PWM count register */ ++ unsigned int pad2; /* Reserved */ ++ unsigned int pad3; /* Reserved */ ++ unsigned int pad4; /* Reserved */ ++ unsigned int cmp0; /* PWM 0 compare register */ ++ unsigned int cmp1; /* PWM 1 compare register */ ++ unsigned int cmp2; /* PWM 2 compare register */ ++ unsigned int cmp3; /* PWM 3 compare register */ ++}; ++ ++#define PWM0_BASE 0x10020000 ++#define PWM1_BASE 0x10021000 ++#define PWM_CFG_INIT 0x1000 ++#define PWM_CMP_ENABLE_VAL 0x0 ++#define PWM_CMP_DISABLE_VAL 0xffff ++ ++void pwm_device_init(void) ++{ ++ struct pwm_sifive_regs *pwm0, *pwm1; ++ pwm0 = (struct pwm_sifive_regs *)PWM0_BASE; ++ pwm1 = (struct pwm_sifive_regs *)PWM1_BASE; ++ writel(PWM_CMP_DISABLE_VAL, (void *)&pwm0->cmp0); ++ /* Set the 3-color PWM LEDs to yellow in SPL */ ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm0->cmp1); ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm0->cmp2); ++ writel(PWM_CMP_DISABLE_VAL, (void *)&pwm0->cmp3); ++ writel(PWM_CFG_INIT, (void *)&pwm0->cfg); ++ ++ writel(PWM_CMP_DISABLE_VAL, (void *)&pwm0->cmp3); ++ /* Turn on all the fans, (J21), (J23) and (J24), on the unmatched board */ ++ /* The SoC fan(J21) on the rev3 board cannot be controled by PWM_COMP0, ++ so here sets the initial value of PWM_COMP0 as DISABLE */ ++ if (get_pcb_revision_from_eeprom() == PCB_REVISION_REV3) ++ writel(PWM_CMP_DISABLE_VAL, (void *)&pwm1->cmp1); ++ else ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm1->cmp1); ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm1->cmp2); ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm1->cmp3); ++ writel(PWM_CFG_INIT, (void *)&pwm1->cfg); ++} +diff --git a/board/sifive/unmatched/spl.c b/board/sifive/unmatched/spl.c +index 7c0beedc08..f3a661a81e 100644 +--- a/board/sifive/unmatched/spl.c ++++ b/board/sifive/unmatched/spl.c +@@ -90,6 +90,8 @@ int spl_board_init_f(void) + goto end; + } + ++ pwm_device_init(); ++ + ret = spl_gemgxl_init(); + if (ret) { + debug("Gigabit ethernet PHY (VSC8541) init failed: %d\n", ret); +-- +2.27.0 + diff --git a/package/boot/uboot-sifiveu/patches/0003-board-sifive-Set-LED-s-color-to-purple-in-the-U-boot.patch b/package/boot/uboot-sifiveu/patches/0003-board-sifive-Set-LED-s-color-to-purple-in-the-U-boot.patch new file mode 100644 index 00000000000..9820d2e2f9d --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0003-board-sifive-Set-LED-s-color-to-purple-in-the-U-boot.patch @@ -0,0 +1,68 @@ +From 7ead6d662a2f9d8498af6650ea38418c64b52048 Mon Sep 17 00:00:00 2001 +From: Vincent Chen +Date: Mon, 24 Jan 2022 02:42:02 -0800 +Subject: [PATCH 3/8] board: sifive: Set LED's color to purple in the U-boot + stage + +Set LED's color to purple in the U-boot stage. Because there are still +some functions to be executed before board_early_init_f(), it means +the LED's is not changed to purple instantly when entering the U-boot +stage. +--- + board/sifive/unmatched/pwm.c | 7 +++++++ + board/sifive/unmatched/unmatched.c | 6 ++++++ + configs/sifive_unmatched_defconfig | 1 + + 3 files changed, 14 insertions(+) + +diff --git a/board/sifive/unmatched/pwm.c b/board/sifive/unmatched/pwm.c +index e1cc02310a..bd67672c22 100644 +--- a/board/sifive/unmatched/pwm.c ++++ b/board/sifive/unmatched/pwm.c +@@ -36,6 +36,7 @@ void pwm_device_init(void) + struct pwm_sifive_regs *pwm0, *pwm1; + pwm0 = (struct pwm_sifive_regs *)PWM0_BASE; + pwm1 = (struct pwm_sifive_regs *)PWM1_BASE; ++#ifdef CONFIG_SPL_BUILD + writel(PWM_CMP_DISABLE_VAL, (void *)&pwm0->cmp0); + /* Set the 3-color PWM LEDs to yellow in SPL */ + writel(PWM_CMP_ENABLE_VAL, (void *)&pwm0->cmp1); +@@ -54,4 +55,10 @@ void pwm_device_init(void) + writel(PWM_CMP_ENABLE_VAL, (void *)&pwm1->cmp2); + writel(PWM_CMP_ENABLE_VAL, (void *)&pwm1->cmp3); + writel(PWM_CFG_INIT, (void *)&pwm1->cfg); ++#else ++ /* Set the 3-color PWM LEDs to purple in U-boot */ ++ writel(PWM_CMP_DISABLE_VAL, (void *)&pwm0->cmp1); ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm0->cmp2); ++ writel(PWM_CMP_ENABLE_VAL, (void *)&pwm0->cmp3); ++#endif + } +diff --git a/board/sifive/unmatched/unmatched.c b/board/sifive/unmatched/unmatched.c +index 6295deeae2..30c082d001 100644 +--- a/board/sifive/unmatched/unmatched.c ++++ b/board/sifive/unmatched/unmatched.c +@@ -22,6 +22,12 @@ void *board_fdt_blob_setup(int *err) + return (ulong *)&_end; + } + ++int board_early_init_f(void) ++{ ++ pwm_device_init(); ++ return 0; ++} ++ + int board_init(void) + { + /* enable all cache ways */ +diff --git a/configs/sifive_unmatched_defconfig b/configs/sifive_unmatched_defconfig +index d400ed0b23..0758f8e90f 100644 +--- a/configs/sifive_unmatched_defconfig ++++ b/configs/sifive_unmatched_defconfig +@@ -51,3 +51,4 @@ CONFIG_DM_SCSI=y + CONFIG_USB=y + CONFIG_USB_XHCI_HCD=y + CONFIG_USB_XHCI_PCI=y ++CONFIG_BOARD_EARLY_INIT_F=y +-- +2.27.0 + diff --git a/package/boot/uboot-sifiveu/patches/0004-board-sifive-Set-LED-s-color-to-blue-before-jumping-.patch b/package/boot/uboot-sifiveu/patches/0004-board-sifive-Set-LED-s-color-to-blue-before-jumping-.patch new file mode 100644 index 00000000000..b5bffd22bd6 --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0004-board-sifive-Set-LED-s-color-to-blue-before-jumping-.patch @@ -0,0 +1,30 @@ +From 6ef7023c0dcfde320015ab19e0e0d423921be77d Mon Sep 17 00:00:00 2001 +From: Vincent Chen +Date: Mon, 15 Nov 2021 03:39:07 -0800 +Subject: [PATCH 1/2] board: sifive: Set LED's color to blue before jumping to + Linux + +The LED's color wil be changed from purple to blue before executing +the sysboot command. Because the sysboot command includes the image loading +from the boot partition, It means the LED's color is blue when executing +"Retrieving file: /Image.gz". +--- + include/configs/sifive-unmatched.h | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/include/configs/sifive-unmatched.h ++++ b/include/configs/sifive-unmatched.h +@@ -49,7 +49,12 @@ + "type_guid_gpt_system=" TYPE_GUID_SYSTEM "\0" \ + "partitions=" PARTS_DEFAULT "\0" \ + "fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \ +- BOOTENV ++ "setled_blue=mw.l 0x10020024 0x0000ffff; mw.l 0x10020028 0x0000ffff; mw.l 0x1002002c 0x0\0" \ ++ BOOTENV \ ++ "boot_extlinux=" \ ++ "run setled_blue; " \ ++ "sysboot ${devtype} ${devnum}:${distro_bootpart} any " \ ++ "${scriptaddr} ${prefix}${boot_syslinux_conf};\0" + + #define CONFIG_SYS_EEPROM_BUS_NUM 0 + diff --git a/package/boot/uboot-sifiveu/patches/0005-board-sifive-spl-Set-remote-thermal-of-TMP451-to-85-.patch b/package/boot/uboot-sifiveu/patches/0005-board-sifive-spl-Set-remote-thermal-of-TMP451-to-85-.patch new file mode 100644 index 00000000000..dc0d04151fa --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0005-board-sifive-spl-Set-remote-thermal-of-TMP451-to-85-.patch @@ -0,0 +1,111 @@ +From 07f84ed283b913cbdf87181ae2ed65467d923df5 Mon Sep 17 00:00:00 2001 +From: Vincent Chen +Date: Mon, 24 Jan 2022 02:57:40 -0800 +Subject: [PATCH 2/2] board: sifive: spl: Set remote thermal of TMP451 to 85 + deg C for the unmatched board + +For TMP451 on the unmatched board, the default value of the remote +thermal threshold is 108 deg C. This commit initilizes it to 85 deg C at SPL. +--- + board/sifive/unmatched/spl.c | 29 +++++++++++++++++++++++++++++ + drivers/misc/Kconfig | 10 ++++++++++ + include/configs/sifive-unmatched.h | 4 ++++ + scripts/config_whitelist.txt | 1 + + 4 files changed, 44 insertions(+) + +--- a/board/sifive/unmatched/spl.c ++++ b/board/sifive/unmatched/spl.c +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -26,6 +28,27 @@ + #define MODE_SELECT_SD 0xb + #define MODE_SELECT_MASK GENMASK(3, 0) + ++#define TMP451_REMOTE_THERM_LIMIT_REG_OFFSET 0x19 ++#define TMP451_REMOTE_THERM_LIMIT_INIT_VALUE 0x55 ++ ++static inline int init_tmp451_remote_therm_limit(void) ++{ ++ struct udevice *dev; ++ unsigned char r_therm_limit = TMP451_REMOTE_THERM_LIMIT_INIT_VALUE; ++ int ret; ++ ++ ret = i2c_get_chip_for_busnum(CONFIG_SYS_TMP451_BUS_NUM, ++ CONFIG_SYS_I2C_TMP451_ADDR, ++ CONFIG_SYS_I2C_TMP451_ADDR_LEN, ++ &dev); ++ ++ if (!ret) ++ ret = dm_i2c_write(dev, TMP451_REMOTE_THERM_LIMIT_REG_OFFSET, ++ &r_therm_limit, ++ sizeof(unsigned char)); ++ return ret; ++} ++ + static inline int spl_reset_device_by_gpio(const char *label, int pin, int low_width) + { + int ret; +@@ -92,6 +115,12 @@ int spl_board_init_f(void) + + pwm_device_init(); + ++ ret = init_tmp451_remote_therm_limit(); ++ if (ret) { ++ debug("TMP451 remote THERM limit init failed: %d\n", ret); ++ goto end; ++ } ++ + ret = spl_gemgxl_init(); + if (ret) { + debug("Gigabit ethernet PHY (VSC8541) init failed: %d\n", ret); +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -536,8 +536,18 @@ config SYS_I2C_EEPROM_ADDR + depends on ID_EEPROM || I2C_EEPROM || SPL_I2C_EEPROM || CMD_EEPROM || ENV_IS_IN_EEPROM + default 0 + ++config SYS_I2C_TMP451_ADDR ++ hex "Chip address of the TMP451 device" ++ default 0 ++ + if I2C_EEPROM + ++config SYS_I2C_TMP451_ADDR_LEN ++ int "Length in bytes of the TMP451 memory array address" ++ default 1 ++ help ++ Note: This is NOT the chip address length! ++ + config SYS_I2C_EEPROM_ADDR_OVERFLOW + hex "EEPROM Address Overflow" + default 0x0 +--- a/include/configs/sifive-unmatched.h ++++ b/include/configs/sifive-unmatched.h +@@ -15,6 +15,10 @@ + + #define CONFIG_STANDALONE_LOAD_ADDR 0x80200000 + ++#define CONFIG_SYS_TMP451_BUS_NUM 0 ++#define CONFIG_SYS_I2C_TMP451_ADDR 0x4c ++#define CONFIG_SYS_I2C_TMP451_ADDR_LEN 0x1 ++ + /* Environment options */ + + #define BOOT_TARGET_DEVICES(func) \ +--- a/scripts/config_whitelist.txt ++++ b/scripts/config_whitelist.txt +@@ -1268,6 +1268,7 @@ CONFIG_SYS_TIMER_BASE + CONFIG_SYS_TIMER_COUNTER + CONFIG_SYS_TIMER_COUNTS_DOWN + CONFIG_SYS_TIMER_RATE ++CONFIG_SYS_TMP451_BUS_NUM + CONFIG_SYS_TMPVIRT + CONFIG_SYS_TSEC1_OFFSET + CONFIG_SYS_TX_ETH_BUFFER diff --git a/package/boot/uboot-sifiveu/patches/0008-riscv-dts-Add-few-PMU-events.patch b/package/boot/uboot-sifiveu/patches/0008-riscv-dts-Add-few-PMU-events.patch new file mode 100644 index 00000000000..3f3feb9da70 --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0008-riscv-dts-Add-few-PMU-events.patch @@ -0,0 +1,36 @@ +From c29e4d84cfa17ab96eff2a9044f486ba3c8b5c43 Mon Sep 17 00:00:00 2001 +From: Atish Patra +Date: Mon, 25 Oct 2021 11:35:41 -0700 +Subject: [PATCH] riscv: dts: Add few PMU events + +fu740 has 2 HPM counters and many HPM events defined in the fu740 manual[1]. +This patch adds some of these events and their mapping as per the +OpenSBI PMU DT binding for now. + +[1]https://sifive.cdn.prismic.io/sifive/de1491e5-077c-461d-9605-e8a0ce57337d_fu740-c000-manual-v1p3.pdf + +Signed-off-by: Atish Patra +--- + arch/riscv/dts/fu740-c000.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/arch/riscv/dts/fu740-c000.dtsi ++++ b/arch/riscv/dts/fu740-c000.dtsi +@@ -140,6 +140,17 @@ + #size-cells = <2>; + compatible = "simple-bus"; + ranges; ++ pmu { ++ compatible = "riscv,pmu"; ++ riscv,raw-event-to-mhpmcounters = <0x00000000 0x200 0x18 ++ 0x00000000 0x400 0x18 ++ 0x00000000 0x800 0x18>; ++ riscv,event-to-mhpmcounters = <0x05 0x06 0x18 ++ 0x10009 0x10009 0x18>; ++ riscv,event-to-mhpmevent = <0x05 0x00000000 0x4000 ++ 0x06 0x00000000 0x4001 ++ 0x10008 0x00000000 0x102>; ++ }; + plic0: interrupt-controller@c000000 { + #interrupt-cells = <1>; + #address-cells = <0>; diff --git a/package/boot/uboot-sifiveu/patches/0009-riscv-Fix-build-against-binutils.patch b/package/boot/uboot-sifiveu/patches/0009-riscv-Fix-build-against-binutils.patch new file mode 100644 index 00000000000..87dbf984ec5 --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/0009-riscv-Fix-build-against-binutils.patch @@ -0,0 +1,50 @@ +commit 1dde977518f13824b847e23275001191139bc384 +Author: Alexandre Ghiti +Date: Mon Oct 3 18:07:54 2022 +0200 + + riscv: Fix build against binutils 2.38 + + The following description is copied from the equivalent patch for the + Linux Kernel proposed by Aurelien Jarno: + + >From version 2.38, binutils default to ISA spec version 20191213. This + means that the csr read/write (csrr*/csrw*) instructions and fence.i + instruction has separated from the `I` extension, become two standalone + extensions: Zicsr and Zifencei. As the kernel uses those instruction, + this causes the following build failure: + + arch/riscv/cpu/mtrap.S: Assembler messages: + arch/riscv/cpu/mtrap.S:65: Error: unrecognized opcode `csrr a0,scause' + arch/riscv/cpu/mtrap.S:66: Error: unrecognized opcode `csrr a1,sepc' + arch/riscv/cpu/mtrap.S:67: Error: unrecognized opcode `csrr a2,stval' + arch/riscv/cpu/mtrap.S:70: Error: unrecognized opcode `csrw sepc,a0' + + Signed-off-by: Alexandre Ghiti + Reviewed-by: Bin Meng + Tested-by: Heinrich Schuchardt + Tested-by: Heiko Stuebner + Tested-by: Christian Stewart + Reviewed-by: Rick Chen + +diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile +index 0b80eb8d86..53d1194ffb 100644 +--- a/arch/riscv/Makefile ++++ b/arch/riscv/Makefile +@@ -24,7 +24,16 @@ ifeq ($(CONFIG_CMODEL_MEDANY),y) + CMODEL = medany + endif + +-ARCH_FLAGS = -march=$(ARCH_BASE)$(ARCH_A)$(ARCH_C) -mabi=$(ABI) \ ++RISCV_MARCH = $(ARCH_BASE)$(ARCH_A)$(ARCH_C) ++ ++# Newer binutils versions default to ISA spec version 20191213 which moves some ++# instructions from the I extension to the Zicsr and Zifencei extensions. ++toolchain-need-zicsr-zifencei := $(call cc-option-yn, -mabi=$(ABI) -march=$(RISCV_MARCH)_zicsr_zifencei) ++ifeq ($(toolchain-need-zicsr-zifencei),y) ++ RISCV_MARCH := $(RISCV_MARCH)_zicsr_zifencei ++endif ++ ++ARCH_FLAGS = -march=$(RISCV_MARCH) -mabi=$(ABI) \ + -mcmodel=$(CMODEL) + + PLATFORM_CPPFLAGS += $(ARCH_FLAGS) diff --git a/package/boot/uboot-sifiveu/patches/100-mkimage-check-environment-for-dtc-binary-location.patch b/package/boot/uboot-sifiveu/patches/100-mkimage-check-environment-for-dtc-binary-location.patch new file mode 100644 index 00000000000..482aa1a3693 --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/100-mkimage-check-environment-for-dtc-binary-location.patch @@ -0,0 +1,35 @@ +From 637800493945ffed2f454756300437a4ec86e3b1 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Wed, 19 Jul 2017 22:23:15 +0200 +Subject: mkimage: check environment for dtc binary location + +Currently mkimage assumes the dtc binary is in the path and fails +otherwise. This patch makes it check the DTC environment variable first +for the dtc binary and then fall back to the default path. This makes +it possible to call the u-boot build with make DTC=... and build a fit +image with the dtc binary not being the the default path. + +Signed-off-by: Hauke Mehrtens +Cc: Simon Glass +--- + tools/fit_image.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/tools/fit_image.c ++++ b/tools/fit_image.c +@@ -726,9 +726,14 @@ static int fit_handle_file(struct image_ + } + *cmd = '\0'; + } else if (params->datafile) { ++ const char* dtc = getenv("DTC"); ++ ++ if (!dtc) ++ dtc = MKIMAGE_DTC; ++ + /* dtc -I dts -O dtb -p 500 -o tmpfile datafile */ + snprintf(cmd, sizeof(cmd), "%s %s -o \"%s\" \"%s\"", +- MKIMAGE_DTC, params->dtc, tmpfile, params->datafile); ++ dtc, params->dtc, tmpfile, params->datafile); + debug("Trying to execute \"%s\"\n", cmd); + } else { + snprintf(cmd, sizeof(cmd), "cp \"%s\" \"%s\"", diff --git a/package/boot/uboot-sifiveu/patches/110-no-kwbimage.patch b/package/boot/uboot-sifiveu/patches/110-no-kwbimage.patch new file mode 100644 index 00000000000..65d14f5bece --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/110-no-kwbimage.patch @@ -0,0 +1,10 @@ +--- a/tools/Makefile ++++ b/tools/Makefile +@@ -119,7 +119,6 @@ dumpimage-mkimage-objs := aisimage.o \ + imximage.o \ + imx8image.o \ + imx8mimage.o \ +- kwbimage.o \ + lib/md5.o \ + lpc32xximage.o \ + mxsimage.o \ diff --git a/package/boot/uboot-sifiveu/patches/130-fix-mkimage-host-build.patch b/package/boot/uboot-sifiveu/patches/130-fix-mkimage-host-build.patch new file mode 100644 index 00000000000..cd65c1321fc --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/130-fix-mkimage-host-build.patch @@ -0,0 +1,24 @@ +--- a/tools/image-host.c ++++ b/tools/image-host.c +@@ -1125,6 +1125,7 @@ static int fit_config_add_verification_d + * 2) get public key (X509_get_pubkey) + * 3) provide der format (d2i_RSAPublicKey) + */ ++#ifdef CONFIG_TOOLS_LIBCRYPTO + static int read_pub_key(const char *keydir, const void *name, + unsigned char **pubkey, int *pubkey_len) + { +@@ -1178,6 +1179,13 @@ err_cert: + fclose(f); + return ret; + } ++#else ++static int read_pub_key(const char *keydir, const void *name, ++ unsigned char **pubkey, int *pubkey_len) ++{ ++ return -ENOSYS; ++} ++#endif + + int fit_pre_load_data(const char *keydir, void *keydest, void *fit) + { diff --git a/package/boot/uboot-sifiveu/patches/300-force-pylibfdt-build.patch b/package/boot/uboot-sifiveu/patches/300-force-pylibfdt-build.patch new file mode 100644 index 00000000000..4abf13eda86 --- /dev/null +++ b/package/boot/uboot-sifiveu/patches/300-force-pylibfdt-build.patch @@ -0,0 +1,30 @@ +--- a/Makefile ++++ b/Makefile +@@ -2028,26 +2028,7 @@ endif + # Check dtc and pylibfdt, if DTC is provided, else build them + PHONY += scripts_dtc + scripts_dtc: scripts_basic +- $(Q)if test "$(DTC)" = "$(DTC_INTREE)"; then \ +- $(MAKE) $(build)=scripts/dtc; \ +- else \ +- if ! $(DTC) -v >/dev/null; then \ +- echo '*** Failed to check dtc version: $(DTC)'; \ +- false; \ +- else \ +- if test "$(call dtc-version)" -lt $(DTC_MIN_VERSION); then \ +- echo '*** Your dtc is too old, please upgrade to dtc $(DTC_MIN_VERSION) or newer'; \ +- false; \ +- else \ +- if [ -n "$(CONFIG_PYLIBFDT)" ]; then \ +- if ! echo "import libfdt" | $(PYTHON3) 2>/dev/null; then \ +- echo '*** pylibfdt does not seem to be available with $(PYTHON3)'; \ +- false; \ +- fi; \ +- fi; \ +- fi; \ +- fi; \ +- fi ++ $(MAKE) $(build)=scripts/dtc + + # --------------------------------------------------------------------------- + quiet_cmd_cpp_lds = LDS $@ diff --git a/package/boot/uboot-sifiveu/uEnv-default.txt b/package/boot/uboot-sifiveu/uEnv-default.txt new file mode 100644 index 00000000000..8468a3cace0 --- /dev/null +++ b/package/boot/uboot-sifiveu/uEnv-default.txt @@ -0,0 +1,5 @@ +setenv loadkernel fatload mmc 0:3 \$kernel_addr_r Image +setenv loaddtb fatload mmc 0:3 \$fdt_addr_r dtb +setenv bootargs console=ttySIF0,115200 earlycon=sbi root=/dev/mmcblk0p4 rootwait +setenv uenvcmd run loadkernel \&\& run loaddtb \&\& booti \$kernel_addr_r - \$fdt_addr_r +run uenvcmd diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index b3bec858ef8..afdb49b2e99 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -6,9 +6,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware/qca-wireless.git -PKG_SOURCE_DATE:=2023-04-12 -PKG_SOURCE_VERSION:=b22487d729362feaff7d06354353d005a3a9d6b7 -PKG_MIRROR_HASH:=94eea9db636b2cbf6782ec17d8b8fe36770c61ff283702ec418df1a1b09f54ef +PKG_SOURCE_DATE:=2023-05-22 +PKG_SOURCE_VERSION:=0f73d32e641c4f17e64597da0e6c40ed3cbebe69 +PKG_MIRROR_HASH:=70bd8ecda004528ec74de078d00df792f92322c58c4ec4b0630d1da097a8bc89 PKG_FLAGS:=nonshared @@ -29,6 +29,7 @@ endef ALLWIFIBOARDS:= \ buffalo_wxr-5950ax12 \ + compex_wpq873 \ dynalink_dl-wrx36 \ edgecore_eap102 \ edimax_cax1800 \ @@ -113,6 +114,7 @@ endef # Add $(eval $(call generate-ipq-wifi-package,,)) $(eval $(call generate-ipq-wifi-package,buffalo_wxr-5950ax12,Buffalo WXR-5950AX12)) +$(eval $(call generate-ipq-wifi-package,compex_wpq873,Compex WPQ-873)) $(eval $(call generate-ipq-wifi-package,dynalink_dl-wrx36,Dynalink DL-WRX36)) $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102)) $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800)) diff --git a/package/kernel/ath10k-ct/Makefile b/package/kernel/ath10k-ct/Makefile index fdeb14e46c7..2bb7c7e2f72 100644 --- a/package/kernel/ath10k-ct/Makefile +++ b/package/kernel/ath10k-ct/Makefile @@ -1,21 +1,21 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ath10k-ct -PKG_RELEASE:=5 +PKG_RELEASE:=1 PKG_LICENSE:=GPLv2 PKG_LICENSE_FILES:= PKG_SOURCE_URL:=https://github.com/greearb/ath10k-ct.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2022-05-13 -PKG_SOURCE_VERSION:=f808496fcc6b1f68942914117aebf8b3f8d52bb3 -PKG_MIRROR_HASH:=39527b3408696594eda5579521a9fb798417be4dda73fa08e8b3a8c7b0d3f3d9 +PKG_SOURCE_DATE:=2023-05-26 +PKG_SOURCE_VERSION:=af342b6171a457e067932615deb203b68b1d076a +PKG_MIRROR_HASH:=90e145fbe6057da65b637e3a194317c1e4f529b078c7e4a245a286c37e3d1b76 -# Build the 5.15 ath10k-ct driver version. +# Build the 6.2 ath10k-ct driver version. # Probably this should match as closely as # possible to whatever mac80211 backports version is being used. -CT_KVER="-5.15" +CT_KVER="-6.2" PKG_MAINTAINER:=Ben Greear PKG_BUILD_PARALLEL:=1 diff --git a/package/kernel/ath10k-ct/patches/100-api_update.patch b/package/kernel/ath10k-ct/patches/100-api_update.patch deleted file mode 100644 index 5343c29eb9d..00000000000 --- a/package/kernel/ath10k-ct/patches/100-api_update.patch +++ /dev/null @@ -1,618 +0,0 @@ ---- a/ath10k-5.15/mac.c -+++ b/ath10k-5.15/mac.c -@@ -788,7 +788,7 @@ int ath10k_mac_vif_chan(struct ieee80211 - struct ieee80211_chanctx_conf *conf; - - rcu_read_lock(); -- conf = rcu_dereference(vif->chanctx_conf); -+ conf = rcu_dereference(vif->bss_conf.chanctx_conf); - if (!conf) { - rcu_read_unlock(); - return -ENOENT; -@@ -1764,8 +1764,8 @@ static int ath10k_vdev_start_restart(str - arg.channel.chan_radar = - !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); - } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { -- arg.ssid = arvif->vif->bss_conf.ssid; -- arg.ssid_len = arvif->vif->bss_conf.ssid_len; -+ arg.ssid = arvif->vif->cfg.ssid; -+ arg.ssid_len = arvif->vif->cfg.ssid_len; - } - - ath10k_dbg(ar, ATH10K_DBG_MAC, -@@ -1890,7 +1890,7 @@ static int ath10k_mac_setup_bcn_tmpl(str - arvif->vdev_type != WMI_VDEV_TYPE_IBSS) - return 0; - -- bcn = ieee80211_beacon_get_template(hw, vif, &offs); -+ bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); - if (!bcn) { - ath10k_warn(ar, "failed to get beacon template from mac80211\n"); - return -EPERM; -@@ -2083,8 +2083,7 @@ static void ath10k_control_beaconing(str - } - - static void ath10k_control_ibss(struct ath10k_vif *arvif, -- struct ieee80211_bss_conf *info, -- const u8 self_peer[ETH_ALEN]) -+ struct ieee80211_vif *vif) - { - struct ath10k *ar = arvif->ar; - u32 vdev_param; -@@ -2092,7 +2091,7 @@ static void ath10k_control_ibss(struct a - - lockdep_assert_held(&arvif->ar->conf_mutex); - -- if (!info->ibss_joined) { -+ if (!vif->cfg.ibss_joined) { - if (is_zero_ether_addr(arvif->bssid)) - return; - -@@ -2298,7 +2297,7 @@ static void ath10k_mac_vif_ap_csa_count_ - if (arvif->vdev_type != WMI_VDEV_TYPE_AP) - return; - -- if (!vif->csa_active) -+ if (!vif->bss_conf.csa_active) - return; - - if (!arvif->is_up) -@@ -2433,7 +2432,7 @@ static void ath10k_peer_assoc_h_basic(st - lockdep_assert_held(&ar->conf_mutex); - - if (vif->type == NL80211_IFTYPE_STATION) -- aid = vif->bss_conf.aid; -+ aid = vif->cfg.aid; - else - aid = sta->aid; - -@@ -2463,7 +2462,8 @@ static void ath10k_peer_assoc_h_crypto(s - return; - - bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, -- info->ssid_len ? info->ssid : NULL, info->ssid_len, -+ vif->cfg.ssid_len ? vif->cfg.ssid : NULL, -+ vif->cfg.ssid_len, - IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); - if (bss) { - const struct cfg80211_bss_ies *ies; -@@ -2521,7 +2521,7 @@ static void ath10k_peer_assoc_h_rates(st - - band = def.chan->band; - sband = ar->hw->wiphy->bands[band]; -- ratemask = sta->supp_rates[band]; -+ ratemask = sta->deflink.supp_rates[band]; - ratemask &= arvif->bitrate_mask.control[band].legacy; - rates = sband->bitrates; - -@@ -2770,7 +2770,7 @@ static void ath10k_peer_assoc_h_ht(struc - struct ieee80211_sta *sta, - struct wmi_peer_assoc_complete_arg *arg) - { -- const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; -+ const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; - struct ath10k_vif *arvif = (void *)vif->drv_priv; - struct cfg80211_chan_def def; - enum nl80211_band band; -@@ -2814,7 +2814,7 @@ static void ath10k_peer_assoc_h_ht(struc - if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) - arg->peer_flags |= ar->wmi.peer_flags->ldbc; - -- if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { -+ if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) { - arg->peer_flags |= ar->wmi.peer_flags->bw40; - arg->peer_rate_caps |= WMI_RC_CW40_FLAG; - } -@@ -2883,7 +2883,7 @@ static void ath10k_peer_assoc_h_ht(struc - arg->peer_ht_rates.rates[i] = i; - } else { - arg->peer_ht_rates.num_rates = n; -- arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss); -+ arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss); - } - - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", -@@ -3045,7 +3045,7 @@ static void ath10k_peer_assoc_h_vht(stru - struct ieee80211_sta *sta, - struct wmi_peer_assoc_complete_arg *arg) - { -- const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; -+ const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; - struct ath10k_vif *arvif = (void *)vif->drv_priv; - struct ath10k_hw_params *hw = &ar->hw_params; - struct cfg80211_chan_def def; -@@ -3087,10 +3087,10 @@ static void ath10k_peer_assoc_h_vht(stru - (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + - ampdu_factor)) - 1); - -- if (sta->bandwidth == IEEE80211_STA_RX_BW_80) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) - arg->peer_flags |= ar->wmi.peer_flags->bw80; - -- if (sta->bandwidth == IEEE80211_STA_RX_BW_160) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) - arg->peer_flags |= ar->wmi.peer_flags->bw160; - - /* Calculate peer NSS capability from VHT capabilities if STA -@@ -3104,7 +3104,7 @@ static void ath10k_peer_assoc_h_vht(stru - vht_mcs_mask[i]) - max_nss = i + 1; - } -- arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss); -+ arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss); - arg->peer_vht_rates.rx_max_rate = - __le16_to_cpu(vht_cap->vht_mcs.rx_highest); - arg->peer_vht_rates.rx_mcs_set = -@@ -3266,7 +3266,7 @@ static bool ath10k_mac_sta_has_ofdm_only - { - struct ath10k_vif *arvif = (void *)vif->drv_priv; - u32 msk = arvif->bitrate_mask.control[NL80211_BAND_2GHZ].legacy & -- sta->supp_rates[NL80211_BAND_2GHZ]; -+ sta->deflink.supp_rates[NL80211_BAND_2GHZ]; - /* We have 12 bits of legacy rates, first 4 are /b (CCK) rates. */ - return (msk & 0xff0) && !(msk & 0xf); - } -@@ -3276,7 +3276,7 @@ static bool ath10k_mac_sta_has_ofdm_and_ - { - struct ath10k_vif *arvif = (void *)vif->drv_priv; - u32 msk = arvif->bitrate_mask.control[NL80211_BAND_2GHZ].legacy & -- sta->supp_rates[NL80211_BAND_2GHZ]; -+ sta->deflink.supp_rates[NL80211_BAND_2GHZ]; - /* We have 12 bits of legacy rates, first 4 are /b (CCK) rates. */ - return ((msk & 0xf) && (msk & 0xff0)); - } -@@ -3284,8 +3284,10 @@ static bool ath10k_mac_sta_has_ofdm_and_ - static enum wmi_phy_mode ath10k_mac_get_phymode_vht(struct ath10k *ar, - struct ieee80211_sta *sta) - { -- if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { -- switch (sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { -+ struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -+ -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) { -+ switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { - case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: - return MODE_11AC_VHT160; - case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: -@@ -3296,13 +3298,13 @@ static enum wmi_phy_mode ath10k_mac_get_ - } - } - -- if (sta->bandwidth == IEEE80211_STA_RX_BW_80) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) - return MODE_11AC_VHT80; - -- if (sta->bandwidth == IEEE80211_STA_RX_BW_40) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) - return MODE_11AC_VHT40; - -- if (sta->bandwidth == IEEE80211_STA_RX_BW_20) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) - return MODE_11AC_VHT20; - - return MODE_UNKNOWN; -@@ -3329,15 +3331,15 @@ static void ath10k_peer_assoc_h_phymode( - - switch (band) { - case NL80211_BAND_2GHZ: -- if (sta->vht_cap.vht_supported && -+ if (sta->deflink.vht_cap.vht_supported && - !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) { -- if (sta->bandwidth == IEEE80211_STA_RX_BW_40) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) - phymode = MODE_11AC_VHT40; - else - phymode = MODE_11AC_VHT20; -- } else if (sta->ht_cap.ht_supported && -+ } else if (sta->deflink.ht_cap.ht_supported && - !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) { -- if (sta->bandwidth == IEEE80211_STA_RX_BW_40) -+ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) - phymode = MODE_11NG_HT40; - else - phymode = MODE_11NG_HT20; -@@ -3354,12 +3356,12 @@ static void ath10k_peer_assoc_h_phymode( - /* - * Check VHT first. - */ -- if (sta->vht_cap.vht_supported && -+ if (sta->deflink.vht_cap.vht_supported && - !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) { - phymode = ath10k_mac_get_phymode_vht(ar, sta); -- } else if (sta->ht_cap.ht_supported && -+ } else if (sta->deflink.ht_cap.ht_supported && - !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) { -- if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) -+ if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) - phymode = MODE_11NA_HT40; - else - phymode = MODE_11NA_HT20; -@@ -3373,8 +3375,8 @@ static void ath10k_peer_assoc_h_phymode( - } - - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM phymode %s legacy-supp-rates: 0x%x arvif-legacy-rates: 0x%x vht-supp: %d\n", -- sta->addr, ath10k_wmi_phymode_str(phymode), sta->supp_rates[band], -- arvif->bitrate_mask.control[band].legacy, sta->vht_cap.vht_supported); -+ sta->addr, ath10k_wmi_phymode_str(phymode), sta->deflink.supp_rates[band], -+ arvif->bitrate_mask.control[band].legacy, sta->deflink.vht_cap.vht_supported); - - arg->peer_phymode = phymode; - WARN_ON(phymode == MODE_UNKNOWN); -@@ -3677,8 +3679,8 @@ static void ath10k_bss_assoc(struct ieee - /* ap_sta must be accessed only within rcu section which must be left - * before calling ath10k_setup_peer_smps() which might sleep. - */ -- ht_cap = ap_sta->ht_cap; -- vht_cap = ap_sta->vht_cap; -+ ht_cap = ap_sta->deflink.ht_cap; -+ vht_cap = ap_sta->deflink.vht_cap; - - ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg); - if (ret) { -@@ -3713,11 +3715,11 @@ static void ath10k_bss_assoc(struct ieee - - ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac vdev %d up (associated) bssid %pM aid %d bandwidth %d\n", -- arvif->vdev_id, bss_conf->bssid, bss_conf->aid, ap_sta->bandwidth); -+ arvif->vdev_id, bss_conf->bssid, vif->cfg.aid, ap_sta->deflink.bandwidth); - - WARN_ON(arvif->is_up); - -- arvif->aid = bss_conf->aid; -+ arvif->aid = vif->cfg.aid; - ether_addr_copy(arvif->bssid, bss_conf->bssid); - - ret = ath10k_wmi_pdev_set_param(ar, -@@ -4022,7 +4024,7 @@ static int ath10k_station_assoc(struct a - */ - if (!reassoc) { - ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, -- &sta->ht_cap); -+ &sta->deflink.ht_cap); - if (ret) { - ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n", - arvif->vdev_id, ret); -@@ -6916,7 +6918,7 @@ static void ath10k_recalculate_mgmt_rate - static void ath10k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, -- u32 changed) -+ u64 changed) - { - struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = (void *)vif->drv_priv; -@@ -6930,7 +6932,7 @@ static void ath10k_bss_info_changed(stru - mutex_lock(&ar->conf_mutex); - - if (changed & BSS_CHANGED_IBSS) -- ath10k_control_ibss(arvif, info, vif->addr); -+ ath10k_control_ibss(arvif, vif); - - if (changed & BSS_CHANGED_BEACON_INT) { - arvif->beacon_interval = info->beacon_int; -@@ -6995,9 +6997,9 @@ static void ath10k_bss_info_changed(stru - - if (changed & BSS_CHANGED_SSID && - vif->type == NL80211_IFTYPE_AP) { -- arvif->u.ap.ssid_len = info->ssid_len; -- if (info->ssid_len) -- memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len); -+ arvif->u.ap.ssid_len = vif->cfg.ssid_len; -+ if (vif->cfg.ssid_len) -+ memcpy(arvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len); - arvif->u.ap.hidden_ssid = info->hidden_ssid; - } - -@@ -7074,7 +7076,7 @@ static void ath10k_bss_info_changed(stru - } - - if (changed & BSS_CHANGED_ASSOC) { -- if (info->assoc) { -+ if (vif->cfg.assoc) { - /* Workaround: Make sure monitor vdev is not running - * when associating to prevent some firmware revisions - * (e.g. 10.1 and 10.2) from crashing. -@@ -7099,7 +7101,7 @@ static void ath10k_bss_info_changed(stru - } - - if (changed & BSS_CHANGED_PS) { -- arvif->ps = vif->bss_conf.ps; -+ arvif->ps = vif->cfg.ps; - - ret = ath10k_config_ps(ar); - if (ret) -@@ -7699,7 +7701,7 @@ static void ath10k_sta_rc_update_wk(stru - - if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { - ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM supp rates, bandwidth: %d\n", -- sta->addr, sta->bandwidth); -+ sta->addr, sta->deflink.bandwidth); - - err = ath10k_station_assoc(ar, arvif->vif, sta, true); - if (err) -@@ -7751,10 +7753,10 @@ static int ath10k_sta_set_txpwr(struct i - int ret = 0; - s16 txpwr; - -- if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC) { -+ if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) { - txpwr = 0; - } else { -- txpwr = sta->txpwr.power; -+ txpwr = sta->deflink.txpwr.power; - if (!txpwr) - return -EINVAL; - } -@@ -7874,26 +7876,29 @@ static int ath10k_mac_validate_rate_mask - struct ieee80211_sta *sta, - u32 rate_ctrl_flag, u8 nss) - { -- if (nss > sta->rx_nss) { -+ struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; -+ struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -+ -+ if (nss > sta->deflink.rx_nss) { - ath10k_warn(ar, "Invalid nss field, configured %u limit %u\n", -- nss, sta->rx_nss); -+ nss, sta->deflink.rx_nss); - return -EINVAL; - } - - if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_VHT) { -- if (!sta->vht_cap.vht_supported) { -+ if (!vht_cap->vht_supported) { - ath10k_warn(ar, "Invalid VHT rate for sta %pM\n", - sta->addr); - return -EINVAL; - } - } else if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HT) { -- if (!sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { -+ if (!ht_cap->ht_supported || vht_cap->vht_supported) { - ath10k_warn(ar, "Invalid HT rate for sta %pM\n", - sta->addr); - return -EINVAL; - } - } else { -- if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) -+ if (ht_cap->ht_supported || vht_cap->vht_supported) - return -EINVAL; - } - -@@ -8567,7 +8572,7 @@ static int ath10k_sta_state(struct ieee8 - * New association. - */ - ath10k_dbg(ar, ATH10K_DBG_STA, "mac sta %pM associated, bandwidth: %d\n", -- sta->addr, sta->bandwidth); -+ sta->addr, sta->deflink.bandwidth); - - ret = ath10k_station_assoc(ar, vif, sta, false); - if (ret) -@@ -8580,7 +8585,7 @@ static int ath10k_sta_state(struct ieee8 - * Tdls station authorized. - */ - ath10k_dbg(ar, ATH10K_DBG_STA, "mac tdls sta %pM authorized, bandwidth: %d\n", -- sta->addr, sta->bandwidth); -+ sta->addr, sta->deflink.bandwidth); - - ret = ath10k_station_assoc(ar, vif, sta, false); - if (ret) { -@@ -8721,8 +8726,8 @@ exit: - return ret; - } - --static int ath10k_conf_tx(struct ieee80211_hw *hw, -- struct ieee80211_vif *vif, u16 ac, -+static int ath10k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, -+ unsigned int link_id, u16 ac, - const struct ieee80211_tx_queue_params *params) - { - struct ath10k *ar = hw->priv; -@@ -9308,7 +9313,7 @@ static bool ath10k_mac_set_vht_bitrate_m - u8 rate = arvif->vht_pfr; - - /* skip non vht and multiple rate peers */ -- if (!sta->vht_cap.vht_supported || arvif->vht_num_rates != 1) -+ if (!sta->deflink.vht_cap.vht_supported || arvif->vht_num_rates != 1) - return false; - - err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, -@@ -9349,7 +9354,7 @@ static void ath10k_mac_clr_bitrate_mask_ - int err; - - /* clear vht peers only */ -- if (arsta->arvif != arvif || !sta->vht_cap.vht_supported) -+ if (arsta->arvif != arvif || !sta->deflink.vht_cap.vht_supported) - return; - - err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, -@@ -9534,13 +9539,13 @@ static void ath10k_sta_rc_update(struct - - ath10k_dbg(ar, ATH10K_DBG_STA, - "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", -- sta->addr, changed, sta->bandwidth, sta->rx_nss, -- sta->smps_mode); -+ sta->addr, changed, sta->deflink.bandwidth, sta->deflink.rx_nss, -+ sta->deflink.smps_mode); - - if (changed & IEEE80211_RC_BW_CHANGED) { - bw = WMI_PEER_CHWIDTH_20MHZ; - -- switch (sta->bandwidth) { -+ switch (sta->deflink.bandwidth) { - case IEEE80211_STA_RX_BW_20: - bw = WMI_PEER_CHWIDTH_20MHZ; - break; -@@ -9555,7 +9560,7 @@ static void ath10k_sta_rc_update(struct - break; - default: - ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n", -- sta->bandwidth, sta->addr); -+ sta->deflink.bandwidth, sta->addr); - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - } -@@ -9564,12 +9569,12 @@ static void ath10k_sta_rc_update(struct - } - - if (changed & IEEE80211_RC_NSS_CHANGED) -- arsta->nss = sta->rx_nss; -+ arsta->nss = sta->deflink.rx_nss; - - if (changed & IEEE80211_RC_SMPS_CHANGED) { - smps = WMI_PEER_SMPS_PS_NONE; - -- switch (sta->smps_mode) { -+ switch (sta->deflink.smps_mode) { - case IEEE80211_SMPS_AUTOMATIC: - case IEEE80211_SMPS_OFF: - smps = WMI_PEER_SMPS_PS_NONE; -@@ -9582,7 +9587,7 @@ static void ath10k_sta_rc_update(struct - break; - case IEEE80211_SMPS_NUM_MODES: - ath10k_warn(ar, "Invalid smps %d in sta rc update for %pM\n", -- sta->smps_mode, sta->addr); -+ sta->deflink.smps_mode, sta->addr); - smps = WMI_PEER_SMPS_PS_NONE; - break; - } -@@ -9896,7 +9901,7 @@ ath10k_mac_change_chanctx_cnt_iter(void - { - struct ath10k_mac_change_chanctx_arg *arg = data; - -- if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) -+ if (rcu_access_pointer(vif->bss_conf.chanctx_conf) != arg->ctx) - return; - - arg->n_vifs++; -@@ -9909,7 +9914,7 @@ ath10k_mac_change_chanctx_fill_iter(void - struct ath10k_mac_change_chanctx_arg *arg = data; - struct ieee80211_chanctx_conf *ctx; - -- ctx = rcu_access_pointer(vif->chanctx_conf); -+ ctx = rcu_access_pointer(vif->bss_conf.chanctx_conf); - if (ctx != arg->ctx) - return; - -@@ -9982,6 +9987,7 @@ unlock: - static int - ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, -+ struct ieee80211_bss_conf *link_conf, - struct ieee80211_chanctx_conf *ctx) - { - struct ath10k *ar = hw->priv; -@@ -10061,6 +10067,7 @@ err: - static void - ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, -+ struct ieee80211_bss_conf *link_conf, - struct ieee80211_chanctx_conf *ctx) - { - struct ath10k *ar = hw->priv; ---- a/ath10k-5.15/txrx.c -+++ b/ath10k-5.15/txrx.c -@@ -260,7 +260,7 @@ int ath10k_txrx_tx_unref(struct ath10k_h - nf = ar->debug.nf_sum[0]; - #endif - info->status.ack_signal = nf + tx_done->ack_rssi; -- info->status.is_valid_ack_signal = true; -+ info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; - } - - if (tx_done->tx_rate_code || tx_done->tx_rate_flags || ar->ok_tx_rate_status) { ---- a/ath10k-5.15/wmi.c -+++ b/ath10k-5.15/wmi.c -@@ -2587,7 +2587,7 @@ wmi_process_mgmt_tx_comp(struct ath10k * - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ath10k_get_noisefloor(0, ar) + - param->ack_rssi; -- info->status.is_valid_ack_signal = true; -+ info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; - } - - ieee80211_tx_status_irqsafe(ar->hw, msdu); -@@ -4258,13 +4258,13 @@ void ath10k_wmi_event_host_swba(struct a - * Once CSA counter is completed stop sending beacons until - * actual channel switch is done - */ -- if (arvif->vif->csa_active && -+ if (arvif->vif->bss_conf.csa_active && - ieee80211_beacon_cntdwn_is_complete(arvif->vif)) { - ieee80211_csa_finish(arvif->vif); - continue; - } - -- bcn = ieee80211_beacon_get(ar->hw, arvif->vif); -+ bcn = ieee80211_beacon_get(ar->hw, arvif->vif, 0); - if (!bcn) { - ath10k_warn(ar, "could not get mac80211 beacon, vdev_id: %i addr: %pM\n", - arvif->vdev_id, arvif->vif->addr); ---- a/ath10k-5.15/htt_rx.c -+++ b/ath10k-5.15/htt_rx.c -@@ -4017,7 +4017,7 @@ ath10k_update_per_peer_tx_stats(struct a - switch (txrate.flags) { - case WMI_RATE_PREAMBLE_OFDM: - if (arsta->arvif && arsta->arvif->vif) -- conf = rcu_dereference(arsta->arvif->vif->chanctx_conf); -+ conf = rcu_dereference(arsta->arvif->vif->bss_conf.chanctx_conf); - if (conf && conf->def.chan->band == NL80211_BAND_5GHZ) - arsta->tx_info.status.rates[0].idx = rate_idx - 4; - break; ---- a/ath10k-5.15/wmi-tlv.c -+++ b/ath10k-5.15/wmi-tlv.c -@@ -205,7 +205,7 @@ static int ath10k_wmi_tlv_event_bcn_tx_s - } - - arvif = ath10k_get_arvif(ar, vdev_id); -- if (arvif && arvif->is_up && arvif->vif->csa_active) -+ if (arvif && arvif->is_up && arvif->vif->bss_conf.csa_active) - ieee80211_queue_work(ar->hw, &arvif->ap_csa_work); - - kfree(tb); ---- a/ath10k-5.15/core.c -+++ b/ath10k-5.15/core.c -@@ -4081,7 +4081,7 @@ static int ath10k_core_probe_fw(struct a - ath10k_debug_print_board_info(ar); - } - -- device_get_mac_address(ar->dev, ar->mac_addr, sizeof(ar->mac_addr)); -+ device_get_mac_address(ar->dev, ar->mac_addr); - - /* Try to get mac address from device node (from nvmem cell) */ - of_get_mac_address(ar->dev->of_node, ar->mac_addr); ---- a/ath10k-5.15/pci.c -+++ b/ath10k-5.15/pci.c -@@ -3547,8 +3547,7 @@ static void ath10k_pci_free_irq(struct a - - void ath10k_pci_init_napi(struct ath10k *ar) - { -- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll, -- ATH10K_NAPI_BUDGET); -+ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll); - } - - static int ath10k_pci_init_irq(struct ath10k *ar) ---- a/ath10k-5.15/sdio.c -+++ b/ath10k-5.15/sdio.c -@@ -2531,8 +2531,7 @@ static int ath10k_sdio_probe(struct sdio - return -ENOMEM; - } - -- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll, -- ATH10K_NAPI_BUDGET); -+ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll); - - ath10k_dbg(ar, ATH10K_DBG_BOOT, - "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", ---- a/ath10k-5.15/snoc.c -+++ b/ath10k-5.15/snoc.c -@@ -1242,8 +1242,7 @@ static int ath10k_snoc_napi_poll(struct - - static void ath10k_snoc_init_napi(struct ath10k *ar) - { -- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, -- ATH10K_NAPI_BUDGET); -+ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll); - } - - static int ath10k_snoc_request_irq(struct ath10k *ar) diff --git a/package/kernel/ath10k-ct/patches/120-ath10k-fetch-calibration-data-via-nvmem-subsystem.patch b/package/kernel/ath10k-ct/patches/120-ath10k-fetch-calibration-data-via-nvmem-subsystem.patch deleted file mode 100644 index c935d8575ee..00000000000 --- a/package/kernel/ath10k-ct/patches/120-ath10k-fetch-calibration-data-via-nvmem-subsystem.patch +++ /dev/null @@ -1,162 +0,0 @@ -From e2333703373e8b81294da5d1c73c30154f75b082 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Fri, 15 Oct 2021 18:56:33 +0200 -Subject: [PATCH] ath10k: fetch (pre-)calibration data via nvmem subsystem - -On most embedded ath10k devices (like range extenders, -routers, accesspoints, ...) the calibration data is -stored in a easily accessible MTD partitions named -"ART", "caldata", "calibration", etc... - -Since commit 4b361cfa8624 ("mtd: core: add OTP nvmem provider support"): -MTD partitions and portions of them can be specified -as potential nvmem-cells which are accessible through -the nvmem subsystem. - -This feature - together with an nvmem cell definition either -in the platform data or via device-tree allows drivers to get -the (pre-)calibration data which is required for initializing -the WIFI. - -Tested with Netgear EX6150v2 (IPQ4018) - -Cc: Robert Marko -Cc: Thibaut Varene -Signed-off-by: Christian Lamparter ---- ---- a/ath10k-5.15/core.c -+++ b/ath10k-5.15/core.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -1005,7 +1006,8 @@ static int ath10k_core_get_board_id_from - } - - if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT || -- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE) -+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE || -+ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM) - bmi_board_id_param = BMI_PARAM_GET_FLASH_BOARD_ID; - else - bmi_board_id_param = BMI_PARAM_GET_EEPROM_BOARD_ID; -@@ -2115,7 +2117,8 @@ static int ath10k_download_and_run_otp(s - - /* As of now pre-cal is valid for 10_4 variants */ - if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT || -- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE) -+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE || -+ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM) - bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL; - - ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result); -@@ -2249,6 +2252,39 @@ struct ath10k_bss_rom_ie { - __le32 rom_len; - } __packed; - -+static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name) -+{ -+ struct nvmem_cell *cell; -+ void *buf; -+ size_t len; -+ int ret; -+ -+ cell = devm_nvmem_cell_get(ar->dev, cell_name); -+ if (IS_ERR(cell)) { -+ ret = PTR_ERR(cell); -+ return ret; -+ } -+ -+ buf = nvmem_cell_read(cell, &len); -+ if (IS_ERR(buf)) -+ return PTR_ERR(buf); -+ -+ if (ar->hw_params.cal_data_len != len) { -+ kfree(buf); -+ ath10k_warn(ar, "invalid calibration data length in nvmem-cell '%s': %zu != %u\n", -+ cell_name, len, ar->hw_params.cal_data_len); -+ return -EMSGSIZE; -+ } -+ -+ ret = ath10k_download_board_data(ar, buf, len); -+ kfree(buf); -+ if (ret) -+ ath10k_warn(ar, "failed to download calibration data from nvmem-cell '%s': %d\n", -+ cell_name, ret); -+ -+ return ret; -+} -+ - int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name, - struct ath10k_fw_file *fw_file) - { -@@ -2625,6 +2661,18 @@ static int ath10k_core_pre_cal_download( - { - int ret; - -+ ret = ath10k_download_cal_nvmem(ar, "pre-calibration"); -+ if (ret == 0) { -+ ar->cal_mode = ATH10K_PRE_CAL_MODE_NVMEM; -+ goto success; -+ } else if (ret == -EPROBE_DEFER) { -+ return ret; -+ } -+ -+ ath10k_dbg(ar, ATH10K_DBG_BOOT, -+ "boot did not find a pre-calibration nvmem-cell, try file next: %d\n", -+ ret); -+ - ret = ath10k_download_cal_file(ar, ar->pre_cal_file); - if (ret == 0) { - ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE; -@@ -2691,6 +2739,18 @@ static int ath10k_download_cal_data(stru - "pre cal download procedure failed, try cal file: %d\n", - ret); - -+ ret = ath10k_download_cal_nvmem(ar, "calibration"); -+ if (ret == 0) { -+ ar->cal_mode = ATH10K_CAL_MODE_NVMEM; -+ goto done; -+ } else if (ret == -EPROBE_DEFER) { -+ return ret; -+ } -+ -+ ath10k_dbg(ar, ATH10K_DBG_BOOT, -+ "boot did not find a calibration nvmem-cell, try file next: %d\n", -+ ret); -+ - ret = ath10k_download_cal_file(ar, ar->cal_file); - if (ret == 0) { - ar->cal_mode = ATH10K_CAL_MODE_FILE; ---- a/ath10k-5.15/core.h -+++ b/ath10k-5.15/core.h -@@ -1109,8 +1109,10 @@ enum ath10k_cal_mode { - ATH10K_CAL_MODE_FILE, - ATH10K_CAL_MODE_OTP, - ATH10K_CAL_MODE_DT, -+ ATH10K_CAL_MODE_NVMEM, - ATH10K_PRE_CAL_MODE_FILE, - ATH10K_PRE_CAL_MODE_DT, -+ ATH10K_PRE_CAL_MODE_NVMEM, - ATH10K_CAL_MODE_EEPROM, - }; - -@@ -1130,10 +1132,14 @@ static inline const char *ath10k_cal_mod - return "otp"; - case ATH10K_CAL_MODE_DT: - return "dt"; -+ case ATH10K_CAL_MODE_NVMEM: -+ return "nvmem"; - case ATH10K_PRE_CAL_MODE_FILE: - return "pre-cal-file"; - case ATH10K_PRE_CAL_MODE_DT: - return "pre-cal-dt"; -+ case ATH10K_PRE_CAL_MODE_NVMEM: -+ return "pre-cal-nvmem"; - case ATH10K_CAL_MODE_EEPROM: - return "eeprom"; - } diff --git a/package/kernel/ath10k-ct/patches/130-ath10k-read-qcom-coexist-support-as-a-u32.patch b/package/kernel/ath10k-ct/patches/130-ath10k-read-qcom-coexist-support-as-a-u32.patch index c835a33f492..77e2724991e 100644 --- a/package/kernel/ath10k-ct/patches/130-ath10k-read-qcom-coexist-support-as-a-u32.patch +++ b/package/kernel/ath10k-ct/patches/130-ath10k-read-qcom-coexist-support-as-a-u32.patch @@ -39,9 +39,9 @@ that the feature is properly initialized: Signed-off-by: Vincent Tremblay ---- a/ath10k-5.15/core.c -+++ b/ath10k-5.15/core.c -@@ -2798,14 +2798,14 @@ done: +--- a/ath10k-6.2/core.c ++++ b/ath10k-6.2/core.c +@@ -2869,14 +2869,14 @@ done: static void ath10k_core_fetch_btcoex_dt(struct ath10k *ar) { struct device_node *node; diff --git a/package/kernel/ath10k-ct/patches/201-ath10k-add-LED-and-GPIO-controlling-support-for-various-chipsets.patch b/package/kernel/ath10k-ct/patches/201-ath10k-add-LED-and-GPIO-controlling-support-for-various-chipsets.patch index 2cc7c1e678b..844c089e888 100644 --- a/package/kernel/ath10k-ct/patches/201-ath10k-add-LED-and-GPIO-controlling-support-for-various-chipsets.patch +++ b/package/kernel/ath10k-ct/patches/201-ath10k-add-LED-and-GPIO-controlling-support-for-various-chipsets.patch @@ -66,25 +66,25 @@ v13: * cleanup includes - ath10k-5.15/Kconfig | 10 +++ - ath10k-5.15/Makefile | 1 + - ath10k-5.15/core.c | 22 +++++++ - ath10k-5.15/core.h | 9 ++- - ath10k-5.15/hw.h | 1 + - ath10k-5.15/leds.c | 103 ++++++++++++++++++++++++++++++ - ath10k-5.15/leds.h | 45 +++++++++++++ - ath10k-5.15/mac.c | 1 + - ath10k-5.15/wmi-ops.h | 32 ++++++++++ - ath10k-5.15/wmi-tlv.c | 2 + - ath10k-5.15/wmi.c | 54 ++++++++++++++++ - ath10k-5.15/wmi.h | 35 ++++++++++ + ath10k-6.2/Kconfig | 10 +++ + ath10k-6.2/Makefile | 1 + + ath10k-6.2/core.c | 22 +++++++ + ath10k-6.2/core.h | 9 ++- + ath10k-6.2/hw.h | 1 + + ath10k-6.2/leds.c | 103 ++++++++++++++++++++++++++++++ + ath10k-6.2/leds.h | 45 +++++++++++++ + ath10k-6.2/mac.c | 1 + + ath10k-6.2/wmi-ops.h | 32 ++++++++++ + ath10k-6.2/wmi-tlv.c | 2 + + ath10k-6.2/wmi.c | 54 ++++++++++++++++ + ath10k-6.2/wmi.h | 35 ++++++++++ 12 files changed, 314 insertions(+), 1 deletion(-) - create mode 100644 ath10k-5.15/leds.c - create mode 100644 ath10k-5.15/leds.h + create mode 100644 ath10k-6.2/leds.c + create mode 100644 ath10k-6.2/leds.h ---- a/ath10k-5.15/Kconfig -+++ b/ath10k-5.15/Kconfig -@@ -66,6 +66,16 @@ config ATH10K_DEBUGFS +--- a/ath10k-6.2/Kconfig ++++ b/ath10k-6.2/Kconfig +@@ -67,6 +67,16 @@ config ATH10K_DEBUGFS If unsure, say Y to make it easier to debug problems. @@ -101,8 +101,8 @@ v13: config ATH10K_SPECTRAL bool "Atheros ath10k spectral scan support" depends on ATH10K_DEBUGFS ---- a/ath10k-5.15/Makefile -+++ b/ath10k-5.15/Makefile +--- a/ath10k-6.2/Makefile ++++ b/ath10k-6.2/Makefile @@ -20,6 +20,7 @@ ath10k_core-$(CONFIG_ATH10K_SPECTRAL) += ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o @@ -111,8 +111,8 @@ v13: ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o ath10k_core-$(CONFIG_PM) += wow.o ath10k_core-$(CONFIG_ATH10K_CE) += ce.o ---- a/ath10k-5.15/core.c -+++ b/ath10k-5.15/core.c +--- a/ath10k-6.2/core.c ++++ b/ath10k-6.2/core.c @@ -28,6 +28,7 @@ #include "testmode.h" #include "wmi-ops.h" @@ -121,7 +121,7 @@ v13: /* Disable ath10k-ct DBGLOG output by default */ unsigned int ath10k_debug_mask = ATH10K_DBG_NO_DBGLOG; -@@ -70,6 +71,7 @@ static const struct ath10k_hw_params ath +@@ -78,6 +79,7 @@ static const struct ath10k_hw_params ath .dev_id = QCA988X_2_0_DEVICE_ID, .bus = ATH10K_BUS_PCI, .name = "qca988x hw2.0", @@ -129,7 +129,7 @@ v13: .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL, -@@ -143,6 +145,7 @@ static const struct ath10k_hw_params ath +@@ -159,6 +161,7 @@ static const struct ath10k_hw_params ath .dev_id = QCA9887_1_0_DEVICE_ID, .bus = ATH10K_BUS_PCI, .name = "qca9887 hw1.0", @@ -137,7 +137,7 @@ v13: .patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL, -@@ -360,6 +363,7 @@ static const struct ath10k_hw_params ath +@@ -400,6 +403,7 @@ static const struct ath10k_hw_params ath .dev_id = QCA99X0_2_0_DEVICE_ID, .bus = ATH10K_BUS_PCI, .name = "qca99x0 hw2.0", @@ -145,7 +145,7 @@ v13: .patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, .otp_exe_param = 0x00000700, -@@ -402,6 +406,7 @@ static const struct ath10k_hw_params ath +@@ -446,6 +450,7 @@ static const struct ath10k_hw_params ath .dev_id = QCA9984_1_0_DEVICE_ID, .bus = ATH10K_BUS_PCI, .name = "qca9984/qca9994 hw1.0", @@ -153,7 +153,7 @@ v13: .patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, -@@ -451,6 +456,7 @@ static const struct ath10k_hw_params ath +@@ -499,6 +504,7 @@ static const struct ath10k_hw_params ath .dev_id = QCA9888_2_0_DEVICE_ID, .bus = ATH10K_BUS_PCI, .name = "qca9888 hw2.0", @@ -161,7 +161,7 @@ v13: .patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, -@@ -3977,6 +3983,10 @@ int ath10k_core_start(struct ath10k *ar, +@@ -4080,6 +4086,10 @@ int ath10k_core_start(struct ath10k *ar, ath10k_wmi_check_apply_board_power_ctl_table(ar); } @@ -172,7 +172,7 @@ v13: return 0; err_hif_stop: -@@ -4238,9 +4248,18 @@ static void ath10k_core_register_work(st +@@ -4341,9 +4351,18 @@ static void ath10k_core_register_work(st goto err_spectral_destroy; } @@ -191,7 +191,7 @@ v13: err_spectral_destroy: ath10k_spectral_destroy(ar); err_debug_destroy: -@@ -4300,6 +4319,8 @@ void ath10k_core_unregister(struct ath10 +@@ -4403,6 +4422,8 @@ void ath10k_core_unregister(struct ath10 if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return; @@ -200,8 +200,8 @@ v13: ath10k_thermal_unregister(ar); /* Stop spectral before unregistering from mac80211 to remove the * relayfs debugfs file cleanly. Otherwise the parent debugfs tree ---- a/ath10k-5.15/core.h -+++ b/ath10k-5.15/core.h +--- a/ath10k-6.2/core.h ++++ b/ath10k-6.2/core.h @@ -14,6 +14,7 @@ #include #include @@ -210,7 +210,7 @@ v13: #include "htt.h" #include "htc.h" -@@ -1577,6 +1578,13 @@ struct ath10k { +@@ -1586,6 +1587,13 @@ struct ath10k { } testmode; struct { @@ -224,9 +224,9 @@ v13: /* protected by data_lock */ u32 rx_crc_err_drop; u32 fw_crash_counter; ---- a/ath10k-5.15/hw.h -+++ b/ath10k-5.15/hw.h -@@ -521,6 +521,7 @@ struct ath10k_hw_params { +--- a/ath10k-6.2/hw.h ++++ b/ath10k-6.2/hw.h +@@ -523,6 +523,7 @@ struct ath10k_hw_params { const char *name; u32 patch_load_addr; int uart_pin; @@ -235,7 +235,7 @@ v13: /* Type of hw cycle counter wraparound logic, for more info --- /dev/null -+++ b/ath10k-5.15/leds.c ++++ b/ath10k-6.2/leds.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. @@ -341,7 +341,7 @@ v13: +} + --- /dev/null -+++ b/ath10k-5.15/leds.h ++++ b/ath10k-6.2/leds.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. @@ -384,8 +384,8 @@ v13: + +#endif +#endif /* _LEDS_H_ */ ---- a/ath10k-5.15/mac.c -+++ b/ath10k-5.15/mac.c +--- a/ath10k-6.2/mac.c ++++ b/ath10k-6.2/mac.c @@ -25,6 +25,7 @@ #include "wmi-tlv.h" #include "wmi-ops.h" @@ -394,8 +394,8 @@ v13: /*********/ /* Rates */ ---- a/ath10k-5.15/wmi-ops.h -+++ b/ath10k-5.15/wmi-ops.h +--- a/ath10k-6.2/wmi-ops.h ++++ b/ath10k-6.2/wmi-ops.h @@ -228,7 +228,10 @@ struct wmi_ops { const struct wmi_bb_timing_cfg_arg *arg); struct sk_buff *(*gen_per_peer_per_tid_cfg)(struct ath10k *ar, @@ -443,9 +443,9 @@ v13: static inline int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u64 module_enable, u32 log_level) { ---- a/ath10k-5.15/wmi-tlv.c -+++ b/ath10k-5.15/wmi-tlv.c -@@ -4594,6 +4594,8 @@ static const struct wmi_ops wmi_tlv_ops +--- a/ath10k-6.2/wmi-tlv.c ++++ b/ath10k-6.2/wmi-tlv.c +@@ -4601,6 +4601,8 @@ static const struct wmi_ops wmi_tlv_ops .gen_echo = ath10k_wmi_tlv_op_gen_echo, .gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf, .gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable, @@ -454,9 +454,9 @@ v13: }; static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = { ---- a/ath10k-5.15/wmi.c -+++ b/ath10k-5.15/wmi.c -@@ -8413,6 +8413,49 @@ ath10k_wmi_op_gen_peer_set_param(struct +--- a/ath10k-6.2/wmi.c ++++ b/ath10k-6.2/wmi.c +@@ -8438,6 +8438,49 @@ ath10k_wmi_op_gen_peer_set_param(struct return skb; } @@ -506,7 +506,7 @@ v13: static struct sk_buff * ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, enum wmi_sta_ps_mode psmode) -@@ -10244,6 +10287,9 @@ static const struct wmi_ops wmi_ops = { +@@ -10269,6 +10312,9 @@ static const struct wmi_ops wmi_ops = { .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, .gen_echo = ath10k_wmi_op_gen_echo, @@ -516,7 +516,7 @@ v13: /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ -@@ -10314,6 +10360,8 @@ static const struct wmi_ops wmi_10_1_ops +@@ -10339,6 +10385,8 @@ static const struct wmi_ops wmi_10_1_ops .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, .gen_echo = ath10k_wmi_op_gen_echo, @@ -525,7 +525,7 @@ v13: /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ -@@ -10393,6 +10441,8 @@ static const struct wmi_ops wmi_10_2_ops +@@ -10418,6 +10466,8 @@ static const struct wmi_ops wmi_10_2_ops .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, @@ -534,7 +534,7 @@ v13: /* .gen_pdev_enable_adaptive_cca not implemented */ }; -@@ -10464,6 +10514,8 @@ static const struct wmi_ops wmi_10_2_4_o +@@ -10489,6 +10539,8 @@ static const struct wmi_ops wmi_10_2_4_o ath10k_wmi_op_gen_pdev_enable_adaptive_cca, .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype, .gen_bb_timing = ath10k_wmi_10_2_4_op_gen_bb_timing, @@ -543,7 +543,7 @@ v13: /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ -@@ -10546,6 +10598,8 @@ static const struct wmi_ops wmi_10_4_ops +@@ -10571,6 +10623,8 @@ static const struct wmi_ops wmi_10_4_ops .gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info, .gen_echo = ath10k_wmi_op_gen_echo, .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config, @@ -552,8 +552,8 @@ v13: }; int ath10k_wmi_attach(struct ath10k *ar) ---- a/ath10k-5.15/wmi.h -+++ b/ath10k-5.15/wmi.h +--- a/ath10k-6.2/wmi.h ++++ b/ath10k-6.2/wmi.h @@ -3133,6 +3133,41 @@ enum wmi_10_4_feature_mask { }; diff --git a/package/kernel/ath10k-ct/patches/202-ath10k-use-tpt-trigger-by-default.patch b/package/kernel/ath10k-ct/patches/202-ath10k-use-tpt-trigger-by-default.patch index 18c7930203c..8ac2776f922 100644 --- a/package/kernel/ath10k-ct/patches/202-ath10k-use-tpt-trigger-by-default.patch +++ b/package/kernel/ath10k-ct/patches/202-ath10k-use-tpt-trigger-by-default.patch @@ -9,14 +9,14 @@ traffic. Signed-off-by: Mathias Kresin --- - ath10k-5.15/core.h | 4 ++++ - ath10k-5.15/leds.c | 4 +--- - ath10k-5.15/mac.c | 2 +- + ath10k-6.2/core.h | 4 ++++ + ath10k-6.2/leds.c | 4 +--- + ath10k-6.2/mac.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) ---- a/ath10k-5.15/core.h -+++ b/ath10k-5.15/core.h -@@ -1692,6 +1692,10 @@ struct ath10k { +--- a/ath10k-6.2/core.h ++++ b/ath10k-6.2/core.h +@@ -1701,6 +1701,10 @@ struct ath10k { u8 csi_data[4096]; u16 csi_data_len; @@ -27,8 +27,8 @@ Signed-off-by: Mathias Kresin /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; ---- a/ath10k-5.15/leds.c -+++ b/ath10k-5.15/leds.c +--- a/ath10k-6.2/leds.c ++++ b/ath10k-6.2/leds.c @@ -81,9 +81,7 @@ int ath10k_leds_register(struct ath10k * ar->leds.cdev.name = ar->leds.label; @@ -40,9 +40,9 @@ Signed-off-by: Mathias Kresin ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev); if (ret) ---- a/ath10k-5.15/mac.c -+++ b/ath10k-5.15/mac.c -@@ -11551,7 +11551,7 @@ int ath10k_mac_register(struct ath10k *a +--- a/ath10k-6.2/mac.c ++++ b/ath10k-6.2/mac.c +@@ -11617,7 +11617,7 @@ int ath10k_mac_register(struct ath10k *a ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER; #ifdef CPTCFG_MAC80211_LEDS diff --git a/package/kernel/ath10k-ct/patches/203-ath10k-ct-fix-compilation-warning-for-debug-level.patch b/package/kernel/ath10k-ct/patches/203-ath10k-ct-fix-compilation-warning-for-debug-level.patch deleted file mode 100644 index 6c854923e77..00000000000 --- a/package/kernel/ath10k-ct/patches/203-ath10k-ct-fix-compilation-warning-for-debug-level.patch +++ /dev/null @@ -1,222 +0,0 @@ -From 3b07c3a6e4adebd0466f5e539f318224db8cfc37 Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Sat, 6 May 2023 15:29:52 +0200 -Subject: [PATCH] ath10k-ct: fix compilation warning for debug level - -Rework read_debug_level function as it does exceed the stack limit for -some arch. -Fix compilation error: -/__w/openwrt/openwrt/openwrt/build_dir/target-mips-openwrt-linux-musl_musl/linux-malta_be/ath10k-ct-regular/ath10k-ct-2022-05-13-f808496f/ath10k-5.15/debug.c: In function 'ath10k_read_debug_level': -/__w/openwrt/openwrt/openwrt/build_dir/target-mips-openwrt-linux-musl_musl/linux-malta_be/ath10k-ct-regular/ath10k-ct-2022-05-13-f808496f/ath10k-5.15/debug.c:1388:1: error: the frame size of 1440 bytes is larger than 1024 bytes [-Werror=frame-larger-than=] - 1388 | } - | ^ - -Signed-off-by: Christian Marangi ---- - ath10k-5.15/debug.c | 85 +++++++++++++++++++++++++-------------------- - ath10k-5.17/debug.c | 85 +++++++++++++++++++++++++-------------------- - 2 files changed, 96 insertions(+), 74 deletions(-) - -diff --git a/ath10k-5.15/debug.c b/ath10k-5.15/debug.c -index af84012..d0fa911 100644 ---- a/ath10k-5.15/debug.c -+++ b/ath10k-5.15/debug.c -@@ -1344,47 +1344,58 @@ static const struct file_operations fops_simulate_fw_crash = { - .llseek = default_llseek, - }; - -+static const char debug_level_buf[] = -+ "To change debug level, set value adding up desired flags:\n" -+ "PCI: 0x1\n" -+ "WMI: 0x2\n" -+ "HTC: 0x4\n" -+ "HTT: 0x8\n" -+ "MAC: 0x10\n" -+ "BOOT: 0x20\n" -+ "PCI-DUMP: 0x40\n" -+ "HTT-DUMP: 0x80\n" -+ "MGMT: 0x100\n" -+ "DATA: 0x200\n" -+ "BMI: 0x400\n" -+ "REGULATORY: 0x800\n" -+ "TESTMODE: 0x1000\n" -+ "WMI-PRINT: 0x2000\n" -+ "PCI-PS: 0x4000\n" -+ "AHB: 0x8000\n" -+ "SDIO: 0x10000\n" -+ "SDIO_DUMP: 0x20000\n" -+ "USB: 0x40000\n" -+ "USB_BULK: 0x80000\n" -+ "SNOC: 0x100000\n" -+ "QMI: 0x200000\n" -+ "BEACONS: 0x8000000\n" -+ "NO-FW-DBGLOG:0x10000000\n" -+ "MAC2: 0x20000000\n" -+ "INFO-AS-DBG: 0x40000000\n" -+ "FW: 0x80000000\n" -+ "ALL: 0xEFFFFFFF\n"; -+ -+#define READ_DEBUG_LEVEL_SIZE sizeof(debug_level_buf) + 60 -+ - static ssize_t ath10k_read_debug_level(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) - { -- int sz; -- const char buf[] = -- "To change debug level, set value adding up desired flags:\n" -- "PCI: 0x1\n" -- "WMI: 0x2\n" -- "HTC: 0x4\n" -- "HTT: 0x8\n" -- "MAC: 0x10\n" -- "BOOT: 0x20\n" -- "PCI-DUMP: 0x40\n" -- "HTT-DUMP: 0x80\n" -- "MGMT: 0x100\n" -- "DATA: 0x200\n" -- "BMI: 0x400\n" -- "REGULATORY: 0x800\n" -- "TESTMODE: 0x1000\n" -- "WMI-PRINT: 0x2000\n" -- "PCI-PS: 0x4000\n" -- "AHB: 0x8000\n" -- "SDIO: 0x10000\n" -- "SDIO_DUMP: 0x20000\n" -- "USB: 0x40000\n" -- "USB_BULK: 0x80000\n" -- "SNOC: 0x100000\n" -- "QMI: 0x200000\n" -- "BEACONS: 0x8000000\n" -- "NO-FW-DBGLOG:0x10000000\n" -- "MAC2: 0x20000000\n" -- "INFO-AS-DBG: 0x40000000\n" -- "FW: 0x80000000\n" -- "ALL: 0xEFFFFFFF\n"; -- char wbuf[sizeof(buf) + 60]; -- sz = snprintf(wbuf, sizeof(wbuf), "Current debug level: 0x%x\n\n%s", -- ath10k_debug_mask, buf); -- wbuf[sizeof(wbuf) - 1] = 0; -- -- return simple_read_from_buffer(user_buf, count, ppos, wbuf, sz); -+ int sz, ret; -+ char *wbuf; -+ -+ wbuf = kcalloc(READ_DEBUG_LEVEL_SIZE, sizeof(char), GFP_KERNEL); -+ if (!wbuf) -+ return -ENOMEM; -+ -+ sz = snprintf(wbuf, READ_DEBUG_LEVEL_SIZE, -+ "Current debug level: 0x%x\n\n%s", -+ ath10k_debug_mask, debug_level_buf); -+ -+ ret = simple_read_from_buffer(user_buf, count, ppos, wbuf, sz); -+ kfree(wbuf); -+ -+ return ret; - } - - /* Set logging level. -diff --git a/ath10k-5.17/debug.c b/ath10k-5.17/debug.c -index af84012..d0fa911 100644 ---- a/ath10k-5.17/debug.c -+++ b/ath10k-5.17/debug.c -@@ -1344,47 +1344,58 @@ static const struct file_operations fops_simulate_fw_crash = { - .llseek = default_llseek, - }; - -+static const char debug_level_buf[] = -+ "To change debug level, set value adding up desired flags:\n" -+ "PCI: 0x1\n" -+ "WMI: 0x2\n" -+ "HTC: 0x4\n" -+ "HTT: 0x8\n" -+ "MAC: 0x10\n" -+ "BOOT: 0x20\n" -+ "PCI-DUMP: 0x40\n" -+ "HTT-DUMP: 0x80\n" -+ "MGMT: 0x100\n" -+ "DATA: 0x200\n" -+ "BMI: 0x400\n" -+ "REGULATORY: 0x800\n" -+ "TESTMODE: 0x1000\n" -+ "WMI-PRINT: 0x2000\n" -+ "PCI-PS: 0x4000\n" -+ "AHB: 0x8000\n" -+ "SDIO: 0x10000\n" -+ "SDIO_DUMP: 0x20000\n" -+ "USB: 0x40000\n" -+ "USB_BULK: 0x80000\n" -+ "SNOC: 0x100000\n" -+ "QMI: 0x200000\n" -+ "BEACONS: 0x8000000\n" -+ "NO-FW-DBGLOG:0x10000000\n" -+ "MAC2: 0x20000000\n" -+ "INFO-AS-DBG: 0x40000000\n" -+ "FW: 0x80000000\n" -+ "ALL: 0xEFFFFFFF\n"; -+ -+#define READ_DEBUG_LEVEL_SIZE sizeof(debug_level_buf) + 60 -+ - static ssize_t ath10k_read_debug_level(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) - { -- int sz; -- const char buf[] = -- "To change debug level, set value adding up desired flags:\n" -- "PCI: 0x1\n" -- "WMI: 0x2\n" -- "HTC: 0x4\n" -- "HTT: 0x8\n" -- "MAC: 0x10\n" -- "BOOT: 0x20\n" -- "PCI-DUMP: 0x40\n" -- "HTT-DUMP: 0x80\n" -- "MGMT: 0x100\n" -- "DATA: 0x200\n" -- "BMI: 0x400\n" -- "REGULATORY: 0x800\n" -- "TESTMODE: 0x1000\n" -- "WMI-PRINT: 0x2000\n" -- "PCI-PS: 0x4000\n" -- "AHB: 0x8000\n" -- "SDIO: 0x10000\n" -- "SDIO_DUMP: 0x20000\n" -- "USB: 0x40000\n" -- "USB_BULK: 0x80000\n" -- "SNOC: 0x100000\n" -- "QMI: 0x200000\n" -- "BEACONS: 0x8000000\n" -- "NO-FW-DBGLOG:0x10000000\n" -- "MAC2: 0x20000000\n" -- "INFO-AS-DBG: 0x40000000\n" -- "FW: 0x80000000\n" -- "ALL: 0xEFFFFFFF\n"; -- char wbuf[sizeof(buf) + 60]; -- sz = snprintf(wbuf, sizeof(wbuf), "Current debug level: 0x%x\n\n%s", -- ath10k_debug_mask, buf); -- wbuf[sizeof(wbuf) - 1] = 0; -- -- return simple_read_from_buffer(user_buf, count, ppos, wbuf, sz); -+ int sz, ret; -+ char *wbuf; -+ -+ wbuf = kcalloc(READ_DEBUG_LEVEL_SIZE, sizeof(char), GFP_KERNEL); -+ if (!wbuf) -+ return -ENOMEM; -+ -+ sz = snprintf(wbuf, READ_DEBUG_LEVEL_SIZE, -+ "Current debug level: 0x%x\n\n%s", -+ ath10k_debug_mask, debug_level_buf); -+ -+ ret = simple_read_from_buffer(user_buf, count, ppos, wbuf, sz); -+ kfree(wbuf); -+ -+ return ret; - } - - /* Set logging level. --- -2.39.2 - diff --git a/package/kernel/ath10k-ct/patches/300-ath10k-ct-Fix-spectral-scan-NULL-pointer.patch b/package/kernel/ath10k-ct/patches/300-ath10k-ct-Fix-spectral-scan-NULL-pointer.patch deleted file mode 100644 index a3822a7e49f..00000000000 --- a/package/kernel/ath10k-ct/patches/300-ath10k-ct-Fix-spectral-scan-NULL-pointer.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0d2e335d780bda1432a9ba719c8200f796d27854 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 29 Nov 2021 12:27:12 +0100 -Subject: [PATCH] ath10k-ct: Fix spectral scan NULL pointer - -If spectral scan support is enabled then ath10k-ct will cause a NULL -pointer due to relay_open() being called with a const callback struct -which is only supported in kernel 5.11 and later. - -So, simply check the kernel version and if 5.11 and newer use the const -callback struct, otherwise use the regular struct. - -Fixes: 553a3ac ("ath10k-ct: use 5.15 version") -Signed-off-by: Robert Marko ---- - ath10k-5.15/spectral.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/ath10k-5.15/spectral.c -+++ b/ath10k-5.15/spectral.c -@@ -497,7 +497,11 @@ static int remove_buf_file_handler(struc - return 0; - } - -+#if LINUX_VERSION_IS_GEQ(5,11,0) - static const struct rchan_callbacks rfs_spec_scan_cb = { -+#else -+static struct rchan_callbacks rfs_spec_scan_cb = { -+#endif - .create_buf_file = create_buf_file_handler, - .remove_buf_file = remove_buf_file_handler, - }; diff --git a/package/kernel/ath10k-ct/patches/960-0010-ath10k-limit-htt-rx-ring-size.patch b/package/kernel/ath10k-ct/patches/960-0010-ath10k-limit-htt-rx-ring-size.patch index d50611b9706..1471902be3b 100644 --- a/package/kernel/ath10k-ct/patches/960-0010-ath10k-limit-htt-rx-ring-size.patch +++ b/package/kernel/ath10k-ct/patches/960-0010-ath10k-limit-htt-rx-ring-size.patch @@ -1,5 +1,5 @@ ---- a/ath10k-5.15/htt.h -+++ b/ath10k-5.15/htt.h +--- a/ath10k-6.2/htt.h ++++ b/ath10k-6.2/htt.h @@ -237,7 +237,11 @@ enum htt_rx_ring_flags { }; diff --git a/package/kernel/ath10k-ct/patches/960-0011-ath10k-limit-pci-buffer-size.patch b/package/kernel/ath10k-ct/patches/960-0011-ath10k-limit-pci-buffer-size.patch index b8c6c353266..145b2e7c358 100644 --- a/package/kernel/ath10k-ct/patches/960-0011-ath10k-limit-pci-buffer-size.patch +++ b/package/kernel/ath10k-ct/patches/960-0011-ath10k-limit-pci-buffer-size.patch @@ -1,5 +1,5 @@ ---- a/ath10k-5.15/pci.c -+++ b/ath10k-5.15/pci.c +--- a/ath10k-6.2/pci.c ++++ b/ath10k-6.2/pci.c @@ -131,7 +131,11 @@ static const struct ce_attr pci_host_ce_ .flags = CE_ATTR_FLAGS, .src_nentries = 0, diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 934ba3c4f2d..d2cc67d1644 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -111,6 +111,7 @@ $(eval $(call KernelPackage,libphy)) define KernelPackage/phylink SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=Model for MAC to optional PHY connection + DEPENDS:=+kmod-libphy KCONFIG:=CONFIG_PHYLINK FILES:=$(LINUX_DIR)/drivers/net/phy/phylink.ko AUTOLOAD:=$(call AutoLoad,15,phylink,1) @@ -1540,3 +1541,18 @@ endef $(eval $(call KernelPackage,lan743x)) +define KernelPackage/amazon-ena + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Elastic Network Adapter (for Amazon AWS) + DEPENDS:=@TARGET_x86_64||TARGET_armvirt_64 + KCONFIG:=CONFIG_ENA_ETHERNET + FILES:=$(LINUX_DIR)/drivers/net/ethernet/amazon/ena/ena.ko + AUTOLOAD:=$(call AutoLoad,12,ena) +endef + +define KernelPackage/amazon-ena/description + This driver supports Elastic Network Adapter (ENA) + used by Amazon AWS T3 (2018) and later instances. +endef + +$(eval $(call KernelPackage,amazon-ena)) diff --git a/package/kernel/linux/modules/netfilter.mk b/package/kernel/linux/modules/netfilter.mk index 7cfbecd949c..37b5ff597cd 100644 --- a/package/kernel/linux/modules/netfilter.mk +++ b/package/kernel/linux/modules/netfilter.mk @@ -1,6 +1,6 @@ # -# Copyright (C) 2006-2010 OpenWrt.org +# Copyright (C) 2006-2023 OpenWrt.org # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. @@ -1147,6 +1147,26 @@ endef $(eval $(call KernelPackage,nft-bridge)) +define KernelPackage/nft-dup-inet + SUBMENU:=$(NF_MENU) + TITLE:=Netfilter nf_tables dup in ip/ip6/inet familly support + DEPENDS:=+kmod-nft-core +kmod-nf-conntrack +IPV6:kmod-nf-conntrack6 + KCONFIG:= \ + CONFIG_NF_DUP_IPV4 \ + CONFIG_NF_DUP_IPV6 \ + CONFIG_NFT_DUP_IPV4 \ + CONFIG_NFT_DUP_IPV6 + FILES:= \ + $(LINUX_DIR)/net/ipv4/netfilter/nf_dup_ipv4.ko \ + $(LINUX_DIR)/net/ipv6/netfilter/nf_dup_ipv6.ko \ + $(LINUX_DIR)/net/ipv4/netfilter/nft_dup_ipv4.ko \ + $(LINUX_DIR)/net/ipv6/netfilter/nft_dup_ipv6.ko + AUTOLOAD:=$(call AutoProbe,nf_dup_ipv4 nf_dup_ipv6 nft_dup_ipv4 nft_dup_ipv6) +endef + +$(eval $(call KernelPackage,nft-dup-inet)) + + define KernelPackage/nft-nat SUBMENU:=$(NF_MENU) TITLE:=Netfilter nf_tables NAT support diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk index dcc2799c79a..8fcbb3e08c2 100644 --- a/package/kernel/linux/modules/netsupport.mk +++ b/package/kernel/linux/modules/netsupport.mk @@ -1275,7 +1275,8 @@ define KernelPackage/rxrpc FILES:= \ $(LINUX_DIR)/net/rxrpc/rxrpc.ko AUTOLOAD:=$(call AutoLoad,30,rxrpc.ko) - DEPENDS:= +kmod-crypto-manager +kmod-crypto-pcbc +kmod-crypto-fcrypt + DEPENDS:= +kmod-crypto-manager +kmod-crypto-pcbc +kmod-crypto-fcrypt \ + +kmod-udptunnel4 +kmod-udptunnel6 endef define KernelPackage/rxrpc/description @@ -1362,6 +1363,21 @@ endef $(eval $(call KernelPackage,mdio)) +define KernelPackage/mdio-bus-mux + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=MDIO bus multiplexers + KCONFIG:=CONFIG_MDIO_BUS_MUX + HIDDEN:=1 + FILES:=$(LINUX_DIR)/drivers/net/mdio/mdio-mux.ko + AUTOLOAD:=$(call AutoLoad,32,mdio-mux) +endef + +define KernelPackage/mdio/description + Kernel framework for MDIO bus multiplexers. +endef + +$(eval $(call KernelPackage,mdio-bus-mux)) + define KernelPackage/macsec SUBMENU:=$(NETWORK_SUPPORT_MENU) TITLE:=IEEE 802.1AE MAC-level encryption (MAC) diff --git a/package/kernel/linux/modules/usb.mk b/package/kernel/linux/modules/usb.mk index f9b00fabd8a..644365ed19e 100644 --- a/package/kernel/linux/modules/usb.mk +++ b/package/kernel/linux/modules/usb.mk @@ -331,17 +331,6 @@ define KernelPackage/usb-bcma endef $(eval $(call KernelPackage,usb-bcma)) -define KernelPackage/usb-fotg210 - TITLE:=Support for FOTG210 USB host controllers - DEPENDS:=@USB_SUPPORT @TARGET_gemini - KCONFIG:=CONFIG_USB_FOTG210_HCD - FILES:= \ - $(if $(CONFIG_USB_FOTG210_HCD),$(LINUX_DIR)/drivers/usb/host/fotg210-hcd.ko) - AUTOLOAD:=$(call AutoLoad,50,fotg210-hcd,1) - $(call AddDepends/usb) -endef -$(eval $(call KernelPackage,usb-fotg210)) - define KernelPackage/usb-ssb TITLE:=Support for SSB USB controllers DEPENDS:=@USB_SUPPORT @TARGET_bcm47xx @@ -1285,7 +1274,7 @@ $(eval $(call KernelPackage,usb-net-smsc75xx)) define KernelPackage/usb-net-smsc95xx TITLE:=SMSC LAN95XX based USB 2.0 10/100 ethernet devices - DEPENDS:=+kmod-libphy +kmod-phy-smsc + DEPENDS:=+kmod-libphy +kmod-phy-smsc +LINUX_6_1:kmod-net-selftests KCONFIG:=CONFIG_USB_NET_SMSC95XX FILES:=$(LINUX_DIR)/drivers/$(USBNET_DIR)/smsc95xx.ko AUTOLOAD:=$(call AutoProbe,smsc95xx) diff --git a/package/kernel/mac80211/patches/build/050-lib80211_option.patch b/package/kernel/mac80211/patches/build/050-lib80211_option.patch deleted file mode 100644 index c1b1bc757f2..00000000000 --- a/package/kernel/mac80211/patches/build/050-lib80211_option.patch +++ /dev/null @@ -1,34 +0,0 @@ ---- a/net/wireless/Kconfig -+++ b/net/wireless/Kconfig -@@ -188,7 +188,7 @@ config CFG80211_WEXT_EXPORT - endif # CFG80211 - - config LIB80211 -- tristate -+ tristate "lib80211" - depends on m - default n - help -@@ -198,19 +198,19 @@ config LIB80211 - Drivers should select this themselves if needed. - - config LIB80211_CRYPT_WEP -- tristate -+ tristate "lib80211 WEP support" - depends on m - select BPAUTO_CRYPTO_LIB_ARC4 - - config LIB80211_CRYPT_CCMP -- tristate -+ tristate "lib80211 CCMP support" - depends on m - depends on CRYPTO - depends on CRYPTO_AES - depends on CRYPTO_CCM - - config LIB80211_CRYPT_TKIP -- tristate -+ tristate "lib80211 TKIP support" - depends on m - select BPAUTO_CRYPTO_LIB_ARC4 - diff --git a/package/kernel/qca-nss-dp/patches/0014-nss-dp-edma-v1-use-NAPI-GRO-by-default.patch b/package/kernel/qca-nss-dp/patches/0014-nss-dp-edma-v1-use-NAPI-GRO-by-default.patch index 3683b4ec4c9..f3a0948dfa8 100644 --- a/package/kernel/qca-nss-dp/patches/0014-nss-dp-edma-v1-use-NAPI-GRO-by-default.patch +++ b/package/kernel/qca-nss-dp/patches/0014-nss-dp-edma-v1-use-NAPI-GRO-by-default.patch @@ -18,8 +18,6 @@ Signed-off-by: Robert Marko hal/dp_ops/edma_dp/edma_v1/edma_tx_rx.c | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) -diff --git a/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c b/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c -index 1d748db..e81c461 100644 --- a/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c +++ b/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c @@ -589,10 +589,12 @@ drop: @@ -39,11 +37,9 @@ index 1d748db..e81c461 100644 } /* TODO - check if this is needed */ -diff --git a/hal/dp_ops/edma_dp/edma_v1/edma_tx_rx.c b/hal/dp_ops/edma_dp/edma_v1/edma_tx_rx.c -index 5780a30..a002a79 100644 --- a/hal/dp_ops/edma_dp/edma_v1/edma_tx_rx.c +++ b/hal/dp_ops/edma_dp/edma_v1/edma_tx_rx.c -@@ -410,8 +410,12 @@ static uint32_t edma_clean_rx(struct edma_hw *ehw, +@@ -410,8 +410,12 @@ static uint32_t edma_clean_rx(struct edm if (unlikely(EDMA_RXPH_SERVICE_CODE_GET(rxph) == NSS_PTP_EVENT_SERVICE_CODE)) nss_phy_tstamp_rx_buf(ndev, skb); @@ -58,6 +54,3 @@ index 5780a30..a002a79 100644 next_rx_desc: /* --- -2.38.1 - diff --git a/package/kernel/qca-nss-dp/patches/0015-nss-dp-allow-setting-netdev-name-from-DTS.patch b/package/kernel/qca-nss-dp/patches/0015-nss-dp-allow-setting-netdev-name-from-DTS.patch index b1aebc268f7..906e5ee7624 100644 --- a/package/kernel/qca-nss-dp/patches/0015-nss-dp-allow-setting-netdev-name-from-DTS.patch +++ b/package/kernel/qca-nss-dp/patches/0015-nss-dp-allow-setting-netdev-name-from-DTS.patch @@ -13,11 +13,9 @@ Signed-off-by: Robert Marko nss_dp_main.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) -diff --git a/nss_dp_main.c b/nss_dp_main.c -index 18e1088..19e14fb 100644 --- a/nss_dp_main.c +++ b/nss_dp_main.c -@@ -685,18 +685,29 @@ static int32_t nss_dp_probe(struct platform_device *pdev) +@@ -685,18 +685,29 @@ static int32_t nss_dp_probe(struct platf struct nss_dp_dev *dp_priv; struct device_node *np = pdev->dev.of_node; struct nss_gmac_hal_platform_data gmac_hal_pdata; @@ -50,6 +48,3 @@ index 18e1088..19e14fb 100644 return -ENOMEM; } --- -2.38.1 - diff --git a/package/kernel/qca-nss-dp/patches/0016-nss-dp-netdev-mac_addr-is-const.patch b/package/kernel/qca-nss-dp/patches/0016-nss-dp-netdev-mac_addr-is-const.patch new file mode 100644 index 00000000000..6957f146092 --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0016-nss-dp-netdev-mac_addr-is-const.patch @@ -0,0 +1,57 @@ +From ff9284e3a59982c78a0132e6f2c5e3f04ba11472 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 29 Sep 2022 20:37:46 +0200 +Subject: [PATCH 15/16] nss-dp: netdev mac_addr is const + +Signed-off-by: Robert Marko +--- + exports/nss_dp_api_if.h | 2 +- + hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c | 2 +- + hal/dp_ops/edma_dp/edma_v2/edma_dp.c | 2 +- + hal/dp_ops/syn_gmac_dp/syn_dp.c | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +--- a/exports/nss_dp_api_if.h ++++ b/exports/nss_dp_api_if.h +@@ -83,7 +83,7 @@ struct nss_dp_data_plane_ops { + int (*close)(struct nss_dp_data_plane_ctx *dpc); + int (*link_state)(struct nss_dp_data_plane_ctx *dpc, + uint32_t link_state); +- int (*mac_addr)(struct nss_dp_data_plane_ctx *dpc, uint8_t *addr); ++ int (*mac_addr)(struct nss_dp_data_plane_ctx *dpc, const uint8_t *addr); + int (*change_mtu)(struct nss_dp_data_plane_ctx *dpc, uint32_t mtu); + netdev_tx_t (*xmit)(struct nss_dp_data_plane_ctx *dpc, struct sk_buff *os_buf); + void (*set_features)(struct nss_dp_data_plane_ctx *dpc); +--- a/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c ++++ b/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c +@@ -494,7 +494,7 @@ static int edma_if_link_state(struct nss + /* + * edma_if_mac_addr() + */ +-static int edma_if_mac_addr(struct nss_dp_data_plane_ctx *dpc, uint8_t *addr) ++static int edma_if_mac_addr(struct nss_dp_data_plane_ctx *dpc, const uint8_t *addr) + { + return NSS_DP_SUCCESS; + } +--- a/hal/dp_ops/edma_dp/edma_v2/edma_dp.c ++++ b/hal/dp_ops/edma_dp/edma_v2/edma_dp.c +@@ -99,7 +99,7 @@ static int edma_dp_link_state(struct nss + * edma_dp_mac_addr() + * EDMA data plane MAC address change API + */ +-static int edma_dp_mac_addr(struct nss_dp_data_plane_ctx *dpc, uint8_t *addr) ++static int edma_dp_mac_addr(struct nss_dp_data_plane_ctx *dpc, const uint8_t *addr) + { + return NSS_DP_SUCCESS; + } +--- a/hal/dp_ops/syn_gmac_dp/syn_dp.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp.c +@@ -289,7 +289,7 @@ static int syn_dp_if_link_state(struct n + /* + * syn_dp_if_mac_addr() + */ +-static int syn_dp_if_mac_addr(struct nss_dp_data_plane_ctx *dpc, uint8_t *addr) ++static int syn_dp_if_mac_addr(struct nss_dp_data_plane_ctx *dpc, const uint8_t *addr) + { + return NSS_DP_SUCCESS; + } diff --git a/package/kernel/qca-nss-dp/patches/0017-nss-dp-use-proper-netdev-MAC-helpers.patch b/package/kernel/qca-nss-dp/patches/0017-nss-dp-use-proper-netdev-MAC-helpers.patch new file mode 100644 index 00000000000..beb6db4996d --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0017-nss-dp-use-proper-netdev-MAC-helpers.patch @@ -0,0 +1,27 @@ +From 704706969301076961c15423dedce9e2e6f1026e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 29 Sep 2022 20:39:07 +0200 +Subject: [PATCH 16/16] nss-dp: use proper netdev MAC helpers + +mac_addr is const, so utilize proper helpers for setting random and +desired MAC addres as old ones were dropped in newer kernels. + +Signed-off-by: Robert Marko +--- + nss_dp_main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/nss_dp_main.c ++++ b/nss_dp_main.c +@@ -599,9 +599,9 @@ static int32_t nss_dp_of_get_pdata(struc + + ret = of_get_mac_address(np, maddr); + if (!ret && is_valid_ether_addr(maddr)) { +- ether_addr_copy(netdev->dev_addr, maddr); ++ eth_hw_addr_set(netdev, maddr); + } else { +- random_ether_addr(netdev->dev_addr); ++ eth_hw_addr_random(netdev); + pr_info("GMAC%d(%px) Invalid MAC@ - using %pM\n", dp_priv->macid, + dp_priv, netdev->dev_addr); + } diff --git a/package/kernel/qca-nss-dp/patches/0018-nss-dp-adapt-to-netif_napi_add-changes.patch b/package/kernel/qca-nss-dp/patches/0018-nss-dp-adapt-to-netif_napi_add-changes.patch new file mode 100644 index 00000000000..084f30da136 --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0018-nss-dp-adapt-to-netif_napi_add-changes.patch @@ -0,0 +1,84 @@ +From ba748ab91a62db57f9bdf69dd306e6557315db85 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 3 Oct 2022 23:05:14 +0200 +Subject: [PATCH] nss-dp: adapt to netif_napi_add() changes + +netif_napi_add() removed the weight argument and just uses the default +NAPI_POLL_WEIGHT in background, so for those requiring custom weight use +netif_napi_add_weight() instead. + +Signed-off-by: Robert Marko +--- + hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c | 6 +++++- + hal/dp_ops/edma_dp/edma_v2/edma_cfg_rx.c | 8 +++++++- + hal/dp_ops/edma_dp/edma_v2/edma_cfg_tx.c | 7 ++++++- + hal/dp_ops/syn_gmac_dp/syn_dp.c | 5 +++++ + 4 files changed, 23 insertions(+), 3 deletions(-) + +--- a/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c ++++ b/hal/dp_ops/edma_dp/edma_v1/edma_data_plane.c +@@ -838,8 +838,12 @@ static int edma_register_netdevice(struc + * NAPI add + */ + if (!edma_hw.napi_added) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + netif_napi_add(netdev, &edma_hw.napi, edma_napi, +- NAPI_POLL_WEIGHT); ++ NAPI_POLL_WEIGHT); ++#else ++ netif_napi_add(netdev, &edma_hw.napi, edma_napi); ++#endif + /* + * Register the interrupt handlers and enable interrupts + */ +--- a/hal/dp_ops/edma_dp/edma_v2/edma_cfg_rx.c ++++ b/hal/dp_ops/edma_dp/edma_v2/edma_cfg_rx.c +@@ -1097,8 +1097,14 @@ void edma_cfg_rx_napi_add(struct edma_gb + + for (i = 0; i < egc->num_rxdesc_rings; i++) { + struct edma_rxdesc_ring *rxdesc_ring = &egc->rxdesc_rings[i]; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + netif_napi_add(netdev, &rxdesc_ring->napi, +- edma_rx_napi_poll, nss_dp_rx_napi_budget); ++ edma_rx_napi_poll, nss_dp_rx_napi_budget); ++ ++#else ++ netif_napi_add_weight(netdev, &rxdesc_ring->napi, ++ edma_rx_napi_poll, nss_dp_rx_napi_budget); ++#endif + rxdesc_ring->napi_added = true; + } + edma_info("%s: Rx NAPI budget: %d\n", netdev->name, nss_dp_rx_napi_budget); +--- a/hal/dp_ops/edma_dp/edma_v2/edma_cfg_tx.c ++++ b/hal/dp_ops/edma_dp/edma_v2/edma_cfg_tx.c +@@ -672,8 +672,13 @@ void edma_cfg_tx_napi_add(struct edma_gb + for (i = 0; i < egc->num_txcmpl_rings; i++) { + struct edma_txcmpl_ring *txcmpl_ring = &egc->txcmpl_rings[i]; + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + netif_napi_add(netdev, &txcmpl_ring->napi, +- edma_tx_napi_poll, nss_dp_tx_napi_budget); ++ edma_tx_napi_poll, nss_dp_tx_napi_budget); ++#else ++ netif_napi_add_weight(netdev, &txcmpl_ring->napi, ++ edma_tx_napi_poll, nss_dp_tx_napi_budget); ++#endif + txcmpl_ring->napi_added = true; + } + edma_info("Tx NAPI budget: %d\n", nss_dp_tx_napi_budget); +--- a/hal/dp_ops/syn_gmac_dp/syn_dp.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp.c +@@ -189,8 +189,13 @@ static int syn_dp_if_init(struct nss_dp_ + } + + if (!dev_info->napi_added) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + netif_napi_add(netdev, &rx_info->napi_rx, syn_dp_napi_poll_rx, SYN_DP_NAPI_BUDGET_RX); + netif_napi_add(netdev, &tx_info->napi_tx, syn_dp_napi_poll_tx, SYN_DP_NAPI_BUDGET_TX); ++#else ++ netif_napi_add_weight(netdev, &rx_info->napi_rx, syn_dp_napi_poll_rx, SYN_DP_NAPI_BUDGET_RX); ++ netif_napi_add_weight(netdev, &tx_info->napi_tx, syn_dp_napi_poll_tx, SYN_DP_NAPI_BUDGET_TX); ++#endif + + /* + * Requesting irq. Set IRQ_DISABLE_UNLAZY flag, this flag diff --git a/package/kernel/qca-ssdk/patches/0015-SSDK-config-add-kernel-6.1.patch b/package/kernel/qca-ssdk/patches/0015-SSDK-config-add-kernel-6.1.patch new file mode 100644 index 00000000000..77c54979a9c --- /dev/null +++ b/package/kernel/qca-ssdk/patches/0015-SSDK-config-add-kernel-6.1.patch @@ -0,0 +1,55 @@ +From 1f9eb43f118b86c0b68e9d82bfae77471d6c3921 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 29 Sep 2022 09:59:20 +0200 +Subject: [PATCH] SSDK: config: add kernel 6.1 + +Allow kernel 6.1 to be recognized and compiled under it. + +Signed-off-by: Robert Marko +--- + config | 6 +++++- + make/linux_opt.mk | 4 ++-- + 2 files changed, 7 insertions(+), 3 deletions(-) + +--- a/config ++++ b/config +@@ -28,6 +28,10 @@ ifeq ($(KVER),$(filter 5.15%,$(KVER))) + OS_VER=5_15 + endif + ++ifeq ($(KVER),$(filter 6.1%,$(KVER))) ++OS_VER=6_1 ++endif ++ + ifeq ($(KVER), 3.4.0) + OS_VER=3_4 + endif +@@ -136,7 +140,7 @@ ifeq ($(ARCH), arm) + endif + + ifeq ($(ARCH), arm64) +- ifeq ($(KVER),$(filter 4.1% 4.4% 4.9% 5.4% 5.15%,$(KVER))) ++ ifeq ($(KVER),$(filter 4.1% 4.4% 4.9% 5.4% 5.15% 6.1%,$(KVER))) + CPU_CFLAG= -DMODULE -Os -pipe -march=armv8-a -mcpu=cortex-a53+crypto -fno-caller-saves -fno-strict-aliasing -fno-common -Wno-format-security -Wno-pointer-sign -Wno-unused-but-set-variable -Wno-error=unused-result -mcmodel=large + endif + endif +--- a/make/linux_opt.mk ++++ b/make/linux_opt.mk +@@ -437,7 +437,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) + KASAN_SHADOW_SCALE_SHIFT := 3 + endif + +- ifeq (5_4 5_15, $(OS_VER)) ++ ifeq (5_4 5_15 6_1, $(OS_VER)) + ifeq ($(ARCH), arm64) + KASAN_OPTION += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT) + endif +@@ -468,7 +468,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) + + endif + +- ifeq ($(OS_VER),$(filter 4_4 5_4 5_15, $(OS_VER))) ++ ifeq ($(OS_VER),$(filter 4_4 5_4 5_15 6_1, $(OS_VER))) + MODULE_CFLAG += -DKVER34 + MODULE_CFLAG += -DKVER32 + MODULE_CFLAG += -DLNX26_22 diff --git a/package/kernel/qca-ssdk/patches/0016-SSDK-qca808x-use-get_random_u32.patch b/package/kernel/qca-ssdk/patches/0016-SSDK-qca808x-use-get_random_u32.patch new file mode 100644 index 00000000000..d8cd89b7442 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/0016-SSDK-qca808x-use-get_random_u32.patch @@ -0,0 +1,27 @@ +From 55ea8c9b278aafe3211f7250986b1f9d9a06cd21 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 21 Oct 2022 13:40:15 +0200 +Subject: [PATCH] SSDK: qca808x: use get_random_u32 + +prandom has been removed from the kernel in 6.1-rc1, so use get_random_u32 +instead as its the drop-in replacement. + +Signed-off-by: Robert Marko +--- + src/hsl/phy/qca808x_phy.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/src/hsl/phy/qca808x_phy.c ++++ b/src/hsl/phy/qca808x_phy.c +@@ -299,7 +299,11 @@ qca808x_phy_ms_random_seed_set(a_uint32_ + phy_data = qca808x_phy_debug_read(dev_id, phy_id, + QCA808X_DEBUG_LOCAL_SEED); + phy_data &= ~(QCA808X_MASTER_SLAVE_SEED_CFG); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + phy_data |= (prandom_u32()%QCA808X_MASTER_SLAVE_SEED_RANGE) << 2; ++#else ++ phy_data |= (get_random_u32()%QCA808X_MASTER_SLAVE_SEED_RANGE) << 2; ++#endif + SSDK_DEBUG("QCA808X_DEBUG_LOCAL_SEED:%x\n", phy_data); + rv = qca808x_phy_debug_write(dev_id, phy_id, + QCA808X_DEBUG_LOCAL_SEED, phy_data); diff --git a/package/libs/libselinux/Makefile b/package/libs/libselinux/Makefile index db2221d7e31..816e09f30ce 100644 --- a/package/libs/libselinux/Makefile +++ b/package/libs/libselinux/Makefile @@ -103,7 +103,7 @@ endef $(foreach a,$(LIBSELINUX_UTILS),$(eval $(call GenUtilPkg,libselinux-$(a),$(a)))) # Needed to link libselinux utilities, which link against -# libselinux.so, which indirectly depends on libpcre.so, installed in +# libselinux.so, which indirectly depends on libpcre2.so, installed in # $(STAGING_DIR_HOSTPKG). HOST_LDFLAGS += -Wl,-rpath="$(STAGING_DIR_HOSTPKG)/lib" diff --git a/package/libs/openssl/patches/110-openwrt_targets.patch b/package/libs/openssl/patches/110-openwrt_targets.patch index 5abedd400e3..a97c603fa7c 100644 --- a/package/libs/openssl/patches/110-openwrt_targets.patch +++ b/package/libs/openssl/patches/110-openwrt_targets.patch @@ -9,7 +9,7 @@ Signed-off-by: Eneas U de Queiroz --- /dev/null +++ b/Configurations/25-openwrt.conf -@@ -0,0 +1,52 @@ +@@ -0,0 +1,56 @@ +## Openwrt "CONFIG_ARCH" matching targets. + +# The targets need to end in '-openwrt' for the AFALG patch to work @@ -53,6 +53,10 @@ Signed-off-by: Eneas U de Queiroz + inherit_from => [ "linux-ppc64", "openwrt" ], + perlasm_scheme => "linux64v2", + }, ++ "linux-riscv64-openwrt" => { ++ inherit_from => [ "linux-generic64", "openwrt" ], ++ perlasm_scheme => "linux64", ++ }, + "linux-x86_64-openwrt" => { + inherit_from => [ "linux-x86_64", "openwrt" ], + }, diff --git a/package/libs/pcre2/Makefile b/package/libs/pcre2/Makefile index d178e3cd04f..fa4282cee8c 100644 --- a/package/libs/pcre2/Makefile +++ b/package/libs/pcre2/Makefile @@ -66,7 +66,8 @@ CMAKE_HOST_OPTIONS += \ -DPCRE2_SUPPORT_JIT=OFF \ -DPCRE2_SHOW_REPORT=OFF \ -DPCRE2_BUILD_PCRE2GREP=OFF \ - -DPCRE2_BUILD_TESTS=OFF + -DPCRE2_BUILD_TESTS=OFF \ + -DPCRE2_STATIC_PIC=ON CMAKE_OPTIONS += \ -DBUILD_SHARED_LIBS=ON \ diff --git a/package/network/config/netifd/Makefile b/package/network/config/netifd/Makefile index 8dcff7987d2..8d179beb30c 100644 --- a/package/network/config/netifd/Makefile +++ b/package/network/config/netifd/Makefile @@ -5,9 +5,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git -PKG_SOURCE_DATE:=2023-04-17 -PKG_SOURCE_VERSION:=7de5440a520f9c6687358c7ef88ac4aad0ff98a5 -PKG_MIRROR_HASH:=aaf6aef1c991c67b6ef43b4c7f102ba94319bf522f620e93293d031b8333bd8f +PKG_SOURCE_DATE:=2023-05-31 +PKG_SOURCE_VERSION:=38cbdc1c8cbbe2e30d62227d74565bd3fa21a36b +PKG_MIRROR_HASH:=c275ef1f550726b045b3dd7bcee1d660bba9d56c4d53bf32f1eeb6f68bc4116c PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=GPL-2.0 diff --git a/package/network/services/hostapd/Config.in b/package/network/services/hostapd/Config.in index 8f28eb2bd4f..87ad7e093e7 100644 --- a/package/network/services/hostapd/Config.in +++ b/package/network/services/hostapd/Config.in @@ -73,11 +73,6 @@ config WPA_WOLFSSL select WOLFSSL_HAS_SESSION_TICKET select WOLFSSL_HAS_WPAS -config DRIVER_WEXT_SUPPORT - bool - select KERNEL_WIRELESS_EXT - default n - config DRIVER_11AC_SUPPORT bool default n diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index b8921e84527..dc296704898 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -27,7 +27,6 @@ PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_hostapd-basic \ CONFIG_PACKAGE_hostapd-mini \ CONFIG_WPA_RFKILL_SUPPORT \ - CONFIG_DRIVER_WEXT_SUPPORT \ CONFIG_DRIVER_11AC_SUPPORT \ CONFIG_DRIVER_11AX_SUPPORT \ CONFIG_WPA_ENABLE_WEP @@ -87,7 +86,6 @@ DRIVER_MAKEOPTS= \ CONFIG_DRIVER_NL80211=$(CONFIG_PACKAGE_kmod-cfg80211) \ CONFIG_IEEE80211AC=$(HOSTAPD_IEEE80211AC) \ CONFIG_IEEE80211AX=$(HOSTAPD_IEEE80211AX) \ - CONFIG_DRIVER_WEXT=$(CONFIG_DRIVER_WEXT_SUPPORT) \ CONFIG_MBO=$(CONFIG_WPA_MBO_SUPPORT) ifeq ($(SSL_VARIANT),openssl) diff --git a/package/network/services/hostapd/files/wpa_supplicant-basic.config b/package/network/services/hostapd/files/wpa_supplicant-basic.config index 6abd8e2331a..944b4d92876 100644 --- a/package/network/services/hostapd/files/wpa_supplicant-basic.config +++ b/package/network/services/hostapd/files/wpa_supplicant-basic.config @@ -26,7 +26,7 @@ # replacement for WEXT and its use allows wpa_supplicant to properly control # the driver to improve existing functionality like roaming and to support new # functionality. -CONFIG_DRIVER_WEXT=y +#CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y diff --git a/package/network/services/hostapd/files/wpa_supplicant-full.config b/package/network/services/hostapd/files/wpa_supplicant-full.config index d24fbbb01f1..b39dabca069 100644 --- a/package/network/services/hostapd/files/wpa_supplicant-full.config +++ b/package/network/services/hostapd/files/wpa_supplicant-full.config @@ -26,7 +26,7 @@ # replacement for WEXT and its use allows wpa_supplicant to properly control # the driver to improve existing functionality like roaming and to support new # functionality. -CONFIG_DRIVER_WEXT=y +#CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y diff --git a/package/network/services/hostapd/files/wpa_supplicant-mini.config b/package/network/services/hostapd/files/wpa_supplicant-mini.config index 9eb1111e523..2a3f8fb69de 100644 --- a/package/network/services/hostapd/files/wpa_supplicant-mini.config +++ b/package/network/services/hostapd/files/wpa_supplicant-mini.config @@ -26,7 +26,7 @@ # replacement for WEXT and its use allows wpa_supplicant to properly control # the driver to improve existing functionality like roaming and to support new # functionality. -CONFIG_DRIVER_WEXT=y +#CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y diff --git a/package/network/services/hostapd/files/wpa_supplicant-p2p.config b/package/network/services/hostapd/files/wpa_supplicant-p2p.config index 0dcc88e6486..7f5140622cc 100644 --- a/package/network/services/hostapd/files/wpa_supplicant-p2p.config +++ b/package/network/services/hostapd/files/wpa_supplicant-p2p.config @@ -26,7 +26,7 @@ # replacement for WEXT and its use allows wpa_supplicant to properly control # the driver to improve existing functionality like roaming and to support new # functionality. -CONFIG_DRIVER_WEXT=y +#CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y diff --git a/package/network/services/hostapd/patches/432-missing-typedef.patch b/package/network/services/hostapd/patches/432-missing-typedef.patch deleted file mode 100644 index 7a100f1a0d2..00000000000 --- a/package/network/services/hostapd/patches/432-missing-typedef.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/src/drivers/linux_wext.h -+++ b/src/drivers/linux_wext.h -@@ -26,6 +26,7 @@ typedef int32_t __s32; - typedef uint16_t __u16; - typedef int16_t __s16; - typedef uint8_t __u8; -+typedef int8_t __s8; - #ifndef __user - #define __user - #endif /* __user */ diff --git a/package/system/ca-certificates/Makefile b/package/system/ca-certificates/Makefile index 9fac32e7e3e..ec588cc65b6 100644 --- a/package/system/ca-certificates/Makefile +++ b/package/system/ca-certificates/Makefile @@ -7,17 +7,20 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ca-certificates -PKG_VERSION:=20211016 +PKG_VERSION:=20230311 PKG_RELEASE:=1 PKG_MAINTAINER:= PKG_SOURCE:=$(PKG_NAME)_$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@DEBIAN/pool/main/c/ca-certificates -PKG_HASH:=2ae9b6dc5f40c25d6d7fe55e07b54f12a8967d1955d3b7b2f42ee46266eeef88 +PKG_HASH:=83de934afa186e279d1ed08ea0d73f5cf43a6fbfb5f00874b6db3711c64576f3 PKG_INSTALL:=1 include $(INCLUDE_DIR)/package.mk +TAR_OPTIONS+= --strip-components 1 +TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS) + define Package/ca-certificates SECTION:=base CATEGORY:=Base system @@ -34,13 +37,6 @@ define Package/ca-bundle PROVIDES:=ca-certs endef -define Build/Prepare - $(DECOMPRESS_CMD) $(HOST_TAR) -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) - $(Build/Patch) -endef - -MAKE_PATH := work - define Build/Install mkdir -p \ $(PKG_INSTALL_DIR)/usr/sbin \ diff --git a/package/system/ca-certificates/patches/0001-ca-certificates-fix-python3-cryptography-woes-in-cer.patch b/package/system/ca-certificates/patches/0001-ca-certificates-fix-python3-cryptography-woes-in-cer.patch index add01f42c0f..09092617f18 100644 --- a/package/system/ca-certificates/patches/0001-ca-certificates-fix-python3-cryptography-woes-in-cer.patch +++ b/package/system/ca-certificates/patches/0001-ca-certificates-fix-python3-cryptography-woes-in-cer.patch @@ -18,8 +18,8 @@ Reported-by: Chen Minqiang Reported-by: Shane Synan Signed-off-by: Christian Lamparter --- ---- a/work/mozilla/certdata2pem.py -+++ b/work/mozilla/certdata2pem.py +--- a/mozilla/certdata2pem.py ++++ b/mozilla/certdata2pem.py @@ -21,16 +21,12 @@ # USA. @@ -42,8 +42,8 @@ Signed-off-by: Christian Lamparter if not obj['CKA_LABEL'] in trust or not trust[obj['CKA_LABEL']]: continue - -- cert = x509.load_der_x509_certificate(obj['CKA_VALUE']) -- if cert.not_valid_after < datetime.datetime.now(): +- cert = x509.load_der_x509_certificate(bytes(obj['CKA_VALUE'])) +- if cert.not_valid_after < datetime.datetime.utcnow(): - print('!'*74) - print('Trusted but expired certificate found: %s' % obj['CKA_LABEL']) - print('!'*74) diff --git a/package/system/selinux-policy/Makefile b/package/system/selinux-policy/Makefile index bcf6b4a3c24..8e36b267bb4 100644 --- a/package/system/selinux-policy/Makefile +++ b/package/system/selinux-policy/Makefile @@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=selinux-policy PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://git.defensec.nl/selinux-policy.git -PKG_VERSION:=1.2.3 -PKG_MIRROR_HASH:=ff1ddca168a6631aeac34352657f424bc4acf5d50b8aa7ff8dfa8c9663ba8538 +PKG_VERSION:=1.2.5 +PKG_MIRROR_HASH:=81ac6e31d2f1febddbe594f3578a9c40444fc0e349075ab6abd3d3ee014a988e PKG_SOURCE_VERSION:=v$(PKG_VERSION) PKG_BUILD_DEPENDS:=secilc/host policycoreutils/host diff --git a/package/utils/firmware-utils/Makefile b/package/utils/firmware-utils/Makefile new file mode 100644 index 00000000000..1316fa5c6b1 --- /dev/null +++ b/package/utils/firmware-utils/Makefile @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only + +include $(TOPDIR)/rules.mk + +PKG_NAME:=firmware-utils +PKG_RELEASE:=2 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git +PKG_SOURCE_DATE:=2023-05-18 +PKG_SOURCE_VERSION:=02cdbc6a4d61605c008efef09162f772f553fcde +PKG_MIRROR_HASH:=f5188fc38bb03ddbcc34763ff049597e2d8af98c0854910dc87f10e5927096e2 + +PKG_BUILD_DEPENDS:=openssl zlib + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Package/default + SECTION:=utils + CATEGORY:=Base system + TITLE:=Firmware utility $(1) + DEPENDS:=$(2) +endef + +Package/oseama = $(call Package/default,oseama,@TARGET_bcm53xx) + +define Package/oseama/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/oseama $(1)/usr/bin/ +endef + +Package/otrx = $(call Package/default,otrx,@(TARGET_bcm47xx||TARGET_bcm53xx)) + +define Package/otrx/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/otrx $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,oseama)) +$(eval $(call BuildPackage,otrx)) diff --git a/package/utils/oseama/Makefile b/package/utils/oseama/Makefile deleted file mode 100644 index 7d9303b92d5..00000000000 --- a/package/utils/oseama/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (C) 2016 Rafał Miłecki -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=oseama -PKG_RELEASE:=1 - -PKG_FLAGS:=nonshared - -include $(INCLUDE_DIR)/package.mk - -define Package/oseama - SECTION:=utils - CATEGORY:=Base system - TITLE:=Utility for handling Seama firmware images - MAINTAINER:=Rafał Miłecki - DEPENDS:=@TARGET_bcm53xx -endef - -define Package/oseama/description - This package contains an utility that allows handling Seama images. -endef - -define Build/Compile - $(MAKE) -C $(PKG_BUILD_DIR) \ - CC="$(TARGET_CC)" \ - CFLAGS="$(TARGET_CFLAGS) -Wall" -endef - -define Package/oseama/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/oseama $(1)/usr/bin/ -endef - -$(eval $(call BuildPackage,oseama)) diff --git a/package/utils/oseama/src/Makefile b/package/utils/oseama/src/Makefile deleted file mode 100644 index ca35e6ee99d..00000000000 --- a/package/utils/oseama/src/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: oseama - -oseama: - $(CC) $(CFLAGS) -Wall oseama.c md5.c -o $@ $^ - -clean: - rm -f oseama diff --git a/package/utils/oseama/src/md5.c b/package/utils/oseama/src/md5.c deleted file mode 100644 index 52d96accd30..00000000000 --- a/package/utils/oseama/src/md5.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. - * MD5 Message-Digest Algorithm (RFC 1321). - * - * Homepage: - * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 - * - * Author: - * Alexander Peslyak, better known as Solar Designer - * - * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. - * In case this attempt to disclaim copyright and place the software in the - * public domain is deemed null and void, then the software is - * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the - * general public under the following terms: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted. - * - * There's ABSOLUTELY NO WARRANTY, express or implied. - * - * (This is a heavily cut-down "BSD license".) - * - * This differs from Colin Plumb's older public domain implementation in that - * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there's no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from - * Colin Plumb's implementation has been reused; this comment merely compares - * the properties of the two independent implementations. - * - * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known - * optimizations are not included to reduce source code size and avoid - * compile-time configuration. - */ - -#ifndef HAVE_OPENSSL - -#include - -#include "md5.h" - -/* - * The basic MD5 functions. - * - * F and G are optimized compared to their RFC 1321 definitions for - * architectures that lack an AND-NOT instruction, just like in Colin Plumb's - * implementation. - */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -#define H(x, y, z) (((x) ^ (y)) ^ (z)) -#define H2(x, y, z) ((x) ^ ((y) ^ (z))) -#define I(x, y, z) ((y) ^ ((x) | ~(z))) - -/* - * The MD5 transformation for all four rounds. - */ -#define STEP(f, a, b, c, d, x, t, s) \ - (a) += f((b), (c), (d)) + (x) + (t); \ - (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ - (a) += (b); - -/* - * SET reads 4 input bytes in little-endian byte order and stores them - * in a properly aligned word in host byte order. - * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * doesn't work. - */ -#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define SET(n) \ - (*(MD5_u32plus *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) -#else -#define SET(n) \ - (ctx->block[(n)] = \ - (MD5_u32plus)ptr[(n) * 4] | \ - ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ - (ctx->block[(n)]) -#endif - -/* - * This processes one or more 64-byte data blocks, but does NOT update - * the bit counters. There are no alignment requirements. - */ -static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) -{ - const unsigned char *ptr; - MD5_u32plus a, b, c, d; - MD5_u32plus saved_a, saved_b, saved_c, saved_d; - - ptr = (const unsigned char *)data; - - a = ctx->a; - b = ctx->b; - c = ctx->c; - d = ctx->d; - - do { - saved_a = a; - saved_b = b; - saved_c = c; - saved_d = d; - -/* Round 1 */ - STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) - STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) - STEP(F, c, d, a, b, SET(2), 0x242070db, 17) - STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) - STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) - STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) - STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) - STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) - STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) - STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) - STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) - STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) - STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) - STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) - STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) - STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) - -/* Round 2 */ - STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) - STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) - STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) - STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) - STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) - STEP(G, d, a, b, c, GET(10), 0x02441453, 9) - STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) - STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) - STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) - STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) - STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) - STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) - STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) - STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) - STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) - STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) - -/* Round 3 */ - STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) - STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) - STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) - STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) - STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) - STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) - STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) - STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) - STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) - STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) - STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) - STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) - STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) - STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) - STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) - STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) - -/* Round 4 */ - STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) - STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) - STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) - STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) - STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) - STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) - STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) - STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) - STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) - STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) - STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) - STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) - STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) - STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) - STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) - STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) - - a += saved_a; - b += saved_b; - c += saved_c; - d += saved_d; - - ptr += 64; - } while (size -= 64); - - ctx->a = a; - ctx->b = b; - ctx->c = c; - ctx->d = d; - - return ptr; -} - -void MD5_Init(MD5_CTX *ctx) -{ - ctx->a = 0x67452301; - ctx->b = 0xefcdab89; - ctx->c = 0x98badcfe; - ctx->d = 0x10325476; - - ctx->lo = 0; - ctx->hi = 0; -} - -void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) -{ - MD5_u32plus saved_lo; - unsigned long used, available; - - saved_lo = ctx->lo; - if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) - ctx->hi++; - ctx->hi += size >> 29; - - used = saved_lo & 0x3f; - - if (used) { - available = 64 - used; - - if (size < available) { - memcpy(&ctx->buffer[used], data, size); - return; - } - - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; - body(ctx, ctx->buffer, 64); - } - - if (size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; - } - - memcpy(ctx->buffer, data, size); -} - -void MD5_Final(unsigned char *result, MD5_CTX *ctx) -{ - unsigned long used, available; - - used = ctx->lo & 0x3f; - - ctx->buffer[used++] = 0x80; - - available = 64 - used; - - if (available < 8) { - memset(&ctx->buffer[used], 0, available); - body(ctx, ctx->buffer, 64); - used = 0; - available = 64; - } - - memset(&ctx->buffer[used], 0, available - 8); - - ctx->lo <<= 3; - ctx->buffer[56] = ctx->lo; - ctx->buffer[57] = ctx->lo >> 8; - ctx->buffer[58] = ctx->lo >> 16; - ctx->buffer[59] = ctx->lo >> 24; - ctx->buffer[60] = ctx->hi; - ctx->buffer[61] = ctx->hi >> 8; - ctx->buffer[62] = ctx->hi >> 16; - ctx->buffer[63] = ctx->hi >> 24; - - body(ctx, ctx->buffer, 64); - - result[0] = ctx->a; - result[1] = ctx->a >> 8; - result[2] = ctx->a >> 16; - result[3] = ctx->a >> 24; - result[4] = ctx->b; - result[5] = ctx->b >> 8; - result[6] = ctx->b >> 16; - result[7] = ctx->b >> 24; - result[8] = ctx->c; - result[9] = ctx->c >> 8; - result[10] = ctx->c >> 16; - result[11] = ctx->c >> 24; - result[12] = ctx->d; - result[13] = ctx->d >> 8; - result[14] = ctx->d >> 16; - result[15] = ctx->d >> 24; - - memset(ctx, 0, sizeof(*ctx)); -} - -#endif diff --git a/package/utils/oseama/src/md5.h b/package/utils/oseama/src/md5.h deleted file mode 100644 index 2da44bf355a..00000000000 --- a/package/utils/oseama/src/md5.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. - * MD5 Message-Digest Algorithm (RFC 1321). - * - * Homepage: - * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 - * - * Author: - * Alexander Peslyak, better known as Solar Designer - * - * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. - * In case this attempt to disclaim copyright and place the software in the - * public domain is deemed null and void, then the software is - * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the - * general public under the following terms: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted. - * - * There's ABSOLUTELY NO WARRANTY, express or implied. - * - * See md5.c for more information. - */ - -#ifdef HAVE_OPENSSL -#include -#elif !defined(_MD5_H) -#define _MD5_H - -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD5_u32plus; - -typedef struct { - MD5_u32plus lo, hi; - MD5_u32plus a, b, c, d; - unsigned char buffer[64]; - MD5_u32plus block[16]; -} MD5_CTX; - -extern void MD5_Init(MD5_CTX *ctx); -extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); -extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); - -#endif diff --git a/package/utils/oseama/src/oseama.c b/package/utils/oseama/src/oseama.c deleted file mode 100644 index 4434b11162e..00000000000 --- a/package/utils/oseama/src/oseama.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * oseama - * - * Copyright (C) 2016 Rafał Miłecki - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "md5.h" - -#if !defined(__BYTE_ORDER) -#error "Unknown byte order" -#endif - -#if __BYTE_ORDER == __BIG_ENDIAN -#define cpu_to_be32(x) (x) -#define be32_to_cpu(x) (x) -#define cpu_to_be16(x) (x) -#define be16_to_cpu(x) (x) -#elif __BYTE_ORDER == __LITTLE_ENDIAN -#define cpu_to_be32(x) bswap_32(x) -#define be32_to_cpu(x) bswap_32(x) -#define cpu_to_be16(x) bswap_16(x) -#define be16_to_cpu(x) bswap_16(x) -#else -#error "Unsupported endianness" -#endif - -#define SEAMA_MAGIC 0x5ea3a417 - -struct seama_seal_header { - uint32_t magic; - uint16_t reserved; - uint16_t metasize; - uint32_t imagesize; -} __attribute__ ((packed)); - -struct seama_entity_header { - uint32_t magic; - uint16_t reserved; - uint16_t metasize; - uint32_t imagesize; - uint8_t md5[16]; -} __attribute__ ((packed)); - -char *seama_path; -int entity_idx = -1; -char *out_path; - -static inline size_t oseama_min(size_t x, size_t y) { - return x < y ? x : y; -} - -/************************************************** - * Info - **************************************************/ - -static void oseama_info_parse_options(int argc, char **argv) { - int c; - - while ((c = getopt(argc, argv, "e:")) != -1) { - switch (c) { - case 'e': - entity_idx = atoi(optarg); - break; - } - } -} - -static int oseama_info_entities(FILE *seama) { - struct seama_entity_header hdr; - size_t bytes, metasize, imagesize; - uint8_t buf[1024]; - char *end, *tmp; - int i = 0; - int err = 0; - - while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) { - if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { - fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); - err = -EINVAL; - goto err_out; - } - metasize = be16_to_cpu(hdr.metasize); - imagesize = be32_to_cpu(hdr.imagesize); - - if (entity_idx >= 0 && i != entity_idx) { - fseek(seama, metasize + imagesize, SEEK_CUR); - i++; - continue; - } - - if (metasize >= sizeof(buf)) { - fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize); - err = -EINVAL; - goto err_out; - } - - if (entity_idx < 0) - printf("\n"); - printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr)); - printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize); - printf("Meta size:\t%zd\n", metasize); - printf("Image size:\t%zd\n", imagesize); - - bytes = fread(buf, 1, metasize, seama); - if (bytes != metasize) { - fprintf(stderr, "Couldn't read %zd B of meta\n", metasize); - err = -EIO; - goto err_out; - } - - end = (char *)&buf[metasize - 1]; - *end = '\0'; - for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { - printf("Meta entry:\t%s\n", tmp); - } - - fseek(seama, imagesize, SEEK_CUR); - i++; - } - -err_out: - return err; -} - -static int oseama_info(int argc, char **argv) { - FILE *seama; - struct seama_seal_header hdr; - size_t bytes; - uint16_t metasize; - uint32_t imagesize; - uint8_t buf[1024]; - int err = 0; - - if (argc < 3) { - fprintf(stderr, "No Seama file passed\n"); - err = -EINVAL; - goto out; - } - seama_path = argv[2]; - - optind = 3; - oseama_info_parse_options(argc, argv); - - seama = fopen(seama_path, "r"); - if (!seama) { - fprintf(stderr, "Couldn't open %s\n", seama_path); - err = -EACCES; - goto out; - } - - bytes = fread(&hdr, 1, sizeof(hdr), seama); - if (bytes != sizeof(hdr)) { - fprintf(stderr, "Couldn't read %s header\n", seama_path); - err = -EIO; - goto err_close; - } - metasize = be16_to_cpu(hdr.metasize); - imagesize = be32_to_cpu(hdr.imagesize); - - if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { - fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); - err = -EINVAL; - goto err_close; - } - - if (metasize >= sizeof(buf)) { - fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize); - err = -EINVAL; - goto err_close; - } - - if (imagesize) { - fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize); - err = -EINVAL; - goto err_close; - } - - bytes = fread(buf, 1, metasize, seama); - if (bytes != metasize) { - fprintf(stderr, "Couldn't read %d B of meta\n", metasize); - err = -EIO; - goto err_close; - } - - if (entity_idx < 0) { - char *end, *tmp; - - printf("Meta size:\t%d\n", metasize); - printf("Image size:\t%d\n", imagesize); - - end = (char *)&buf[metasize - 1]; - *end = '\0'; - for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { - printf("Meta entry:\t%s\n", tmp); - } - } - - oseama_info_entities(seama); - -err_close: - fclose(seama); -out: - return err; -} - -/************************************************** - * Create - **************************************************/ - -static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) { - FILE *in; - size_t bytes; - ssize_t length = 0; - uint8_t buf[128]; - - in = fopen(in_path, "r"); - if (!in) { - fprintf(stderr, "Couldn't open %s\n", in_path); - return -EACCES; - } - - while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { - if (fwrite(buf, 1, bytes, seama) != bytes) { - fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path); - length = -EIO; - break; - } - length += bytes; - } - - fclose(in); - - return length; -} - -static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) { - uint8_t *buf; - - buf = malloc(length); - if (!buf) - return -ENOMEM; - memset(buf, 0, length); - - if (fwrite(buf, 1, length, seama) != length) { - fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path); - return -EIO; - } - - return length; -} - -static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) { - if (curr_offset & (alignment - 1)) { - size_t length = alignment - (curr_offset % alignment); - - return oseama_entity_append_zeros(seama, length); - } - - return 0; -} - -static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) { - struct seama_entity_header hdr = {}; - uint8_t buf[128]; - size_t length = imagesize; - size_t bytes; - MD5_CTX ctx; - - fseek(seama, sizeof(hdr) + metasize, SEEK_SET); - MD5_Init(&ctx); - while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) { - MD5_Update(&ctx, buf, bytes); - length -= bytes; - } - MD5_Final(hdr.md5, &ctx); - - hdr.magic = cpu_to_be32(SEAMA_MAGIC); - hdr.metasize = cpu_to_be16(metasize); - hdr.imagesize = cpu_to_be32(imagesize); - - fseek(seama, 0, SEEK_SET); - bytes = fwrite(&hdr, 1, sizeof(hdr), seama); - if (bytes != sizeof(hdr)) { - fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path); - return -EIO; - } - - return 0; -} - -static int oseama_entity(int argc, char **argv) { - FILE *seama; - ssize_t sbytes; - size_t curr_offset = sizeof(struct seama_entity_header); - size_t metasize = 0, imagesize = 0; - int c; - int err = 0; - - if (argc < 3) { - fprintf(stderr, "No Seama file passed\n"); - err = -EINVAL; - goto out; - } - seama_path = argv[2]; - - seama = fopen(seama_path, "w+"); - if (!seama) { - fprintf(stderr, "Couldn't open %s\n", seama_path); - err = -EACCES; - goto out; - } - fseek(seama, curr_offset, SEEK_SET); - - optind = 3; - while ((c = getopt(argc, argv, "m:f:b:")) != -1) { - switch (c) { - case 'm': - sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama); - if (sbytes < 0) { - fprintf(stderr, "Failed to write meta %s\n", optarg); - } else { - curr_offset += sbytes; - metasize += sbytes; - } - - sbytes = oseama_entity_align(seama, curr_offset, 4); - if (sbytes < 0) { - fprintf(stderr, "Failed to append zeros\n"); - } else { - curr_offset += sbytes; - metasize += sbytes; - } - - break; - case 'f': - case 'b': - break; - } - } - - optind = 3; - while ((c = getopt(argc, argv, "m:f:b:")) != -1) { - switch (c) { - case 'm': - break; - case 'f': - sbytes = oseama_entity_append_file(seama, optarg); - if (sbytes < 0) { - fprintf(stderr, "Failed to append file %s\n", optarg); - } else { - curr_offset += sbytes; - imagesize += sbytes; - } - break; - case 'b': - sbytes = strtol(optarg, NULL, 0) - curr_offset; - if (sbytes < 0) { - fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0)); - } else { - sbytes = oseama_entity_append_zeros(seama, sbytes); - if (sbytes < 0) { - fprintf(stderr, "Failed to append zeros\n"); - } else { - curr_offset += sbytes; - imagesize += sbytes; - } - } - break; - } - if (err) - break; - } - - oseama_entity_write_hdr(seama, metasize, imagesize); - - fclose(seama); -out: - return err; -} - -/************************************************** - * Extract - **************************************************/ - -static void oseama_extract_parse_options(int argc, char **argv) { - int c; - - while ((c = getopt(argc, argv, "e:o:")) != -1) { - switch (c) { - case 'e': - entity_idx = atoi(optarg); - break; - case 'o': - out_path = optarg; - break; - } - } -} - -static int oseama_extract_entity(FILE *seama, FILE *out) { - struct seama_entity_header hdr; - size_t bytes, metasize, imagesize, length; - uint8_t buf[1024]; - int i = 0; - int err = 0; - - while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) { - if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { - fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); - err = -EINVAL; - break; - } - metasize = be16_to_cpu(hdr.metasize); - imagesize = be32_to_cpu(hdr.imagesize); - - if (i != entity_idx) { - fseek(seama, metasize + imagesize, SEEK_CUR); - i++; - continue; - } - - fseek(seama, -sizeof(hdr), SEEK_CUR); - - length = sizeof(hdr) + metasize + imagesize; - while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) { - if (fwrite(buf, 1, bytes, out) != bytes) { - fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); - err = -EIO; - break; - } - length -= bytes; - } - - if (length) { - fprintf(stderr, "Couldn't extract whole entity %d from %s (%zu B left)\n", entity_idx, seama_path, length); - err = -EIO; - break; - } - - break; - } - - return err; -} - -static int oseama_extract(int argc, char **argv) { - FILE *seama; - FILE *out; - struct seama_seal_header hdr; - size_t bytes; - uint16_t metasize; - int err = 0; - - if (argc < 3) { - fprintf(stderr, "No Seama file passed\n"); - err = -EINVAL; - goto out; - } - seama_path = argv[2]; - - optind = 3; - oseama_extract_parse_options(argc, argv); - if (entity_idx < 0) { - fprintf(stderr, "No entity specified\n"); - err = -EINVAL; - goto out; - } else if (!out_path) { - fprintf(stderr, "No output file specified\n"); - err = -EINVAL; - goto out; - } - - seama = fopen(seama_path, "r"); - if (!seama) { - fprintf(stderr, "Couldn't open %s\n", seama_path); - err = -EACCES; - goto out; - } - - out = fopen(out_path, "w"); - if (!out) { - fprintf(stderr, "Couldn't open %s\n", out_path); - err = -EACCES; - goto err_close_seama; - } - - bytes = fread(&hdr, 1, sizeof(hdr), seama); - if (bytes != sizeof(hdr)) { - fprintf(stderr, "Couldn't read %s header\n", seama_path); - err = -EIO; - goto err_close_out; - } - metasize = be16_to_cpu(hdr.metasize); - - fseek(seama, metasize, SEEK_CUR); - - oseama_extract_entity(seama, out); - -err_close_out: - fclose(out); -err_close_seama: - fclose(seama); -out: - return err; -} - -/************************************************** - * Start - **************************************************/ - -static void usage() { - printf("Usage:\n"); - printf("\n"); - printf("Info about Seama seal (container):\n"); - printf("\toseama info [options]\n"); - printf("\t-e\t\t\t\tprint info about specified entity only\n"); - printf("\n"); - printf("Create Seama entity:\n"); - printf("\toseama entity [options]\n"); - printf("\t-m meta\t\t\t\tmeta into to put in header\n"); - printf("\t-f file\t\t\t\tappend content from file\n"); - printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n"); - printf("\n"); - printf("Extract from Seama seal (container):\n"); - printf("\toseama extract [options]\n"); - printf("\t-e\t\t\t\tindex of entity to extract\n"); - printf("\t-o file\t\t\t\toutput file\n"); -} - -int main(int argc, char **argv) { - if (argc > 1) { - if (!strcmp(argv[1], "info")) - return oseama_info(argc, argv); - else if (!strcmp(argv[1], "entity")) - return oseama_entity(argc, argv); - else if (!strcmp(argv[1], "extract")) - return oseama_extract(argc, argv); - } - - usage(); - return 0; -} diff --git a/package/utils/otrx/Makefile b/package/utils/otrx/Makefile deleted file mode 100644 index 35c59e27301..00000000000 --- a/package/utils/otrx/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright (C) 2015 Rafał Miłecki -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=otrx -PKG_RELEASE:=1 - -PKG_SOURCE_PROTO:=git -PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git -PKG_SOURCE_DATE:=2021-12-02 -PKG_SOURCE_VERSION:=56e8e19151743c923f48604c457850cf8eb52076 -PKG_MIRROR_HASH:=2a40ac73e8eab0a7a4474cb331b8e2fc972635314b0b5e02a9f2b9a32c5d5f3b - -include $(INCLUDE_DIR)/package.mk - -define Package/otrx - SECTION:=utils - CATEGORY:=Base system - TITLE:=Utility for opening (analyzing) TRX firmware images - MAINTAINER:=Rafał Miłecki - DEPENDS:=@(TARGET_bcm47xx||TARGET_bcm53xx) -endef - -define Package/otrx/description - This package contains an utility that allows validating TRX images. -endef - -TARGET_CFLAGS += -Wall - -define Build/Compile - $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) \ - -o $(PKG_BUILD_DIR)/otrx \ - $(PKG_BUILD_DIR)/src/otrx.c -endef - -define Package/otrx/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/otrx $(1)/usr/bin/ -endef - -$(eval $(call BuildPackage,otrx)) diff --git a/scripts/gen_image_generic.sh b/scripts/gen_image_generic.sh index 5c88dcea80c..11e40f38868 100755 --- a/scripts/gen_image_generic.sh +++ b/scripts/gen_image_generic.sh @@ -9,8 +9,10 @@ fi OUTPUT="$1" KERNELSIZE="$2" KERNELDIR="$3" +KERNELPARTTYPE=${KERNELPARTTYPE:-83} ROOTFSSIZE="$4" ROOTFSIMAGE="$5" +ROOTFSPARTTYPE=${ROOTFSPARTTYPE:-83} ALIGN="$6" rm -f "$OUTPUT" @@ -19,7 +21,7 @@ head=16 sect=63 # create partition table -set $(ptgen -o "$OUTPUT" -h $head -s $sect ${GUID:+-g} -p "${KERNELSIZE}m${PARTOFFSET:+@$PARTOFFSET}" -p "${ROOTFSSIZE}m" ${ALIGN:+-l $ALIGN} ${SIGNATURE:+-S 0x$SIGNATURE} ${GUID:+-G $GUID}) +set $(ptgen -o "$OUTPUT" -h $head -s $sect ${GUID:+-g} -t "${KERNELPARTTYPE}" -p "${KERNELSIZE}m${PARTOFFSET:+@$PARTOFFSET}" -t "${ROOTFSPARTTYPE}" -p "${ROOTFSSIZE}m" ${ALIGN:+-l $ALIGN} ${SIGNATURE:+-S 0x$SIGNATURE} ${GUID:+-G $GUID}) KERNELOFFSET="$(($1 / 512))" KERNELSIZE="$2" diff --git a/target/Config.in b/target/Config.in index a6b3351a618..ac0f1f9826b 100644 --- a/target/Config.in +++ b/target/Config.in @@ -184,6 +184,10 @@ config powerpc64 select ARCH_64BIT bool +config riscv64 + select ARCH_64BIT + bool + config sh3 bool @@ -223,6 +227,7 @@ config ARCH default "mips64el" if mips64el default "powerpc" if powerpc default "powerpc64" if powerpc64 + default "riscv64" if riscv64 default "sh3" if sh3 default "sh3eb" if sh3eb default "sh4" if sh4 diff --git a/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch b/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch index 8df11630c75..6c21666ed11 100644 --- a/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch +++ b/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch @@ -25,7 +25,7 @@ obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o --- /dev/null +++ b/drivers/spi/spi-en7523.c -@@ -0,0 +1,311 @@ +@@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -166,6 +166,7 @@ + } +} + ++#if 0 +static void set_spi_clock_speed(int freq_mhz) +{ + u32 tmp, val; @@ -178,6 +179,7 @@ + tmp |= (val << 8) | 1; + writel(tmp, ENSPI_CLOCK_DIVIDER); +} ++#endif + +static void init_hw(void) +{ diff --git a/target/linux/apm821xx/Makefile b/target/linux/apm821xx/Makefile index bb0ee9b5b19..e5e2841fd34 100644 --- a/target/linux/apm821xx/Makefile +++ b/target/linux/apm821xx/Makefile @@ -10,6 +10,7 @@ FEATURES:=fpu dt gpio ramdisk squashfs usb SUBTARGETS:=nand sata KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 define Target/Description Build images for AppliedMicro APM821xx based boards. @@ -20,6 +21,7 @@ include $(INCLUDE_DIR)/target.mk KERNELNAME:=uImage DEFAULT_PACKAGES += \ - kmod-leds-gpio kmod-i2c-core kmod-gpio-button-hotplug uboot-envtools + kmod-leds-gpio kmod-i2c-core kmod-gpio-button-hotplug uboot-envtools \ + kmod-hw-crypto-4xx $(eval $(call BuildTarget)) diff --git a/target/linux/apm821xx/config-5.15 b/target/linux/apm821xx/config-5.15 index 2af8110553f..adcccbe567c 100644 --- a/target/linux/apm821xx/config-5.15 +++ b/target/linux/apm821xx/config-5.15 @@ -39,15 +39,11 @@ CONFIG_CRC16=y # CONFIG_CRC32_SARWATE is not set CONFIG_CRC32_SLICEBY8=y CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_PPC4XX=y -CONFIG_CRYPTO_HW=y CONFIG_CRYPTO_JITTERENTROPY=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 CONFIG_CRYPTO_LZO=y # CONFIG_CRYPTO_MD5_PPC is not set -CONFIG_CRYPTO_RNG=y -CONFIG_CRYPTO_RNG2=y # CONFIG_CRYPTO_SHA1_PPC is not set CONFIG_DATA_SHIFT=12 CONFIG_DMADEVICES=y @@ -96,8 +92,6 @@ CONFIG_GPIO_GENERIC_PLATFORM=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_PPC4XX=y CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y diff --git a/target/linux/apm821xx/config-6.1 b/target/linux/apm821xx/config-6.1 new file mode 100644 index 00000000000..9e51e94a2f5 --- /dev/null +++ b/target/linux/apm821xx/config-6.1 @@ -0,0 +1,250 @@ +# CONFIG_40x is not set +# CONFIG_440_CPU is not set +CONFIG_44x=y +CONFIG_464_CPU=y +CONFIG_4xx=y +CONFIG_4xx_SOC=y +# CONFIG_ADVANCED_OPTIONS is not set +CONFIG_APM821xx=y +# CONFIG_APOLLO3G is not set +# CONFIG_ARCHES is not set +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_ARCH_MMAP_RND_BITS=11 +CONFIG_ARCH_MMAP_RND_BITS_MAX=17 +CONFIG_ARCH_MMAP_RND_BITS_MIN=11 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=17 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_SPLIT_ARG64=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WEAK_RELEASE_ACQUIRE=y +CONFIG_AUDIT_ARCH=y +# CONFIG_BAMBOO is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_MQ_PCI=y +CONFIG_BLUESTONE=y +CONFIG_BOOKE=y +CONFIG_BOOKE_OR_40x=y +CONFIG_BOOKE_WDT=y +# CONFIG_CANYONLANDS is not set +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs noinitrd" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_MD5_PPC is not set +# CONFIG_CRYPTO_SHA1_PPC is not set +CONFIG_DATA_SHIFT=12 +CONFIG_DMADEVICES=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DTC=y +CONFIG_DW_DMAC=y +CONFIG_DW_DMAC_CORE=y +CONFIG_EARLY_PRINTK=y +# CONFIG_EBONY is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +# CONFIG_EIGER is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXTRA_TARGETS="uImage" +CONFIG_FIXED_PHY=y +CONFIG_FORCE_PCI=y +# CONFIG_FSL_LBC is not set +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +# CONFIG_GEN_RTC is not set +# CONFIG_GLACIER is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_IBM_IIC=y +CONFIG_IBM_EMAC=y +CONFIG_IBM_EMAC_EMAC4=y +CONFIG_IBM_EMAC_POLL_WEIGHT=32 +CONFIG_IBM_EMAC_RGMII=y +CONFIG_IBM_EMAC_RXB=128 +CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256 +CONFIG_IBM_EMAC_TAH=y +CONFIG_IBM_EMAC_TXB=128 +# CONFIG_ICON is not set +CONFIG_ILLEGAL_POINTER_VALUE=0 +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_ISA_DMA_API=y +# CONFIG_JFFS2_FS is not set +# CONFIG_KATMAI is not set +CONFIG_KERNEL_START=0xc0000000 +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_PATTERN=y +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOWMEM_SIZE=0x30000000 +# CONFIG_MATH_EMULATION is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MIGRATION=y +CONFIG_MMU_GATHER_PAGE_SIZE=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_SELFTESTS=y +CONFIG_NOT_COHERENT_CACHE=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=1 +CONFIG_NR_IRQS=512 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND=y +CONFIG_PACKING=y +CONFIG_PAGE_OFFSET=0xc0000000 +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_ARCH_FALLBACKS=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYSICAL_START=0x00000000 +CONFIG_PHYS_64BIT=y +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PMU_SYSFS is not set +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_PPC44x_SIMPLE=y +CONFIG_PPC4xx_GPIO=y +CONFIG_PPC4xx_PCI_EXPRESS=y +# CONFIG_PPC64 is not set +# CONFIG_PPC_47x is not set +# CONFIG_PPC_85xx is not set +# CONFIG_PPC_8xx is not set +CONFIG_PPC_ADV_DEBUG_DACS=2 +CONFIG_PPC_ADV_DEBUG_DAC_RANGE=y +CONFIG_PPC_ADV_DEBUG_DVCS=2 +CONFIG_PPC_ADV_DEBUG_IACS=4 +CONFIG_PPC_ADV_DEBUG_REGS=y +# CONFIG_PPC_BOOK3S_32 is not set +CONFIG_PPC_DCR=y +CONFIG_PPC_DCR_NATIVE=y +# CONFIG_PPC_EARLY_DEBUG is not set +CONFIG_PPC_FPU=y +CONFIG_PPC_FPU_REGS=y +CONFIG_PPC_INDIRECT_PCI=y +CONFIG_PPC_KUAP=y +# CONFIG_PPC_KUAP_DEBUG is not set +CONFIG_PPC_KUEP=y +CONFIG_PPC_MMU_NOHASH=y +CONFIG_PPC_PAGE_SHIFT=12 +# CONFIG_PPC_PCI_BUS_NUM_DOMAIN_DEPENDENT is not set +CONFIG_PPC_UDBG_16550=y +CONFIG_PPC_WERROR=y +CONFIG_PTE_64BIT=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +# CONFIG_RAINIER is not set +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGULATOR=y +CONFIG_RSEQ=y +# CONFIG_SAM440EP is not set +# CONFIG_SCOM_DEBUGFS is not set +# CONFIG_SEQUOIA is not set +# CONFIG_SERIAL_8250_FSL is not set +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SPARSE_IRQ=y +CONFIG_SRCU=y +# CONFIG_STATIC_CALL_SELFTEST is not set +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +# CONFIG_TAISHAN is not set +CONFIG_TARGET_CPU="464" +CONFIG_TARGET_CPU_BOOL=y +CONFIG_TASK_SIZE=0xc0000000 +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_THREAD_SHIFT=13 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TINY_SRCU=y +# CONFIG_TOOLCHAIN_DEFAULT_CPU is not set +CONFIG_USB_SUPPORT=y +CONFIG_VDSO32=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set +# CONFIG_WARP is not set +CONFIG_WATCHDOG_CORE=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_POWERPC=y +# CONFIG_YOSEMITE is not set +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/apm821xx/dts/meraki-mx60.dts b/target/linux/apm821xx/dts/meraki-mx60.dts index 33f9a3d9f78..0e343ed7825 100644 --- a/target/linux/apm821xx/dts/meraki-mx60.dts +++ b/target/linux/apm821xx/dts/meraki-mx60.dts @@ -47,8 +47,6 @@ status = "okay"; nand { - nand-is-boot-medium; - partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/apm821xx/image/nand.mk b/target/linux/apm821xx/image/nand.mk index 2848b87719e..2b8abfc22fd 100644 --- a/target/linux/apm821xx/image/nand.mk +++ b/target/linux/apm821xx/image/nand.mk @@ -100,7 +100,7 @@ define Device/netgear_wndr4700 kmod-dm kmod-fs-ext4 kmod-fs-vfat kmod-usb-ledtrig-usbport \ kmod-md-mod kmod-nls-cp437 kmod-nls-iso8859-1 kmod-nls-iso8859-15 \ kmod-nls-utf8 kmod-usb3 kmod-usb-dwc2 kmod-usb-storage \ - partx-utils + partx-utils kmod-ata-dwc BOARD_NAME := wndr4700 PAGESIZE := 2048 SUBPAGESIZE := 512 diff --git a/target/linux/apm821xx/modules.mk b/target/linux/apm821xx/modules.mk new file mode 100644 index 00000000000..f8880d67124 --- /dev/null +++ b/target/linux/apm821xx/modules.mk @@ -0,0 +1,34 @@ +define KernelPackage/ata-dwc + TITLE:=DesignWare Cores SATA support + KCONFIG:=CONFIG_SATA_DWC + FILES:=$(LINUX_DIR)/drivers/ata/sata_dwc_460ex.ko + AUTOLOAD:=$(call AutoLoad,40,sata_dwc_460ex,1) + $(call AddDepends/ata) +endef + +define KernelPackage/ata-dwc/description + Platform support for the on-chip SATA controller. +endef + +$(eval $(call KernelPackage,ata-dwc)) + +define KernelPackage/hw-crypto-4xx + TITLE:=Driver AMCC PPC4xx crypto accelerator + KCONFIG:= \ + CONFIG_CRYPTO_HW=y \ + CONFIG_HW_RANDOM=y \ + CONFIG_CRYPTO_DEV_PPC4XX \ + CONFIG_HW_RANDOM_PPC4XX=y + DEPENDS:=@TARGET_apm821xx +kmod-random-core +kmod-crypto-manager \ + +kmod-crypto-ccm +kmod-crypto-gcm \ + +kmod-crypto-sha1 +kmod-crypto-sha256 +kmod-crypto-sha512 + FILES:=$(LINUX_DIR)/drivers/crypto/amcc/crypto4xx.ko + AUTOLOAD:=$(call AutoLoad,09,sata_dwc_460ex,1) + $(call AddDepends/crypto) +endef + +define KernelPackage/hw-crypto-4xx/description + Platform support for the on-chip crypto acceleration. +endef + +$(eval $(call KernelPackage,hw-crypto-4xx)) diff --git a/target/linux/apm821xx/nand/config-default b/target/linux/apm821xx/nand/config-default index a8ab4fd8920..7070b34b93d 100644 --- a/target/linux/apm821xx/nand/config-default +++ b/target/linux/apm821xx/nand/config-default @@ -2,14 +2,8 @@ CONFIG_AT803X_PHY=y CONFIG_AR8216_PHY=y # CONFIG_SATA_DWC_OLD_DMA is not set CONFIG_IKAREM=y -CONFIG_ATA=y -CONFIG_ATA_SFF=y -CONFIG_ATA_BMDMA=y # CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set -CONFIG_SATA_PMP=y CONFIG_GENERIC_PHY=y -CONFIG_SATA_DWC=y -# CONFIG_SATA_DWC_DEBUG is not set CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_GPIO_GENERIC=y diff --git a/target/linux/apm821xx/patches-6.1/201-add-amcc-apollo3g-support.patch b/target/linux/apm821xx/patches-6.1/201-add-amcc-apollo3g-support.patch new file mode 100644 index 00000000000..d26e74dfb71 --- /dev/null +++ b/target/linux/apm821xx/patches-6.1/201-add-amcc-apollo3g-support.patch @@ -0,0 +1,30 @@ +--- a/arch/powerpc/platforms/44x/Kconfig ++++ b/arch/powerpc/platforms/44x/Kconfig +@@ -118,6 +118,17 @@ config CANYONLANDS + help + This option enables support for the AMCC PPC460EX evaluation board. + ++config APOLLO3G ++ bool "Apollo3G" ++ depends on 44x ++ default n ++ select PPC44x_SIMPLE ++ select APM821xx ++ select IBM_EMAC_RGMII ++ select 460EX ++ help ++ This option enables support for the AMCC Apollo 3G board. ++ + config GLACIER + bool "Glacier" + depends on 44x +--- a/arch/powerpc/platforms/44x/ppc44x_simple.c ++++ b/arch/powerpc/platforms/44x/ppc44x_simple.c +@@ -46,6 +46,7 @@ machine_device_initcall(ppc44x_simple, p + * board.c file for it rather than adding it to this list. + */ + static char *board[] __initdata = { ++ "amcc,apollo3g", + "amcc,arches", + "amcc,bamboo", + "apm,bluestone", diff --git a/target/linux/apm821xx/patches-6.1/300-fix-atheros-nics-on-apm82181.patch b/target/linux/apm821xx/patches-6.1/300-fix-atheros-nics-on-apm82181.patch new file mode 100644 index 00000000000..706f86d0cfe --- /dev/null +++ b/target/linux/apm821xx/patches-6.1/300-fix-atheros-nics-on-apm82181.patch @@ -0,0 +1,51 @@ +--- a/arch/powerpc/platforms/4xx/pci.c ++++ b/arch/powerpc/platforms/4xx/pci.c +@@ -1061,15 +1061,24 @@ static int __init apm821xx_pciex_init_po + u32 val; + + /* +- * Do a software reset on PCIe ports. +- * This code is to fix the issue that pci drivers doesn't re-assign +- * bus number for PCIE devices after Uboot +- * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 +- * PT quad port, SAS LSI 1064E) ++ * Only reset the PHY when no link is currently established. ++ * This is for the Atheros PCIe board which has problems to establish ++ * the link (again) after this PHY reset. All other currently tested ++ * PCIe boards don't show this problem. + */ +- +- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); +- mdelay(10); ++ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); ++ if (!(val & 0x00001000)) { ++ /* ++ * Do a software reset on PCIe ports. ++ * This code is to fix the issue that pci drivers doesn't re-assign ++ * bus number for PCIE devices after Uboot ++ * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 ++ * PT quad port, SAS LSI 1064E) ++ */ ++ ++ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); ++ mdelay(10); ++ } + + if (port->endpoint) + val = PTYPE_LEGACY_ENDPOINT << 20; +@@ -1086,9 +1095,12 @@ static int __init apm821xx_pciex_init_po + mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); + mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); + +- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); +- mdelay(50); +- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); ++ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); ++ if (!(val & 0x00001000)) { ++ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); ++ mdelay(50); ++ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); ++ } + + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, + mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | diff --git a/target/linux/apm821xx/patches-6.1/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-6.1/301-fix-memory-map-wndr4700.patch new file mode 100644 index 00000000000..0c9fb370f1c --- /dev/null +++ b/target/linux/apm821xx/patches-6.1/301-fix-memory-map-wndr4700.patch @@ -0,0 +1,14 @@ +--- a/arch/powerpc/platforms/4xx/pci.c ++++ b/arch/powerpc/platforms/4xx/pci.c +@@ -1903,9 +1903,9 @@ static void __init ppc4xx_configure_pcie + * if it works + */ + out_le32(mbase + PECFG_PIM0LAL, 0x00000000); +- out_le32(mbase + PECFG_PIM0LAH, 0x00000000); ++ out_le32(mbase + PECFG_PIM0LAH, 0x00000008); + out_le32(mbase + PECFG_PIM1LAL, 0x00000000); +- out_le32(mbase + PECFG_PIM1LAH, 0x00000000); ++ out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); + out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); + out_le32(mbase + PECFG_PIM01SAL, 0x00000000); + diff --git a/target/linux/apm821xx/patches-6.1/900-powerpc-bootwrapper-force-gzip-as-mkimage-s-compress.patch b/target/linux/apm821xx/patches-6.1/900-powerpc-bootwrapper-force-gzip-as-mkimage-s-compress.patch new file mode 100644 index 00000000000..2e2bda7c60f --- /dev/null +++ b/target/linux/apm821xx/patches-6.1/900-powerpc-bootwrapper-force-gzip-as-mkimage-s-compress.patch @@ -0,0 +1,29 @@ +From c9395ad54e2cabb87d408becc37566f3d8248933 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Sun, 1 Dec 2019 02:08:23 +0100 +Subject: [PATCH] powerpc: bootwrapper: force gzip as mkimage's compression + method + +Due to CONFIG_KERNEL_XZ symbol, the bootwrapper code tries to +instruct the mkimage to use the xz compression, which isn't +supported. This patch forces the gzip compression, which is +supported and doesn't matter because the generated uImage for +the apm821xx target gets ignored as the OpenWrt toolchain will +do separate U-Boot kernel images for each device individually. + +Signed-off-by: Christian Lamparter +--- + arch/powerpc/boot/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/boot/Makefile ++++ b/arch/powerpc/boot/Makefile +@@ -272,7 +272,7 @@ compressor-$(CONFIG_KERNEL_LZO) := lzo + + # args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd + quiet_cmd_wrap = WRAP $@ +- cmd_wrap =$(CONFIG_SHELL) $(wrapper) -Z $(compressor-y) -c -o $@ -p $2 \ ++ cmd_wrap =$(CONFIG_SHELL) $(wrapper) -Z gzip -c -o $@ -p $2 \ + $(CROSSWRAP) $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) \ + vmlinux + diff --git a/target/linux/apm821xx/sata/config-default b/target/linux/apm821xx/sata/config-default index 70a1ac760d4..daff2d04bef 100644 --- a/target/linux/apm821xx/sata/config-default +++ b/target/linux/apm821xx/sata/config-default @@ -40,12 +40,14 @@ CONFIG_PM=y CONFIG_PM_AUTOSLEEP=y CONFIG_PM_CLK=y CONFIG_PM_SLEEP=y +# CONFIG_PM_USERSPACE_AUTOSLEEP is not set CONFIG_PM_WAKELOCKS=y CONFIG_PM_WAKELOCKS_GC=y CONFIG_PM_WAKELOCKS_LIMIT=100 CONFIG_PPC_EARLY_DEBUG=y CONFIG_PPC_EARLY_DEBUG_44x=y # CONFIG_PPC_EARLY_DEBUG_MEMCONS is not set +# CONFIG_PPC_EARLY_DEBUG_16550 is not set CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH=0x4 CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0xef600300 CONFIG_PPC4xx_CPM=y diff --git a/target/linux/armvirt/32/config-6.1 b/target/linux/armvirt/32/config-6.1 new file mode 100644 index 00000000000..664ef2e05b2 --- /dev/null +++ b/target/linux/armvirt/32/config-6.1 @@ -0,0 +1,78 @@ +CONFIG_ALIGNMENT_TRAP=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MULTIPLATFORM=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_VIRT=y +CONFIG_ARM=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_HEAVY_MB=y +# CONFIG_ARM_HIGHBANK_CPUIDLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_LPAE=y +CONFIG_ARM_PATCH_IDIV=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_PSCI=y +CONFIG_ARM_THUMB=y +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_AUTO_ZRELADDR=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_CACHE_L2X0=y +CONFIG_COMPAT_32BIT_TIME=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_HAS_ASID=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_SPECTRE=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DMA_OPS=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_VDSO_32=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HAVE_SMP=y +CONFIG_HZ_FIXED=0 +CONFIG_HZ_PERIODIC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_NEON=y +CONFIG_NR_CPUS=4 +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PERF_USE_VMALLOC=y +CONFIG_RTC_MC146818_LIB=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SMP_ON_UP=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNWINDER_ARM=y +CONFIG_USE_OF=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 diff --git a/target/linux/armvirt/32/target.mk b/target/linux/armvirt/32/target.mk index 8d42a14b7cf..df220402411 100644 --- a/target/linux/armvirt/32/target.mk +++ b/target/linux/armvirt/32/target.mk @@ -1,6 +1,6 @@ ARCH:=arm SUBTARGET:=32 -BOARDNAME:=QEMU ARM Virtual Machine (cortex-a15) +BOARDNAME:=32-bit ARM QEMU Virtual Machine CPU_TYPE:=cortex-a15 CPU_SUBTYPE:=neon-vfpv4 KERNELNAME:=zImage diff --git a/target/linux/armvirt/64/config-6.1 b/target/linux/armvirt/64/config-6.1 new file mode 100644 index 00000000000..f1b4838d153 --- /dev/null +++ b/target/linux/armvirt/64/config-6.1 @@ -0,0 +1,444 @@ +CONFIG_64BIT=y +CONFIG_ACPI_PCC=y +CONFIG_ARCH_HISI=y +CONFIG_ARCH_INTEL_SOCFPGA=y +CONFIG_ARCH_LAYERSCAPE=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_MVEBU=y +CONFIG_ARCH_NXP=y +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUNXI=y +CONFIG_ARCH_SYNQUACER=y +CONFIG_ARCH_THUNDER=y +CONFIG_ARCH_THUNDER2=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_ZYNQMP=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CRYPTO=y +CONFIG_ARM64_EPAN=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_SME=y +CONFIG_ARM64_SVE=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=48 +CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +# CONFIG_ARMADA_37XX_RWTM_MBOX is not set +CONFIG_ARMADA_37XX_WATCHDOG=y +CONFIG_ARMADA_THERMAL=y +CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y +# CONFIG_ARM_DMC620_PMU is not set +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ARM_SBSA_WATCHDOG=y +CONFIG_ARM_SMC_WATCHDOG=y +CONFIG_ARM_SMMU=y +# CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT is not set +# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set +CONFIG_ARM_SMMU_V3=y +# CONFIG_ARM_SMMU_V3_PMU is not set +# CONFIG_ARM_SMMU_V3_SVA is not set +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +# CONFIG_AXI_DMAC is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BLK_PM=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLK_INTEL_SOCFPGA=y +CONFIG_CLK_INTEL_SOCFPGA64=y +CONFIG_CLK_LS1028A_PLLDIG=y +CONFIG_CLK_PX30=y +CONFIG_CLK_QORIQ=y +CONFIG_CLK_RK3308=y +CONFIG_CLK_RK3328=y +CONFIG_CLK_RK3368=y +CONFIG_CLK_RK3399=y +CONFIG_CLK_RK3568=y +CONFIG_CLK_SP810=y +CONFIG_CLK_SUNXI=y +CONFIG_CLK_SUNXI_CLOCKS=y +# CONFIG_CLK_SUNXI_PRCM_SUN6I is not set +# CONFIG_CLK_SUNXI_PRCM_SUN8I is not set +# CONFIG_CLK_SUNXI_PRCM_SUN9I is not set +CONFIG_CLK_VEXPRESS_OSC=y +# CONFIG_COMMON_CLK_FSL_FLEXSPI is not set +# CONFIG_COMMON_CLK_FSL_SAI is not set +CONFIG_COMMON_CLK_HI3516CV300=y +CONFIG_COMMON_CLK_HI3519=y +CONFIG_COMMON_CLK_HI3559A=y +CONFIG_COMMON_CLK_HI3660=y +CONFIG_COMMON_CLK_HI3670=y +CONFIG_COMMON_CLK_HI3798CV200=y +CONFIG_COMMON_CLK_HI6220=y +CONFIG_COMMON_CLK_HI655X=y +CONFIG_COMMON_CLK_ROCKCHIP=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_ZYNQMP=y +CONFIG_COMMON_RESET_HI3660=y +CONFIG_COMMON_RESET_HI6220=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_PM=y +CONFIG_CRYPTO_AES_ARM64=y +CONFIG_CRYPTO_AES_ARM64_BS=y +CONFIG_CRYPTO_AES_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_CHACHA20=y +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_CRYPTD=y +# CONFIG_CRYPTO_DEV_ALLWINNER is not set +# CONFIG_CRYPTO_DEV_FSL_DPAA2_CAAM is not set +# CONFIG_CRYPTO_DEV_HISI_HPRE is not set +# CONFIG_CRYPTO_DEV_HISI_SEC2 is not set +# CONFIG_CRYPTO_DEV_HISI_TRNG is not set +# CONFIG_CRYPTO_DEV_OCTEONTX2_CPT is not set +# CONFIG_CRYPTO_DEV_ROCKCHIP is not set +# CONFIG_CRYPTO_DEV_ZYNQMP_AES is not set +# CONFIG_CRYPTO_DEV_ZYNQMP_SHA3 is not set +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y +CONFIG_CRYPTO_POLYVAL_ARM64_CE=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA256_ARM64=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64=y +CONFIG_CRYPTO_SIMD=y +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DMA_SUN6I=y +CONFIG_DRM=y +CONFIG_DRM_BOCHS=y +CONFIG_DRM_BRIDGE=y +CONFIG_DRM_GEM_SHMEM_HELPER=y +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_PANEL=y +CONFIG_DRM_PANEL_BRIDGE=y +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y +CONFIG_DRM_QXL=y +# CONFIG_DRM_ROCKCHIP is not set +CONFIG_DRM_TTM=y +CONFIG_DRM_TTM_HELPER=y +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_DRM_VRAM_HELPER=y +# CONFIG_DWMAC_SUN8I is not set +# CONFIG_DWMAC_SUNXI is not set +CONFIG_DW_WATCHDOG=y +CONFIG_FB=y +CONFIG_FB_ARMCLCD=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_CMDLINE=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_XILINX is not set +CONFIG_FRAME_POINTER=y +CONFIG_FRAME_WARN=2048 +# CONFIG_FSL_DPAA is not set +# CONFIG_FSL_DPAA2_QDMA is not set +CONFIG_FSL_ERRATUM_A008585=y +# CONFIG_FSL_IMX8_DDR_PMU is not set +# CONFIG_FSL_PQ_MDIO is not set +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +# CONFIG_GIANFAR is not set +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_MPC8XXX=y +CONFIG_GPIO_ROCKCHIP=y +CONFIG_GPIO_THUNDERX=y +CONFIG_GPIO_XLP=y +CONFIG_GPIO_ZYNQ=y +CONFIG_GPIO_ZYNQMP_MODEPIN=y +CONFIG_HDMI=y +CONFIG_HI3660_MBOX=y +CONFIG_HI6220_MBOX=y +CONFIG_HISILICON_LPC=y +CONFIG_HISI_PMU=y +CONFIG_HISI_THERMAL=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y +# CONFIG_HW_RANDOM_HISI is not set +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_ALTERA=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_HIX5HD2 is not set +CONFIG_I2C_IMX=y +# CONFIG_I2C_SLAVE_TESTUNIT is not set +CONFIG_I2C_SYNQUACER=y +CONFIG_I2C_THUNDERX=y +# CONFIG_I2C_XLP9XX is not set +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +# CONFIG_IMX2_WDT is not set +# CONFIG_INPUT_HISI_POWERKEY is not set +# CONFIG_INPUT_IBM_PANEL is not set +# CONFIG_INTEL_STRATIX10_RSU is not set +# CONFIG_INTEL_STRATIX10_SERVICE is not set +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set +CONFIG_IOMMU_DEFAULT_DMA_STRICT=y +CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_DART is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set +CONFIG_IOMMU_SUPPORT=y +# CONFIG_K3_DMA is not set +CONFIG_KCMP=y +# CONFIG_KEYBOARD_SUN4I_LRADC is not set +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +# CONFIG_MAILBOX_TEST is not set +CONFIG_MARVELL_10G_PHY=y +# CONFIG_MARVELL_CN10K_DDR_PMU is not set +# CONFIG_MARVELL_CN10K_TAD_PMU is not set +CONFIG_MDIO_SUN4I=y +# CONFIG_MFD_ALTERA_A10SR is not set +CONFIG_MFD_ALTERA_SYSMGR=y +# CONFIG_MFD_AXP20X_RSB is not set +CONFIG_MFD_CORE=y +CONFIG_MFD_HI655X_PMIC=y +# CONFIG_MFD_KHADAS_MCU is not set +CONFIG_MFD_SUN4I_GPADC=y +# CONFIG_MFD_SUN6I_PRCM is not set +CONFIG_MFD_SYSCON=y +CONFIG_MFD_VEXPRESS_SYSREG=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_CAVIUM_THUNDERX=y +CONFIG_MMC_DW=y +# CONFIG_MMC_DW_BLUEFIELD is not set +# CONFIG_MMC_DW_EXYNOS is not set +# CONFIG_MMC_DW_HI3798CV200 is not set +# CONFIG_MMC_DW_K3 is not set +# CONFIG_MMC_DW_PCI is not set +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_RICOH_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_PCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SUNXI=y +CONFIG_MODULES_USE_ELF_RELA=y +# CONFIG_MVNETA is not set +# CONFIG_MVPP2 is not set +# CONFIG_MV_XOR is not set +CONFIG_NEED_SG_DMA_LENGTH=y +# CONFIG_NET_VENDOR_ALLWINNER is not set +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=64 +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +# CONFIG_NVMEM_LAYERSCAPE_SFP is not set +CONFIG_NVMEM_ROCKCHIP_EFUSE=y +# CONFIG_NVMEM_ROCKCHIP_OTP is not set +# CONFIG_NVMEM_SUNXI_SID is not set +# CONFIG_NVMEM_ZYNQMP is not set +CONFIG_PCC=y +CONFIG_PCIE_HISI_STB=y +CONFIG_PCIE_LAYERSCAPE=y +CONFIG_PCIE_MOBIVEIL_PLAT=y +CONFIG_PCIE_ROCKCHIP=y +# CONFIG_PCIE_ROCKCHIP_DW_HOST is not set +CONFIG_PCIE_ROCKCHIP_HOST=y +CONFIG_PCIE_XILINX_CPM=y +CONFIG_PCIE_XILINX_NWL=y +CONFIG_PCI_AARDVARK=y +CONFIG_PCI_LAYERSCAPE=y +# CONFIG_PHY_FSL_LYNX_28G is not set +CONFIG_PHY_HI3660_USB=y +CONFIG_PHY_HI3670_PCIE=y +CONFIG_PHY_HI3670_USB=y +CONFIG_PHY_HI6220_USB=y +CONFIG_PHY_HISI_INNO_USB2=y +# CONFIG_PHY_HISTB_COMBPHY is not set +CONFIG_PHY_MVEBU_A3700_COMPHY=y +CONFIG_PHY_MVEBU_A3700_UTMI=y +CONFIG_PHY_MVEBU_A38X_COMPHY=y +CONFIG_PHY_MVEBU_CP110_COMPHY=y +# CONFIG_PHY_ROCKCHIP_DP is not set +# CONFIG_PHY_ROCKCHIP_DPHY_RX0 is not set +CONFIG_PHY_ROCKCHIP_EMMC=y +# CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_HDMI is not set +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +# CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY is not set +CONFIG_PHY_ROCKCHIP_PCIE=y +CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +# CONFIG_PHY_ROCKCHIP_USB is not set +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_SUN50I_USB3=y +# CONFIG_PHY_SUN6I_MIPI_DPHY is not set +CONFIG_PHY_SUN9I_USB=y +# CONFIG_PHY_XILINX_ZYNQMP is not set +CONFIG_PINCTRL_ROCKCHIP=y +# CONFIG_PINCTRL_SUN20I_D1 is not set +CONFIG_PINCTRL_SUN4I_A10=y +CONFIG_PINCTRL_SUN50I_A100=y +CONFIG_PINCTRL_SUN50I_A100_R=y +CONFIG_PINCTRL_SUN50I_A64=y +CONFIG_PINCTRL_SUN50I_A64_R=y +CONFIG_PINCTRL_SUN50I_H5=y +CONFIG_PINCTRL_SUN50I_H6=y +CONFIG_PINCTRL_SUN50I_H616=y +CONFIG_PINCTRL_SUN50I_H616_R=y +CONFIG_PINCTRL_SUN50I_H6_R=y +CONFIG_PINCTRL_SUN5I=y +# CONFIG_PINCTRL_SUN6I_A31 is not set +# CONFIG_PINCTRL_SUN6I_A31_R is not set +# CONFIG_PINCTRL_SUN8I_A23 is not set +# CONFIG_PINCTRL_SUN8I_A23_R is not set +# CONFIG_PINCTRL_SUN8I_A33 is not set +# CONFIG_PINCTRL_SUN8I_A83T is not set +# CONFIG_PINCTRL_SUN8I_A83T_R is not set +# CONFIG_PINCTRL_SUN8I_H3 is not set +# CONFIG_PINCTRL_SUN8I_H3_R is not set +# CONFIG_PINCTRL_SUN8I_V3S is not set +# CONFIG_PINCTRL_SUN9I_A80 is not set +# CONFIG_PINCTRL_SUN9I_A80_R is not set +CONFIG_PINCTRL_ZYNQMP=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_HISI=y +CONFIG_POWER_RESET_VEXPRESS=y +CONFIG_POWER_SUPPLY=y +CONFIG_QORIQ_THERMAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_HI655X=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IOMMU=y +# CONFIG_ROCKCHIP_MBOX is not set +CONFIG_ROCKCHIP_PM_DOMAINS=y +# CONFIG_ROCKCHIP_SARADC is not set +# CONFIG_ROCKCHIP_THERMAL is not set +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +# CONFIG_RTC_DRV_FSL_FTM_ALARM is not set +CONFIG_RTC_DRV_MV=y +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_SERIAL_8250_EXAR is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_MVEBU_CONSOLE=y +CONFIG_SERIAL_MVEBU_UART=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +# CONFIG_SMC91X is not set +# CONFIG_SND_SUN4I_I2S is not set +# CONFIG_SND_SUN50I_CODEC_ANALOG is not set +# CONFIG_SND_SUN50I_DMIC is not set +# CONFIG_SND_SUN8I_CODEC is not set +# CONFIG_SND_SUN8I_CODEC_ANALOG is not set +# CONFIG_SNI_NETSEC is not set +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPI_ARMADA_3700=y +# CONFIG_SPI_FSL_QUADSPI is not set +# CONFIG_SPI_HISI_KUNPENG is not set +# CONFIG_SPI_HISI_SFC is not set +# CONFIG_SPI_HISI_SFC_V3XX is not set +# CONFIG_SPI_ROCKCHIP_SFC is not set +# CONFIG_SPI_SUN4I is not set +# CONFIG_SPI_SUN6I is not set +# CONFIG_SPI_SYNQUACER is not set +CONFIG_SPI_THUNDERX=y +# CONFIG_SPI_XLP is not set +CONFIG_STUB_CLK_HI3660=y +CONFIG_STUB_CLK_HI6220=y +CONFIG_SUN50I_A100_CCU=y +CONFIG_SUN50I_A100_R_CCU=y +CONFIG_SUN50I_A64_CCU=y +CONFIG_SUN50I_H616_CCU=y +CONFIG_SUN50I_H6_CCU=y +CONFIG_SUN50I_H6_R_CCU=y +CONFIG_SUN50I_IOMMU=y +CONFIG_SUN6I_MSGBOX=y +CONFIG_SUN6I_RTC_CCU=y +# CONFIG_SUN8I_A83T_CCU is not set +CONFIG_SUN8I_DE2_CCU=y +# CONFIG_SUN8I_H3_CCU is not set +CONFIG_SUN8I_R_CCU=y +CONFIG_SUN8I_THERMAL=y +CONFIG_SUNXI_CCU=y +CONFIG_SUNXI_RSB=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +# CONFIG_TCG_TIS_SYNQUACER is not set +CONFIG_THREAD_INFO_IN_TASK=y +# CONFIG_TURRIS_MOX_RWTM is not set +# CONFIG_UACCE is not set +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_XILINX=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_HISTB=y +CONFIG_USB_XHCI_MVEBU=y +CONFIG_USB_XHCI_PLATFORM=y +CONFIG_VEXPRESS_CONFIG=y +CONFIG_VIDEOMODE_HELPERS=y +CONFIG_VIRTIO_DMA_SHARED_BUFFER=y +# CONFIG_VIRTIO_IOMMU is not set +CONFIG_VMAP_STACK=y +CONFIG_WDAT_WDT=y +# CONFIG_XILINX_AMS is not set +# CONFIG_XILINX_INTC is not set +CONFIG_XLNX_EVENT_MANAGER=y +CONFIG_ZONE_DMA32=y +CONFIG_ZYNQMP_FIRMWARE=y +# CONFIG_ZYNQMP_FIRMWARE_DEBUG is not set +CONFIG_ZYNQMP_PM_DOMAINS=y +CONFIG_ZYNQMP_POWER=y diff --git a/target/linux/armvirt/64/target.mk b/target/linux/armvirt/64/target.mk index 58adcc7d602..ac5a60d848b 100644 --- a/target/linux/armvirt/64/target.mk +++ b/target/linux/armvirt/64/target.mk @@ -1,8 +1,6 @@ ARCH:=aarch64 SUBTARGET:=64 -BOARDNAME:=QEMU ARMv8 Virtual Machine (cortex-a53) -CPU_TYPE:=cortex-a53 -KERNELNAME:=Image +BOARDNAME:=64-bit ARM machines define Target/Description Build multi-platform images for the ARMv8 instruction set architecture diff --git a/target/linux/armvirt/Makefile b/target/linux/armvirt/Makefile index 73913f4a5b7..0ae51597bcb 100644 --- a/target/linux/armvirt/Makefile +++ b/target/linux/armvirt/Makefile @@ -6,13 +6,16 @@ include $(TOPDIR)/rules.mk BOARD:=armvirt BOARDNAME:=QEMU ARM Virtual Machine -FEATURES:=fpu pci rtc usb -FEATURES+=cpiogz ext4 ramdisk squashfs targz +FEATURES:=fpu pci pcie rtc usb boot-part rootfs-part +FEATURES+=cpiogz ext4 ramdisk squashfs targz vmdk -KERNEL_PATCHVER:=5.15 +KERNEL_PATCHVER:=6.1 include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES += mkf2fs e2fsprogs +# blkid used for resolving PARTUUID +# in sysupgrade +DEFAULT_PACKAGES += blkid $(eval $(call BuildTarget)) diff --git a/target/linux/armvirt/README b/target/linux/armvirt/README index b870fe19f7c..b4409f8f113 100644 --- a/target/linux/armvirt/README +++ b/target/linux/armvirt/README @@ -1,21 +1,23 @@ -This is intended to be used with OpenWrt project to provide image for use with -QEMU ARM virt machine. +This target generates images that can be used on ARM machines with EFI +support (e.g EDKII/TianoCore or U-Boot with bootefi). + +The kernel and filesystem images can also be used directly by QEMU: Run with qemu-system-arm # boot with initramfs embedded in - qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-zImage-initramfs + qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-generic-initramfs-kernel.bin # boot with accel=kvm qemu-system-arm -nographic -M virt,accel=kvm -cpu host -m 64 -kernel - openwrt-armvirt-32-zImage-initramfs + openwrt-armvirt-32-generic-initramfs-kernel.bin # boot with a separate rootfs - qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-zImage \ - -drive file=openwrt-armvirt-32-root.ext4,format=raw,if=virtio -append 'root=/dev/vda rootwait' + qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-generic-kernel.bin \ + -drive file=openwrt-armvirt-32-generic-ext4-rootfs.img,format=raw,if=virtio -append 'root=/dev/vda rootwait' # boot with local dir as rootfs - qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-zImage \ + qemu-system-arm -nographic -M virt -m 64 -kernel openwrt-armvirt-32-generic-kernel.bin \ -fsdev local,id=rootdev,path=root-armvirt/,security_model=none \ -device virtio-9p-pci,fsdev=rootdev,mount_tag=/dev/root \ -append 'rootflags=trans=virtio,version=9p2000.L,cache=loose rootfstype=9p' @@ -37,4 +39,34 @@ Run with kvmtool The multi-platform ARMv8 target can be used with QEMU: qemu-system-aarch64 -machine virt -cpu cortex-a57 -nographic \ - -kernel openwrt-armvirt-64-Image-initramfs \ + -kernel openwrt-armvirt-64-generic-initramfs-kernel.bin \ + +With a EDKII or U-Boot binary for the QEMU ARM virtual machines, you can use these +images in EFI mode: + +32-bit: +gunzip -c bin/targets/armvirt/32/openwrt-armvirt-32-generic-ext4-combined.img.gz > openwrt-arm-32.img +qemu-system-arm -nographic \ + -cpu cortex-a15 -machine virt \ + -bios QEMU_EFI_32.fd \ + -smp 1 -m 1024 \ + -device virtio-rng-pci \ + -drive file=openwrt-arm-32.img,format=raw,index=0,media=disk \ + -netdev user,id=testlan -net nic,netdev=testlan \ + -netdev user,id=testwan -net nic,netdev=testwan + +64-bit: +gunzip -c bin/targets/armvirt/64/openwrt-armvirt-64-generic-ext4-combined.img.gz > openwrt-arm-64.img +qemu-system-aarch64 -nographic \ + -cpu cortex-a53 -machine virt \ + -bios QEMU_EFI_64.fd \ + -smp 1 -m 1024 \ + -device virtio-rng-pci \ + -drive file=openwrt-arm-64.img,format=raw,index=0,media=disk \ + -netdev user,id=testlan -net nic,netdev=testlan \ + -netdev user,id=testwan -net nic,netdev=testwan + +One can find EFI/BIOS binaries from: +- Compile mainline U-Boot for the QEMU ARM virtual machine (qemu_arm_defconfig/qemu_arm64_defconfig) +- From distribution packages (such as qemu-efi-arm and qemu-efi-aarch64 in Debian) +- Community builds, like retrage/edk2-nightly: https://retrage.github.io/edk2-nightly/ diff --git a/target/linux/armvirt/base-files/etc/board.d/00_model b/target/linux/armvirt/base-files/etc/board.d/00_model deleted file mode 100644 index bfaa45f59cc..00000000000 --- a/target/linux/armvirt/base-files/etc/board.d/00_model +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2015 OpenWrt.org -# Copyright (C) 2016 Yousong Zhou - -. /lib/functions/uci-defaults.sh - -board_config_update - -ucidef_set_board_id "armvirt" -ucidef_set_model_name "QEMU ARM Virtual Machine" - -board_config_flush - -exit 0 diff --git a/target/linux/armvirt/base-files/etc/board.d/01_led b/target/linux/armvirt/base-files/etc/board.d/01_led new file mode 100644 index 00000000000..0250a9672f5 --- /dev/null +++ b/target/linux/armvirt/base-files/etc/board.d/01_led @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +traverse,ten64) + ucidef_set_led_netdev "sfp1" "SFP 1" "ten64:green:sfp1:down" "eth8" "link tx rx" + ucidef_set_led_netdev "sfp2" "SFP 2" "ten64:green:sfp2:up" "eth9" "link tx rx" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/armvirt/base-files/etc/board.d/02_network b/target/linux/armvirt/base-files/etc/board.d/02_network new file mode 100644 index 00000000000..f58de1c27d3 --- /dev/null +++ b/target/linux/armvirt/base-files/etc/board.d/02_network @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/system.sh +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in + traverse,ten64) + ucidef_set_interface_lan "eth0 eth1 eth2 eth3" + ucidef_set_interface_wan "eth6" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/armvirt/base-files/etc/board.d/03_gpio_switches b/target/linux/armvirt/base-files/etc/board.d/03_gpio_switches new file mode 100644 index 00000000000..cf07bc0f54f --- /dev/null +++ b/target/linux/armvirt/base-files/etc/board.d/03_gpio_switches @@ -0,0 +1,23 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +traverse,ten64) + ucidef_add_gpio_switch "lte_reset" "Cell Modem Reset" "376" + ucidef_add_gpio_switch "lte_power" "Cell Modem Power" "377" + ucidef_add_gpio_switch "lte_disable" "Cell Modem Airplane mode" "378" + ucidef_add_gpio_switch "gnss_disable" "Cell Modem Disable GNSS receiver" "379" + ucidef_add_gpio_switch "lower_sfp_txidsable" "Lower SFP+ TX Disable" "369" + ucidef_add_gpio_switch "upper_sfp_txdisable" "Upper SFP+ TX Disable" "373" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/armvirt/base-files/etc/inittab b/target/linux/armvirt/base-files/etc/inittab index 837d7f32a41..51832eb7756 100644 --- a/target/linux/armvirt/base-files/etc/inittab +++ b/target/linux/armvirt/base-files/etc/inittab @@ -2,4 +2,8 @@ ::shutdown:/etc/init.d/rcS K shutdown ttyAMA0::askfirst:/usr/libexec/login.sh ttyS0::askfirst:/usr/libexec/login.sh +tty0::askfirst:/usr/libexec/login.sh hvc0::askfirst:/usr/libexec/login.sh +ttymxc0::askfirst:/usr/libexec/login.sh +ttymxc1::askfirst:/usr/libexec/login.sh +ttymxc2::askfirst:/usr/libexec/login.sh diff --git a/target/linux/armvirt/base-files/lib/preinit/01_sysinfo_acpi b/target/linux/armvirt/base-files/lib/preinit/01_sysinfo_acpi new file mode 100644 index 00000000000..1069d74cf3e --- /dev/null +++ b/target/linux/armvirt/base-files/lib/preinit/01_sysinfo_acpi @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +sanitize_name_arm64() { + sed -e ' + y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/; + s/[^a-z0-9_-]\+/-/g; + s/^-//; + s/-$//; + ' "$@" +} + +do_sysinfo_arm64() { + local vendor product file + + for file in sys_vendor board_vendor; do + vendor="$(cat /sys/devices/virtual/dmi/id/$file 2>/dev/null)" + case "$vendor" in + empty | \ + System\ manufacturer | \ + To\ [bB]e\ [fF]illed\ [bB]y\ O\.E\.M\.) + continue + ;; + esac + [ -n "$vendor" ] && break + done + + for file in product_name board_name; do + product="$(cat /sys/devices/virtual/dmi/id/$file 2>/dev/null)" + case "$vendor:$product" in + ?*:empty | \ + ?*:System\ Product\ Name | \ + ?*:To\ [bB]e\ [fF]illed\ [bB]y\ O\.E\.M\.) + continue + ;; + ?*:?*) + break + ;; + esac + done + + [ -d "/sys/firmware/devicetree/base" ] && return + + [ -n "$vendor" -a -n "$product" ] || return + + mkdir -p /tmp/sysinfo + + echo "$vendor $product" > /tmp/sysinfo/model + + sanitize_name_arm64 /tmp/sysinfo/model > /tmp/sysinfo/board_name +} + +boot_hook_add preinit_main do_sysinfo_arm64 diff --git a/target/linux/armvirt/base-files/lib/upgrade/platform.sh b/target/linux/armvirt/base-files/lib/upgrade/platform.sh new file mode 100644 index 00000000000..8263b9c7e3a --- /dev/null +++ b/target/linux/armvirt/base-files/lib/upgrade/platform.sh @@ -0,0 +1,164 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +RAMFS_COPY_BIN="/usr/sbin/blkid" + +platform_check_image() { + local board=$(board_name) + local diskdev partdev diff + [ "$#" -gt 1 ] && return 1 + + v "Board is ${board}" + + export_bootdevice && export_partdevice diskdev 0 || { + v "platform_check_image: Unable to determine upgrade device" + return 1 + } + + get_partitions "/dev/$diskdev" bootdisk + + v "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + + rm -f /tmp/image.bs /tmp/partmap.bootdisk /tmp/partmap.image + + if [ -n "$diff" ]; then + v "Partition layout has changed. Full image will be written." + ask_bool 0 "Abort" && exit 1 + return 0 + fi +} + +platform_copy_config() { + local partdev parttype=ext4 + + if export_partdevice partdev 2; then + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + cp -af "$UPGRADE_BACKUP" "/mnt/$BACKUP_FILE" + umount /mnt + else + v "ERROR: Unable to find partition to copy config data to" + fi + + sleep 5 +} + +# To avoid writing over any firmware +# files (e.g ubootefi.var or firmware/X/ aka EBBR) +# Copy efi/openwrt and efi/boot from the new image +# to the existing ESP +platform_do_upgrade_efi_system_partition() { + local image_file=$1 + local target_partdev=$2 + local image_efisp_start=$3 + local image_efisp_size=$4 + + v "Updating ESP on ${target_partdev}" + NEW_ESP_DIR="/mnt/new_esp_loop" + CUR_ESP_DIR="/mnt/cur_esp" + mkdir "${NEW_ESP_DIR}" + mkdir "${CUR_ESP_DIR}" + + get_image_dd "$image_file" of="/tmp/new_efi_sys_part.img" \ + skip="$image_efisp_start" count="$image_efisp_size" + + mount -t vfat -o loop -o ro /tmp/new_efi_sys_part.img "${NEW_ESP_DIR}" + if [ ! -d "${NEW_ESP_DIR}/efi/boot" ]; then + v "ERROR: Image does not contain EFI boot files (/efi/boot)" + return 1 + fi + + mount -t vfat "/dev/$partdev" "${CUR_ESP_DIR}" + + for d in $(find "${NEW_ESP_DIR}/efi/" -mindepth 1 -maxdepth 1 -type d); do + v "Copying ${d}" + newdir_bname=$(basename "${d}") + rm -rf "${CUR_ESP_DIR}/efi/${newdir_bname}" + cp -r "${d}" "${CUR_ESP_DIR}/efi" + done + + umount "${NEW_ESP_DIR}" + umount "${CUR_ESP_DIR}" +} + +platform_do_upgrade() { + local board=$(board_name) + local diskdev partdev diff + + export_bootdevice && export_partdevice diskdev 0 || { + v "platform_do_upgrade: Unable to determine upgrade device" + return 1 + } + + sync + + if [ "$UPGRADE_OPT_SAVE_PARTITIONS" = "1" ]; then + get_partitions "/dev/$diskdev" bootdisk + + v "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + else + diff=1 + fi + + # Only change the partition table if sysupgrade -p is set, + # otherwise doing so could interfere with embedded "single storage" + # (e.g SoC boot from SD card) setups, as well as other user + # created storage (like uvol) + if [ -n "$diff" ] && [ "${UPGRADE_OPT_SAVE_PARTITIONS}" = "0" ]; then + # Need to remove partitions before dd, otherwise the partitions + # that are added after will have minor numbers offset + partx -d - "/dev/$diskdev" + + get_image_dd "$1" of="/dev/$diskdev" bs=4096 conv=fsync + + # Separate removal and addtion is necessary; otherwise, partition 1 + # will be missing if it overlaps with the old partition 2 + partx -a - "/dev/$diskdev" + + return 0 + fi + + #iterate over each partition from the image and write it to the boot disk + while read part start size; do + if export_partdevice partdev $part; then + v "Writing image to /dev/$partdev..." + if [ "$part" = "1" ]; then + platform_do_upgrade_efi_system_partition \ + $1 $partdev $start $size || return 1 + else + v "Normal partition, doing DD" + get_image_dd "$1" of="/dev/$partdev" ibs=512 obs=1M skip="$start" \ + count="$size" conv=fsync + fi + else + v "Unable to find partition $part device, skipped." + fi + done < /tmp/partmap.image + + local parttype=ext4 + + if (blkid > /dev/null) && export_partdevice partdev 1; then + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + if export_partdevice partdev 2; then + THIS_PART_BLKID=$(blkid -o value -s PARTUUID "/dev/${partdev}") + v "Setting rootfs PARTUUID=${THIS_PART_BLKID}" + sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1${THIS_PART_BLKID}/ig" \ + /mnt/efi/openwrt/grub.cfg + fi + umount /mnt + fi + # Provide time for the storage medium to flush before system reset + # (despite the sync/umount it appears NVMe etc. do it in the background) + sleep 5 +} diff --git a/target/linux/armvirt/config-6.1 b/target/linux/armvirt/config-6.1 new file mode 100644 index 00000000000..a48af89b875 --- /dev/null +++ b/target/linux/armvirt/config-6.1 @@ -0,0 +1,336 @@ +CONFIG_64BIT=y +CONFIG_9P_FS=y +# CONFIG_9P_FS_POSIX_ACL is not set +# CONFIG_9P_FS_SECURITY is not set +# CONFIG_A64FX_DIAG is not set +CONFIG_ACPI=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_EINJ=y +# CONFIG_ACPI_APEI_ERST_DEBUG is not set +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_PCIEAER=y +CONFIG_ACPI_BATTERY=y +# CONFIG_ACPI_BGRT is not set +CONFIG_ACPI_CCA_REQUIRED=y +CONFIG_ACPI_CONTAINER=y +CONFIG_ACPI_CPPC_CPUFREQ=y +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_DEBUGGER is not set +# CONFIG_ACPI_DOCK is not set +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_FAN=y +CONFIG_ACPI_GENERIC_GSI=y +CONFIG_ACPI_GTDT=y +CONFIG_ACPI_HOTPLUG_CPU=y +CONFIG_ACPI_I2C_OPREGION=y +CONFIG_ACPI_IORT=y +CONFIG_ACPI_MCFG=y +# CONFIG_ACPI_PCI_SLOT is not set +# CONFIG_ACPI_PFRUT is not set +CONFIG_ACPI_PPTT=y +CONFIG_ACPI_PRMT=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y +CONFIG_ACPI_SPCR_TABLE=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_TINY_POWER_BUTTON is not set +# CONFIG_ALIBABA_UNCORE_DRW_PMU is not set +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +# CONFIG_ARM64_ACPI_PARKING_PROTOCOL is not set +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_SMMU_V3_PMU is not set +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BALLOON_COMPACTION=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_RNG2=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ACPI=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMI=y +CONFIG_DMIID=y +CONFIG_DMI_SYSFS=y +CONFIG_DTC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EFI=y +CONFIG_EFIVAR_FS=y +CONFIG_EFI_ARMSTUB_DTB_LOADER=y +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_CAPSULE_LOADER is not set +# CONFIG_EFI_COCO_SECRET is not set +# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set +# CONFIG_EFI_DISABLE_PCI_DMA is not set +# CONFIG_EFI_DISABLE_RUNTIME is not set +CONFIG_EFI_EARLYCON=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_GENERIC_STUB=y +# CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER is not set +CONFIG_EFI_PARAMS_FROM_FDT=y +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_TEST is not set +# CONFIG_EFI_ZBOOT is not set +CONFIG_EXT4_FS=y +CONFIG_F2FS_FS=y +CONFIG_FAILOVER=y +CONFIG_FB_EFI=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FONT_8x16=y +CONFIG_FONT_AUTOSELECT=y +CONFIG_FONT_SUPPORT=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FRAME_POINTER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=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_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_ACPI=y +CONFIG_GPIO_CDEV=y +# CONFIG_GPIO_HISI is not set +CONFIG_GPIO_PL061=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_HVC_DRIVER=y +CONFIG_HZ_PERIODIC=y +# CONFIG_I2C_AMD_MP2 is not set +CONFIG_I2C_HID_ACPI=y +# CONFIG_I2C_HISI is not set +# CONFIG_I2C_SLAVE_TESTUNIT is not set +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT_KEYBOARD=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +# CONFIG_ISCSI_IBFT is not set +CONFIG_JBD2=y +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_BALLOON=y +CONFIG_MIGRATION=y +# CONFIG_MLXBF_GIGE is not set +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_MVMDIO=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_9P=y +# CONFIG_NET_9P_DEBUG is not set +# CONFIG_NET_9P_FD is not set +CONFIG_NET_9P_VIRTIO=y +CONFIG_NET_FAILOVER=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NLS=y +CONFIG_NR_CPUS=256 +CONFIG_NVMEM=y +CONFIG_NVME_CORE=y +# CONFIG_NVME_MULTIPATH 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_PADATA=y +CONFIG_PAGE_REPORTING=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +# CONFIG_PCIE_HISI_ERR is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_ECAM=y +CONFIG_PCI_HOST_COMMON=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_LABEL=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PMIC_OPREGION is not set +CONFIG_PNP=y +CONFIG_PNPACPI=y +CONFIG_PNP_DEBUG_MESSAGES=y +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RATIONAL=y +# CONFIG_RESET_ATTACK_MITIGATION is not set +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_HOST=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SCSI_VIRTIO=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PNP=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SRCU=y +# CONFIG_SURFACE_PLATFORMS is not set +CONFIG_SWIOTLB=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFB=y +# CONFIG_SYSFB_SIMPLEFB is not set +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_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_ACPI=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +# CONFIG_UACCE is not set +CONFIG_UCS2_STRING=y +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_HID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_PCI=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PCI=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_PCI_LEGACY=y +CONFIG_VIRTIO_PCI_LIB=y +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_XPS=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/armvirt/image/Makefile b/target/linux/armvirt/image/Makefile index 5f9ef21d658..8642c88a499 100644 --- a/target/linux/armvirt/image/Makefile +++ b/target/linux/armvirt/image/Makefile @@ -5,28 +5,113 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/image.mk -define Image/BuildKernel - $(foreach k,$(filter zImage Image,$(KERNELNAME)), \ - cp $(KDIR)/$(KERNELNAME) $(BIN_DIR)/$(IMG_PREFIX)-$(k) \ +GRUB2_VARIANT = +GRUB_TERMINALS = +GRUB_SERIAL_CONFIG = +GRUB_TERMINAL_CONFIG = +GRUB_CONSOLE_CMDLINE = earlycon + +ifneq ($(CONFIG_GRUB_CONSOLE),) + GRUB_TERMINALS += console +endif + +GRUB_SERIAL:=$(call qstrip,$(CONFIG_GRUB_SERIAL)) + +ifneq ($(GRUB_SERIAL),) + GRUB_SERIAL_CONFIG := serial --unit=0 --speed=$(CONFIG_GRUB_BAUDRATE) --word=8 --parity=no --stop=1 --rtscts=$(if $(CONFIG_GRUB_FLOWCONTROL),on,off) + GRUB_TERMINALS += serial +endif + +ifneq ($(GRUB_TERMINALS),) + GRUB_TERMINAL_CONFIG := terminal_input $(GRUB_TERMINALS); terminal_output $(GRUB_TERMINALS) +endif + +ROOTPART:=$(call qstrip,$(CONFIG_TARGET_ROOTFS_PARTNAME)) +ROOTPART:=$(if $(ROOTPART),$(ROOTPART),PARTUUID=$(IMG_PART_SIGNATURE)-02) +GPT_ROOTPART:=$(call qstrip,$(CONFIG_TARGET_ROOTFS_PARTNAME)) +GPT_ROOTPART:=$(if $(GPT_ROOTPART),$(GPT_ROOTPART),PARTUUID=$(shell echo $(IMG_PART_DISKGUID) | sed 's/00$$/02/')) + +GRUB_TIMEOUT:=$(call qstrip,$(CONFIG_GRUB_TIMEOUT)) +GRUB_TITLE:=$(call qstrip,$(CONFIG_GRUB_TITLE)) + +BOOTOPTS:=$(call qstrip,$(CONFIG_GRUB_BOOTOPTS)) + +define Build/combined + $(INSTALL_DIR) $@.boot/ + $(CP) $(KDIR)/$(KERNEL_NAME) $@.boot/efi/openwrt/ + -$(CP) $(STAGING_DIR_ROOT)/boot/. $@.boot/boot/ + $(if $(filter $(1),efi), + $(INSTALL_DIR) $@.boot/efi/boot + $(CP) $(STAGING_DIR_IMAGE)/grub2/boot$(if $(CONFIG_aarch64),aa64,arm).efi $@.boot/efi/openwrt/ + $(CP) $(STAGING_DIR_IMAGE)/grub2/boot$(if $(CONFIG_aarch64),aa64,arm).efi $@.boot/efi/boot/ ) + KERNELPARTTYPE=ef FAT_TYPE="32" PADDING="1" SIGNATURE="$(IMG_PART_SIGNATURE)" \ + $(if $(filter $(1),efi),GUID="$(IMG_PART_DISKGUID)") $(SCRIPT_DIR)/gen_image_generic.sh \ + $@ \ + $(CONFIG_TARGET_KERNEL_PARTSIZE) $@.boot \ + $(CONFIG_TARGET_ROOTFS_PARTSIZE) $(IMAGE_ROOTFS) \ + 256 endef -define Image/Build/Initramfs - $(foreach k,$(filter zImage Image,$(KERNELNAME)), \ - cp $(KDIR)/$(k)-initramfs $(BIN_DIR)/$(IMG_PREFIX)-$(k)-initramfs \ - ) +define Build/grub-config + rm -fR $@.boot + $(INSTALL_DIR) $@.boot/efi/openwrt/ + sed \ + -e 's#@SERIAL_CONFIG@#$(strip $(GRUB_SERIAL_CONFIG))#g' \ + -e 's#@TERMINAL_CONFIG@#$(strip $(GRUB_TERMINAL_CONFIG))#g' \ + -e 's#@ROOTPART@#root=$(ROOTPART) rootwait#g' \ + -e 's#@GPT_ROOTPART@#root=$(GPT_ROOTPART) rootwait#g' \ + -e 's#@CMDLINE@#$(BOOTOPTS) $(GRUB_CONSOLE_CMDLINE)#g' \ + -e 's#@TIMEOUT@#$(GRUB_TIMEOUT)#g' \ + -e 's#@TITLE@#$(GRUB_TITLE)#g' \ + -e 's#@KERNEL_NAME@#$(KERNEL_NAME)#g' \ + ./grub-$(1).cfg > $@.boot/efi/openwrt/grub.cfg endef -define Image/Build/gzip - gzip -f9n $(BIN_DIR)/$(IMG_ROOTFS)-$(1).img +define Build/grub-install + rm -fR $@.grub2 + $(INSTALL_DIR) $@.grub2 endef -$(eval $(call Image/gzip-ext4-padded-squashfs)) - -define Image/Build - $(call Image/Build/$(1)) - $(CP) $(KDIR)/root.$(1) $(BIN_DIR)/$(IMG_ROOTFS)-$(1).img - $(call Image/Build/gzip/$(1)) +DEVICE_VARS += GRUB2_VARIANT +define Device/efi-default + IMAGE/rootfs.img := append-rootfs | pad-to $(ROOTFS_PARTSIZE) + IMAGE/rootfs.img.gz := append-rootfs | pad-to $(ROOTFS_PARTSIZE) | gzip + IMAGE/combined.img := grub-config efi | combined efi | grub-install efi | append-metadata + IMAGE/combined.img.gz := grub-config efi | combined efi | grub-install efi | gzip | append-metadata + IMAGE/combined.vmdk := grub-config efi | combined efi | grub-install efi | qemu-image vmdk + ifeq ($(CONFIG_TARGET_IMAGES_GZIP),y) + IMAGES-y := rootfs.img.gz + IMAGES-y += combined.img.gz + else + IMAGES-y := rootfs.img + IMAGES-y += combined.img + endif + ifeq ($(CONFIG_VMDK_IMAGES),y) + IMAGES-y += combined.vmdk + endif + KERNEL := kernel-bin + KERNEL_INSTALL := 1 + IMAGES := $$(IMAGES-y) + ARTIFACTS := $$(ARTIFACTS-y) + SUPPORTED_DEVICES := + ifeq ($(CONFIG_arm),y) + KERNEL_NAME = zImage + endif endef +define Device/generic + $(call Device/efi-default) + DEVICE_TITLE := Generic EFI Boot + GRUB2_VARIANT := generic + FILESYSTEMS := ext4 squashfs + DEVICE_PACKAGES += kmod-amazon-ena kmod-e1000e kmod-vmxnet3 kmod-rtc-rx8025 \ + kmod-i2c-mux-pca954x kmod-gpio-pca953x partx-utils kmod-wdt-sp805 \ + kmod-mvneta kmod-mvpp2 kmod-fsl-dpaa1-net kmod-fsl-dpaa2-net \ + kmod-fsl-enetc-net kmod-dwmac-imx kmod-fsl-fec kmod-thunderx-net \ + kmod-dwmac-rockchip kmod-dwmac-sun8i kmod-phy-aquantia kmod-phy-broadcom \ + kmod-phy-marvell kmod-phy-marvell-10g kmod-sfp kmod-atlantic +endef +TARGET_DEVICES += generic + $(eval $(call BuildImage)) diff --git a/target/linux/armvirt/image/grub-efi.cfg b/target/linux/armvirt/image/grub-efi.cfg new file mode 100644 index 00000000000..fd329e41e0f --- /dev/null +++ b/target/linux/armvirt/image/grub-efi.cfg @@ -0,0 +1,14 @@ +@SERIAL_CONFIG@ +@TERMINAL_CONFIG@ + +set default="0" +set timeout="@TIMEOUT@" + +menuentry "@TITLE@" { + search --set=root --label kernel + linux /efi/openwrt/@KERNEL_NAME@ @GPT_ROOTPART@ @CMDLINE@ noinitrd +} +menuentry "@TITLE@ (failsafe)" { + search --set=root --label kernel + linux /efi/openwrt/@KERNEL_NAME@ failsafe=true @GPT_ROOTPART@ @CMDLINE@ noinitrd +} diff --git a/target/linux/armvirt/modules.mk b/target/linux/armvirt/modules.mk new file mode 100644 index 00000000000..c59301aae7e --- /dev/null +++ b/target/linux/armvirt/modules.mk @@ -0,0 +1,292 @@ +define KernelPackage/acpi-mdio + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=ACPI MDIO support + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy +kmod-mdio-devres + KCONFIG:=CONFIG_ACPI_MDIO + FILES:=$(LINUX_DIR)/drivers/net/mdio/acpi_mdio.ko + AUTOLOAD:=$(call AutoLoad,11,acpi_mdio) +endef + +define KernelPackage/acpi-mdio/description + Kernel driver for ACPI MDIO support +endef + +$(eval $(call KernelPackage,acpi-mdio)) + +define KernelPackage/fsl-pcs-lynx + SUBMENU=$(NETWORK_DEVICES_MENU) + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy +kmod-of-mdio +kmod-phylink + TITLE:=NXP (Freescale) Lynx PCS + HIDDEN:=1 + KCONFIG:=CONFIG_PCS_LYNX + FILES=$(LINUX_DIR)/drivers/net/pcs/pcs-lynx.ko + AUTOLOAD=$(call AutoLoad,30,pcs-lynx) +endef + +$(eval $(call KernelPackage,fsl-pcs-lynx)) + +define KernelPackage/pcs-xpcs + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Synopsis DesignWare PCS driver + DEPENDS:=@(TARGET_armvirt_64) +kmod-phylink + KCONFIG:=CONFIG_PCS_XPCS + FILES:=$(LINUX_DIR)/drivers/net/pcs/pcs_xpcs.ko + AUTOLOAD:=$(call AutoLoad,20,pcs_xpcs) +endef + +$(eval $(call KernelPackage,pcs-xpcs)) + +define KernelPackage/fsl-fec + SUBMENU:=$(NETWORK_DEVICES_MENU) + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy +kmod-of-mdio \ + +kmod-ptp +kmod-net-selftests + TITLE:=NXP (Freescale) FEC Ethernet controller (i.MX) + KCONFIG:=CONFIG_FEC + FILES:=$(LINUX_DIR)/drivers/net/ethernet/freescale/fec.ko + AUTOLOAD:=$(call AutoLoad,35,fec) +endef + +$(eval $(call KernelPackage,fsl-fec)) + +define KernelPackage/fsl-xgmac-mdio + SUBMENU=$(NETWORK_DEVICES_MENU) + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy +kmod-of-mdio +kmod-acpi-mdio + TITLE:=NXP (Freescale) MDIO bus + KCONFIG:=CONFIG_FSL_XGMAC_MDIO + FILES=$(LINUX_DIR)/drivers/net/ethernet/freescale/xgmac_mdio.ko + AUTOLOAD=$(call AutoLoad,30,xgmac_mdio) +endef + +$(eval $(call KernelPackage,fsl-xgmac-mdio)) + +define KernelPackage/fsl-mc-dpio + SUBMENU:=$(OTHER_MENU) + TITLE:=NXP DPAA2 DPIO (Data Path IO) driver + HIDDEN:=1 + KCONFIG:=CONFIG_FSL_MC_BUS=y \ + CONFIG_FSL_MC_DPIO + FILES:=$(LINUX_DIR)/drivers/soc/fsl/dpio/fsl-mc-dpio.ko + AUTOLOAD=$(call AutoLoad,30,fsl-mc-dpio) +endef + +$(eval $(call KernelPackage,fsl-mc-dpio)) + +define KernelPackage/fsl-enetc-net + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=:NXP ENETC (LS1028A) Ethernet + DEPENDS:=@(TARGET_armvirt_64) +kmod-phylink +kmod-fsl-pcs-lynx + KCONFIG:= \ + CONFIG_FSL_ENETC \ + CONFIG_FSL_ENETC_VF \ + CONFIG_FSL_ENETC_QOS + FILES:= \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-vf.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-mdio.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/enetc/fsl-enetc-ierb.ko + AUTOLOAD=$(call AutoLoad,35,fsl-enetc) +endef + +$(eval $(call KernelPackage,fsl-enetc-net)) + +define KernelPackage/fsl-dpaa1-net + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=NXP DPAA1 (LS1043/LS1046) Ethernet + DEPENDS:=@(TARGET_armvirt_64) +kmod-fsl-xgmac-mdio +kmod-libphy +kmod-crypto-crc32 + KCONFIG:= \ + CONFIG_FSL_DPAA=y \ + CONFIG_FSL_DPAA_ETH \ + CONFIG_FSL_FMAN \ + CONFIG_FSL_DPAA_CHECKING=n \ + CONFIG_FSL_BMAN_TEST=n \ + CONFIG_FSL_QMAN_TEST=n + MODULES:= \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/dpaa/fsl_dpa.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/fman/fsl_dpaa_fman.ko \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/fman/fsl_dpaa_mac.ko + AUTOLOAD=$(call AutoLoad,35,fsl-dpa) +endef + +$(eval $(call KernelPackage,fsl-dpaa1-net)) + +define KernelPackage/fsl-dpaa2-net + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=NXP DPAA2 Ethernet + DEPENDS:=@(TARGET_armvirt_64) +kmod-fsl-xgmac-mdio +kmod-phylink \ + +kmod-fsl-pcs-lynx +kmod-fsl-mc-dpio + KCONFIG:= \ + CONFIG_FSL_MC_UAPI_SUPPORT=y \ + CONFIG_FSL_DPAA2_ETH + FILES:= \ + $(LINUX_DIR)/drivers/net/ethernet/freescale/dpaa2/fsl-dpaa2-eth.ko + AUTOLOAD=$(call AutoLoad,35,fsl-dpaa2-eth) +endef + +$(eval $(call KernelPackage,fsl-dpaa2-net)) + +define KernelPackage/fsl-dpaa2-console + SUBMENU:=$(OTHER_MENU) + TITLE:=NXP DPAA2 Debug console + DEPENDS:=@(TARGET_armvirt_64) + KCONFIG:=CONFIG_DPAA2_CONSOLE + FILES=$(LINUX_DIR)/drivers/soc/fsl/dpaa2-console.ko + AUTOLOAD=$(call AutoLoad,40,dpaa2-console) +endef + +define KernelPackage/fsl-dpaa2-console/description + Kernel modules for the NXP DPAA2 debug consoles + (Management Complex and AIOP). +endef + +$(eval $(call KernelPackage,fsl-dpaa2-console)) + +define KernelPackage/marvell-mdio + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Marvell Armada platform MDIO driver + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy +kmod-of-mdio +kmod-acpi-mdio + KCONFIG:=CONFIG_MVMDIO + FILES=$(LINUX_DIR)/drivers/net/ethernet/marvell/mvmdio.ko + AUTOLOAD=$(call AutoLoad,30,marvell-mdio) +endef + +$(eval $(call KernelPackage,marvell-mdio)) + +define KernelPackage/phy-marvell-10g + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Marvell Alaska 10G PHY driver + DEPENDS:=@(TARGET_armvirt_64) +kmod-libphy + KCONFIG:=CONFIG_MARVELL_10G_PHY + FILES=$(LINUX_DIR)/drivers/net/phy/marvell10g.ko + AUTOLOAD=$(call AutoLoad,35,marvell10g) +endef + +$(eval $(call KernelPackage,phy-marvell-10g)) + +define KernelPackage/mvneta + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Marvell Armada 370/38x/XP/37xx network driver + DEPENDS:=@(TARGET_armvirt_64) +kmod-marvell-mdio +kmod-phylink + KCONFIG:=CONFIG_MVNETA + FILES:=$(LINUX_DIR)/drivers/net/ethernet/marvell/mvneta.ko + AUTOLOAD=$(call AutoLoad,30,mvneta) +endef + +$(eval $(call KernelPackage,mvneta)) + +define KernelPackage/mvpp2 + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Marvell Armada 375/7K/8K network driver + DEPENDS:=@(TARGET_armvirt_64) +kmod-marvell-mdio +kmod-phylink + KCONFIG:=CONFIG_MVPP2 \ + CONFIG_MVPP2_PTP=n + FILES=$(LINUX_DIR)/drivers/net/ethernet/marvell/mvpp2/mvpp2.ko + AUTOLOAD=$(call AutoLoad,40,mvpp2) +endef + +$(eval $(call KernelPackage,mvpp2)) + +define KernelPackage/imx2-wdt + SUBMENU:=$(OTHER_MENU) + TITLE:=NXP (Freescale) i.MX2+ and Layerscape watchdog driver + KCONFIG:=CONFIG_IMX2_WDT + FILES=$(LINUX_DIR)/drivers/watchdog/imx2_wdt.ko + AUTOLOAD=$(call AutoLoad,60,imx2_wdt) +endef + +$(eval $(call KernelPackage,imx2-wdt)) + +define KernelPackage/imx7-ulp-wdt + SUBMENU:=$(OTHER_MENU) + TITLE:=NXP (Freescale) i.MX7ULP and later watchdog + KCONFIG:=CONFIG_IMX7ULP_WDT + FILES=$(LINUX_DIR)/drivers/watchdog/imx7ulp_wdt.ko + AUTOLOAD=$(call AutoLoad,60,imx7ulp_wdt) +endef + +$(eval $(call KernelPackage,imx7-ulp-wdt)) + +define KernelPackage/stmmac-core + SUBMENU=$(NETWORK_DEVICES_MENU) + TITLE:=Synopsis Ethernet Controller core (NXP,STMMicro,others) + DEPENDS:=@(TARGET_armvirt_64) +kmod-pcs-xpcs +kmod-ptp \ + +kmod-of-mdio + KCONFIG:=CONFIG_STMMAC_ETH \ + CONFIG_STMMAC_SELFTESTS=n \ + CONFIG_STMMAC_PLATFORM \ + CONFIG_CONFIG_DWMAC_DWC_QOS_ETH=n \ + CONFIG_DWMAC_GENERIC + FILES=$(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/stmmac.ko \ + $(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko \ + $(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.ko + AUTOLOAD=$(call AutoLoad,40,stmmac stmmac-platform dwmac-generic) +endef + +$(eval $(call KernelPackage,stmmac-core)) + +define KernelPackage/dwmac-imx + SUBMENU=$(NETWORK_DEVICES_MENU) + TITLE:=NXP i.MX8 Ethernet controller + DEPENDS:=+kmod-stmmac-core + KCONFIG:=CONFIG_DWMAC_IMX8 + FILES=$(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.ko + AUTOLOAD=$(call AutoLoad,45,dwmac-imx) +endef + +$(eval $(call KernelPackage,dwmac-imx)) + +define KernelPackage/dwmac-sun8i + SUBMENU=$(NETWORK_DEVICES_MENU) + TITLE:=Allwinner H3/A83T/A64 (sun8i) Ethernet + DEPENDS:=+kmod-stmmac-core +kmod-mdio-bus-mux + KCONFIG:=CONFIG_DWMAC_SUN8I + FILES=$(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.ko + AUTOLOAD=$(call AutoLoad,45,dwmac-sun8i) +endef + +$(eval $(call KernelPackage,dwmac-sun8i)) + +define KernelPackage/dwmac-rockchip + SUBMENU=$(NETWORK_DEVICES_MENU) + TITLE:=Rockchip RK3328/RK3399/RK3568 Ethernet + DEPENDS:=+kmod-stmmac-core +kmod-mdio-bus-mux + KCONFIG:=CONFIG_DWMAC_ROCKCHIP + FILES=$(LINUX_DIR)/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.ko + AUTOLOAD=$(call AutoLoad,45,dwmac-rk) +endef + +$(eval $(call KernelPackage,dwmac-rockchip)) + +define KernelPackage/thunderx-net + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Marvell (Cavium) ThunderX/2 network drivers + DEPENDS:=@(TARGET_armvirt_64) +kmod-phylink +kmod-of-mdio + KCONFIG:=CONFIG_NET_VENDOR_CAVIUM \ + CONFIG_THUNDER_NIC_PF \ + CONFIG_THUNDER_NIC_VF \ + CONFIG_THUNDER_NIC_BGX \ + CONFIG_THUNDER_NIC_RGX + FILES=$(LINUX_DIR)/drivers/net/ethernet/cavium/thunder/nicvf.ko \ + $(LINUX_DIR)/drivers/net/ethernet/cavium/thunder/nicpf.ko \ + $(LINUX_DIR)/drivers/net/ethernet/cavium/thunder/thunder_xcv.ko \ + $(LINUX_DIR)/drivers/net/ethernet/cavium/thunder/thunder_bgx.ko + AUTOLOAD=$(call AutoLoad,40,nicpf nicvf thunder_xcv thunder_bgx) +endef + +$(eval $(call KernelPackage,thunderx-net)) + +define KernelPackage/wdt-sp805 + SUBMENU:=$(OTHER_MENU) + TITLE:=ARM SP805 Watchdog + KCONFIG:=CONFIG_ARM_SP805_WATCHDOG + FILES=$(LINUX_DIR)/drivers/watchdog/sp805_wdt.ko + AUTOLOAD=$(call AutoLoad,50,sp805_wdt) +endef + +define KernelPackage/wdt-sp805/description + Support for the ARM SP805 wathchdog module. + This is present in the NXP Layerscape family, + HiSilicon HI3660 among others. +endef + +$(eval $(call KernelPackage,wdt-sp805)) + diff --git a/target/linux/armvirt/patches-6.1/221-armvirt-disable-gc_sections.patch b/target/linux/armvirt/patches-6.1/221-armvirt-disable-gc_sections.patch new file mode 100644 index 00000000000..cb124c1cf8f --- /dev/null +++ b/target/linux/armvirt/patches-6.1/221-armvirt-disable-gc_sections.patch @@ -0,0 +1,23 @@ +From b77c0ecdc7915e5c5c515da1aa6cfaf6f4eb8351 Mon Sep 17 00:00:00 2001 +From: Mathew McBride +Date: Wed, 28 Sep 2022 16:39:31 +1000 +Subject: [PATCH] arm: disable code size reduction measures + (gc-sections,-f*-sections) + +This interferes with the EFI boot stub on armv7l. + +Signed-off-by: Mathew McBride +--- + arch/arm/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -122,7 +122,6 @@ config ARM + select HAVE_UID16 + select HAVE_VIRT_CPU_ACCOUNTING_GEN + select IRQ_FORCED_THREADING +- select HAVE_LD_DEAD_CODE_DATA_ELIMINATION + select MODULES_USE_ELF_REL + select NEED_DMA_MAP_STATE + select OF_EARLY_FLATTREE if OF diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0001-net-dpaa2-eth-don-t-use-ENOTSUPP-error-code.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0001-net-dpaa2-eth-don-t-use-ENOTSUPP-error-code.patch new file mode 100644 index 00000000000..6238c0a9039 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0001-net-dpaa2-eth-don-t-use-ENOTSUPP-error-code.patch @@ -0,0 +1,44 @@ +From f3763a0c1b07273218cbf5886bdf8df9df501111 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:10 +0200 +Subject: [PATCH 03/14] net: dpaa2-eth: don't use -ENOTSUPP error code + +dpaa2_eth_setup_dpni() is called from the probe path and +dpaa2_eth_set_link_ksettings() is propagated to user space. + +include/linux/errno.h says that ENOTSUPP is "Defined for the NFSv3 +protocol". Conventional wisdom has it to not use it in networking +drivers. Replace it with -EOPNOTSUPP. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 2 +- + drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -3613,7 +3613,7 @@ static int dpaa2_eth_setup_dpni(struct f + dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n", + priv->dpni_ver_major, priv->dpni_ver_minor, + DPNI_VER_MAJOR, DPNI_VER_MINOR); +- err = -ENOTSUPP; ++ err = -EOPNOTSUPP; + goto close; + } + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +@@ -118,7 +118,7 @@ dpaa2_eth_set_link_ksettings(struct net_ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + + if (!dpaa2_eth_is_type_phy(priv)) +- return -ENOTSUPP; ++ return -EOPNOTSUPP; + + return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings); + } diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0002-net-dpaa2-replace-dpaa2_mac_is_type_fixed-with-dpaa2.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0002-net-dpaa2-replace-dpaa2_mac_is_type_fixed-with-dpaa2.patch new file mode 100644 index 00000000000..501eaf42ff3 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0002-net-dpaa2-replace-dpaa2_mac_is_type_fixed-with-dpaa2.patch @@ -0,0 +1,99 @@ +From 022a11062261dc4703da846d3bf4d194ef6bebf5 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:11 +0200 +Subject: [PATCH 04/14] net: dpaa2: replace dpaa2_mac_is_type_fixed() with + dpaa2_mac_is_type_phy() + +dpaa2_mac_is_type_fixed() is a header with no implementation and no +callers, which is referenced from the documentation though. It can be +deleted. + +On the other hand, it would be useful to reuse the code between +dpaa2_eth_is_type_phy() and dpaa2_switch_port_is_type_phy(). That common +code should be called dpaa2_mac_is_type_phy(), so let's create that. + +The removal and the addition are merged into the same patch because, +in fact, is_type_phy() is the logical opposite of is_type_fixed(). + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../ethernet/freescale/dpaa2/mac-phy-support.rst | 9 ++++++--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 7 +------ + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 10 ++++++++-- + drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h | 7 +------ + 4 files changed, 16 insertions(+), 17 deletions(-) + +--- a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst ++++ b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst +@@ -181,10 +181,13 @@ when necessary using the below listed AP + - int dpaa2_mac_connect(struct dpaa2_mac *mac); + - void dpaa2_mac_disconnect(struct dpaa2_mac *mac); + +-A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED. +-One can check for this condition using the below API:: ++A phylink integration is necessary only when the partner DPMAC is not of ++``TYPE_FIXED``. This means it is either of ``TYPE_PHY``, or of ++``TYPE_BACKPLANE`` (the difference being the two that in the ``TYPE_BACKPLANE`` ++mode, the MC firmware does not access the PCS registers). One can check for ++this condition using the following helper:: + +- - bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io); ++ - static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac); + + Before connection to a MAC, the caller must allocate and populate the + dpaa2_mac structure with the associated net_device, a pointer to the MC portal +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +@@ -733,12 +733,7 @@ static inline unsigned int dpaa2_eth_rx_ + + static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv) + { +- if (priv->mac && +- (priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY || +- priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE)) +- return true; +- +- return false; ++ return dpaa2_mac_is_type_phy(priv->mac); + } + + static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv) +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +@@ -30,8 +30,14 @@ struct dpaa2_mac { + struct phy *serdes_phy; + }; + +-bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, +- struct fsl_mc_io *mc_io); ++static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac) ++{ ++ if (!mac) ++ return false; ++ ++ return mac->attr.link_type == DPMAC_LINK_TYPE_PHY || ++ mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE; ++} + + int dpaa2_mac_open(struct dpaa2_mac *mac); + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h +@@ -230,12 +230,7 @@ static inline bool dpaa2_switch_supports + static inline bool + dpaa2_switch_port_is_type_phy(struct ethsw_port_priv *port_priv) + { +- if (port_priv->mac && +- (port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY || +- port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE)) +- return true; +- +- return false; ++ return dpaa2_mac_is_type_phy(port_priv->mac); + } + + static inline bool dpaa2_switch_port_has_mac(struct ethsw_port_priv *port_priv) diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0003-net-dpaa2-mac-absorb-phylink_start-call-into-dpaa2_m.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0003-net-dpaa2-mac-absorb-phylink_start-call-into-dpaa2_m.patch new file mode 100644 index 00000000000..e84195f3cd7 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0003-net-dpaa2-mac-absorb-phylink_start-call-into-dpaa2_m.patch @@ -0,0 +1,88 @@ +From 97c07369ab8bf9895e05d4b468f18e6567263154 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:12 +0200 +Subject: [PATCH 05/14] net: dpaa2-mac: absorb phylink_start() call into + dpaa2_mac_start() + +The phylink handling is intended to be hidden inside the dpaa2_mac +object. Move the phylink_start() call into dpaa2_mac_start(), and +phylink_stop() into dpaa2_mac_stop(). + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 5 +---- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 8 ++++++++ + drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 5 +---- + 3 files changed, 10 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -2082,10 +2082,8 @@ static int dpaa2_eth_open(struct net_dev + goto enable_err; + } + +- if (dpaa2_eth_is_type_phy(priv)) { ++ if (dpaa2_eth_is_type_phy(priv)) + dpaa2_mac_start(priv->mac); +- phylink_start(priv->mac->phylink); +- } + + return 0; + +@@ -2159,7 +2157,6 @@ static int dpaa2_eth_stop(struct net_dev + int retries = 10; + + if (dpaa2_eth_is_type_phy(priv)) { +- phylink_stop(priv->mac->phylink); + dpaa2_mac_stop(priv->mac); + } else { + netif_tx_stop_all_queues(net_dev); +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -336,12 +336,20 @@ static void dpaa2_mac_set_supported_inte + + void dpaa2_mac_start(struct dpaa2_mac *mac) + { ++ ASSERT_RTNL(); ++ + if (mac->serdes_phy) + phy_power_on(mac->serdes_phy); ++ ++ phylink_start(mac->phylink); + } + + void dpaa2_mac_stop(struct dpaa2_mac *mac) + { ++ ASSERT_RTNL(); ++ ++ phylink_stop(mac->phylink); ++ + if (mac->serdes_phy) + phy_power_off(mac->serdes_phy); + } +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +@@ -703,10 +703,8 @@ static int dpaa2_switch_port_open(struct + + dpaa2_switch_enable_ctrl_if_napi(ethsw); + +- if (dpaa2_switch_port_is_type_phy(port_priv)) { ++ if (dpaa2_switch_port_is_type_phy(port_priv)) + dpaa2_mac_start(port_priv->mac); +- phylink_start(port_priv->mac->phylink); +- } + + return 0; + } +@@ -718,7 +716,6 @@ static int dpaa2_switch_port_stop(struct + int err; + + if (dpaa2_switch_port_is_type_phy(port_priv)) { +- phylink_stop(port_priv->mac->phylink); + dpaa2_mac_stop(port_priv->mac); + } else { + netif_tx_stop_all_queues(netdev); diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0004-net-dpaa2-mac-remove-defensive-check-in-dpaa2_mac_di.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0004-net-dpaa2-mac-remove-defensive-check-in-dpaa2_mac_di.patch new file mode 100644 index 00000000000..c3028357fe5 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0004-net-dpaa2-mac-remove-defensive-check-in-dpaa2_mac_di.patch @@ -0,0 +1,50 @@ +From 095ef388f714d622aa503fcccf20dc4095b72762 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:13 +0200 +Subject: [PATCH 06/14] net: dpaa2-mac: remove defensive check in + dpaa2_mac_disconnect() + +dpaa2_mac_disconnect() will only be called with a NULL mac->phylink if +dpaa2_mac_connect() failed, or was never called. + +The callers are these: + +dpaa2_eth_disconnect_mac(): + + if (dpaa2_eth_is_type_phy(priv)) + dpaa2_mac_disconnect(priv->mac); + +dpaa2_switch_port_disconnect_mac(): + + if (dpaa2_switch_port_is_type_phy(port_priv)) + dpaa2_mac_disconnect(port_priv->mac); + +priv->mac can be NULL, but in that case, dpaa2_eth_is_type_phy() returns +false, and dpaa2_mac_disconnect() is never called. Similar for +dpaa2-switch. + +When priv->mac is non-NULL, it means that dpaa2_mac_connect() returned +zero (success), and therefore, priv->mac->phylink is also a valid +pointer. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -446,9 +446,6 @@ err_pcs_destroy: + + void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + { +- if (!mac->phylink) +- return; +- + phylink_disconnect_phy(mac->phylink); + phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch new file mode 100644 index 00000000000..8c7ed6c793f --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0005-net-dpaa2-eth-assign-priv-mac-after-dpaa2_mac_connec.patch @@ -0,0 +1,101 @@ +From 06efc9b8a1360cad83cae6e71558e5458cc1fbf3 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:14 +0200 +Subject: [PATCH 07/14] net: dpaa2-eth: assign priv->mac after + dpaa2_mac_connect() call + +There are 2 requirements for correct code: + +- Any time the driver accesses the priv->mac pointer at runtime, it + either holds NULL to indicate a DPNI-DPNI connection (or unconnected + DPNI), or a struct dpaa2_mac whose phylink instance was fully + initialized (created and connected to the PHY). No changes are made to + priv->mac while it is being used. Currently, rtnl_lock() watches over + the call to dpaa2_eth_connect_mac(), so it serves the purpose of + serializing this with all readers of priv->mac. + +- dpaa2_mac_connect() should run unlocked, because inside it are 2 + phylink calls with incompatible locking requirements: phylink_create() + requires that the rtnl_mutex isn't held, and phylink_fwnode_phy_connect() + requires that the rtnl_mutex is held. The only way to solve those + contradictory requirements is to let dpaa2_mac_connect() take + rtnl_lock() when it needs to. + +To solve both requirements, we need to identify the writer side of the +priv->mac pointer, which can be wrapped in a mutex private to the driver +in a future patch. The dpaa2_mac_connect() cannot be part of the writer +side critical section, because of an AB/BA deadlock with rtnl_lock(). + +So the strategy needs to be that where we prepare the DPMAC by calling +dpaa2_mac_connect(), and only make priv->mac point to it once it's fully +prepared. This ensures that the writer side critical section has the +absolute minimum surface it can. + +The reverse strategy is adopted in the dpaa2_eth_disconnect_mac() code +path. This makes sure that priv->mac is NULL when we start tearing down +the DPMAC that we disconnected from, and concurrent code will simply not +see it. + +No locking changes in this patch (concurrent code is still blocked by +the rtnl_mutex). + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 21 +++++++++++-------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -4443,9 +4443,8 @@ static int dpaa2_eth_connect_mac(struct + err = dpaa2_mac_open(mac); + if (err) + goto err_free_mac; +- priv->mac = mac; + +- if (dpaa2_eth_is_type_phy(priv)) { ++ if (dpaa2_mac_is_type_phy(mac)) { + err = dpaa2_mac_connect(mac); + if (err && err != -EPROBE_DEFER) + netdev_err(priv->net_dev, "Error connecting to the MAC endpoint: %pe", +@@ -4454,11 +4453,12 @@ static int dpaa2_eth_connect_mac(struct + goto err_close_mac; + } + ++ priv->mac = mac; ++ + return 0; + + err_close_mac: + dpaa2_mac_close(mac); +- priv->mac = NULL; + err_free_mac: + kfree(mac); + return err; +@@ -4466,15 +4466,18 @@ err_free_mac: + + static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) + { +- if (dpaa2_eth_is_type_phy(priv)) +- dpaa2_mac_disconnect(priv->mac); ++ struct dpaa2_mac *mac = priv->mac; ++ ++ priv->mac = NULL; + +- if (!dpaa2_eth_has_mac(priv)) ++ if (!mac) + return; + +- dpaa2_mac_close(priv->mac); +- kfree(priv->mac); +- priv->mac = NULL; ++ if (dpaa2_mac_is_type_phy(mac)) ++ dpaa2_mac_disconnect(mac); ++ ++ dpaa2_mac_close(mac); ++ kfree(mac); + } + + static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0006-net-dpaa2-switch-assign-port_priv-mac-after-dpaa2_ma.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0006-net-dpaa2-switch-assign-port_priv-mac-after-dpaa2_ma.patch new file mode 100644 index 00000000000..e63654984a5 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0006-net-dpaa2-switch-assign-port_priv-mac-after-dpaa2_ma.patch @@ -0,0 +1,73 @@ +From a5e7f7e277bd4403c45c1c7922d56d0eb08dbc7c Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:15 +0200 +Subject: [PATCH 08/14] net: dpaa2-switch: assign port_priv->mac after + dpaa2_mac_connect() call + +The dpaa2-switch has the exact same locking requirements when connected +to a DPMAC, so it needs port_priv->mac to always point either to NULL, +or to a DPMAC with a fully initialized phylink instance. + +Make the same preparatory change in the dpaa2-switch driver as in the +dpaa2-eth one. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../ethernet/freescale/dpaa2/dpaa2-switch.c | 21 +++++++++++-------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +@@ -1450,9 +1450,8 @@ static int dpaa2_switch_port_connect_mac + err = dpaa2_mac_open(mac); + if (err) + goto err_free_mac; +- port_priv->mac = mac; + +- if (dpaa2_switch_port_is_type_phy(port_priv)) { ++ if (dpaa2_mac_is_type_phy(mac)) { + err = dpaa2_mac_connect(mac); + if (err) { + netdev_err(port_priv->netdev, +@@ -1462,11 +1461,12 @@ static int dpaa2_switch_port_connect_mac + } + } + ++ port_priv->mac = mac; ++ + return 0; + + err_close_mac: + dpaa2_mac_close(mac); +- port_priv->mac = NULL; + err_free_mac: + kfree(mac); + return err; +@@ -1474,15 +1474,18 @@ err_free_mac: + + static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) + { +- if (dpaa2_switch_port_is_type_phy(port_priv)) +- dpaa2_mac_disconnect(port_priv->mac); ++ struct dpaa2_mac *mac = port_priv->mac; ++ ++ port_priv->mac = NULL; + +- if (!dpaa2_switch_port_has_mac(port_priv)) ++ if (!mac) + return; + +- dpaa2_mac_close(port_priv->mac); +- kfree(port_priv->mac); +- port_priv->mac = NULL; ++ if (dpaa2_mac_is_type_phy(mac)) ++ dpaa2_mac_disconnect(mac); ++ ++ dpaa2_mac_close(mac); ++ kfree(mac); + } + + static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0007-net-dpaa2-publish-MAC-stringset-to-ethtool-S-even-if.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0007-net-dpaa2-publish-MAC-stringset-to-ethtool-S-even-if.patch new file mode 100644 index 00000000000..c790ba1bd50 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0007-net-dpaa2-publish-MAC-stringset-to-ethtool-S-even-if.patch @@ -0,0 +1,111 @@ +From ce44b6ed9ee65efa9b3025552c513842eabcab88 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:16 +0200 +Subject: [PATCH 09/14] net: dpaa2: publish MAC stringset to ethtool -S even if + MAC is missing + +DPNIs and DPSW objects can connect and disconnect at runtime from DPMAC +objects on the same fsl-mc bus. The DPMAC object also holds "ethtool -S" +unstructured counters. Those counters are only shown for the entity +owning the netdev (DPNI, DPSW) if it's connected to a DPMAC. + +The ethtool stringset code path is split into multiple callbacks, but +currently, connecting and disconnecting the DPMAC takes the rtnl_lock(). +This blocks the entire ethtool code path from running, see +ethnl_default_doit() -> rtnl_lock() -> ops->prepare_data() -> +strset_prepare_data(). + +This is going to be a problem if we are going to no longer require +rtnl_lock() when connecting/disconnecting the DPMAC, because the DPMAC +could appear between ops->get_sset_count() and ops->get_strings(). +If it appears out of the blue, we will provide a stringset into an array +that was dimensioned thinking the DPMAC wouldn't be there => array +accessed out of bounds. + +There isn't really a good way to work around that, and I don't want to +put too much pressure on the ethtool framework by playing locking games. +Just make the DPMAC counters be always available. They'll be zeroes if +the DPNI or DPSW isn't connected to a DPMAC. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 12 +++--------- + .../ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 11 ++--------- + 2 files changed, 5 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +@@ -186,7 +186,6 @@ static int dpaa2_eth_set_pauseparam(stru + static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) + { +- struct dpaa2_eth_priv *priv = netdev_priv(netdev); + u8 *p = data; + int i; + +@@ -200,22 +199,17 @@ static void dpaa2_eth_get_strings(struct + strscpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } +- if (dpaa2_eth_has_mac(priv)) +- dpaa2_mac_get_strings(p); ++ dpaa2_mac_get_strings(p); + break; + } + } + + static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) + { +- int num_ss_stats = DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS; +- struct dpaa2_eth_priv *priv = netdev_priv(net_dev); +- + switch (sset) { + case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */ +- if (dpaa2_eth_has_mac(priv)) +- num_ss_stats += dpaa2_mac_get_sset_count(); +- return num_ss_stats; ++ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS + ++ dpaa2_mac_get_sset_count(); + default: + return -EOPNOTSUPP; + } +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +@@ -145,14 +145,9 @@ dpaa2_switch_set_link_ksettings(struct n + static int + dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset) + { +- struct ethsw_port_priv *port_priv = netdev_priv(netdev); +- int num_ss_stats = DPAA2_SWITCH_NUM_COUNTERS; +- + switch (sset) { + case ETH_SS_STATS: +- if (port_priv->mac) +- num_ss_stats += dpaa2_mac_get_sset_count(); +- return num_ss_stats; ++ return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count(); + default: + return -EOPNOTSUPP; + } +@@ -161,7 +156,6 @@ dpaa2_switch_ethtool_get_sset_count(stru + static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) + { +- struct ethsw_port_priv *port_priv = netdev_priv(netdev); + u8 *p = data; + int i; + +@@ -172,8 +166,7 @@ static void dpaa2_switch_ethtool_get_str + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } +- if (port_priv->mac) +- dpaa2_mac_get_strings(p); ++ dpaa2_mac_get_strings(p); + break; + } + } diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0008-net-dpaa2-switch-replace-direct-MAC-access-with-dpaa.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0008-net-dpaa2-switch-replace-direct-MAC-access-with-dpaa.patch new file mode 100644 index 00000000000..0663bf6fb10 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0008-net-dpaa2-switch-replace-direct-MAC-access-with-dpaa.patch @@ -0,0 +1,28 @@ +From c838d9fd7e6ba9ddd6006bf0a296396266e9f121 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:17 +0200 +Subject: [PATCH 10/14] net: dpaa2-switch replace direct MAC access with + dpaa2_switch_port_has_mac() + +The helper function will gain a lockdep annotation in a future patch. +Make sure to benefit from it. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +@@ -189,7 +189,7 @@ static void dpaa2_switch_ethtool_get_sta + dpaa2_switch_ethtool_counters[i].name, err); + } + +- if (port_priv->mac) ++ if (dpaa2_switch_port_has_mac(port_priv)) + dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); + } + diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0009-net-dpaa2-eth-connect-to-MAC-before-requesting-the-e.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0009-net-dpaa2-eth-connect-to-MAC-before-requesting-the-e.patch new file mode 100644 index 00000000000..37831f264b8 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0009-net-dpaa2-eth-connect-to-MAC-before-requesting-the-e.patch @@ -0,0 +1,93 @@ +From e0ea63162cb5f1ca7f844d6ef5fc4079448ee2d5 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:18 +0200 +Subject: [PATCH 11/14] net: dpaa2-eth: connect to MAC before requesting the + "endpoint changed" IRQ + +dpaa2_eth_connect_mac() is called both from dpaa2_eth_probe() and from +dpni_irq0_handler_thread(). + +It could happen that the DPNI gets connected to a DPMAC on the fsl-mc +bus exactly during probe, as soon as the "endpoint change" interrupt is +requested in dpaa2_eth_setup_irqs(). This will cause the +dpni_irq0_handler_thread() to register a phylink instance for that DPMAC. + +Then, the probing function will also try to register a phylink instance +for the same DPMAC, operation which should fail (and this will fail the +probing of the driver). + +Reorder dpaa2_eth_setup_irqs() and dpaa2_eth_connect_mac(), such that +dpni_irq0_handler_thread() never races with the DPMAC-related portion of +the probing path. + +Also reorder dpaa2_eth_disconnect_mac() to be in the mirror position of +dpaa2_eth_connect_mac() in the teardown path. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -4710,6 +4710,10 @@ static int dpaa2_eth_probe(struct fsl_mc + } + #endif + ++ err = dpaa2_eth_connect_mac(priv); ++ if (err) ++ goto err_connect_mac; ++ + err = dpaa2_eth_setup_irqs(dpni_dev); + if (err) { + netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n"); +@@ -4722,10 +4726,6 @@ static int dpaa2_eth_probe(struct fsl_mc + priv->do_link_poll = true; + } + +- err = dpaa2_eth_connect_mac(priv); +- if (err) +- goto err_connect_mac; +- + err = dpaa2_eth_dl_alloc(priv); + if (err) + goto err_dl_register; +@@ -4759,13 +4759,13 @@ err_dl_port_add: + err_dl_trap_register: + dpaa2_eth_dl_free(priv); + err_dl_register: +- dpaa2_eth_disconnect_mac(priv); +-err_connect_mac: + if (priv->do_link_poll) + kthread_stop(priv->poll_thread); + else + fsl_mc_free_irqs(dpni_dev); + err_poll_thread: ++ dpaa2_eth_disconnect_mac(priv); ++err_connect_mac: + dpaa2_eth_free_rings(priv); + err_alloc_rings: + err_csum: +@@ -4813,9 +4813,6 @@ static int dpaa2_eth_remove(struct fsl_m + #endif + + unregister_netdev(net_dev); +- rtnl_lock(); +- dpaa2_eth_disconnect_mac(priv); +- rtnl_unlock(); + + dpaa2_eth_dl_port_del(priv); + dpaa2_eth_dl_traps_unregister(priv); +@@ -4826,6 +4823,9 @@ static int dpaa2_eth_remove(struct fsl_m + else + fsl_mc_free_irqs(ls_dev); + ++ rtnl_lock(); ++ dpaa2_eth_disconnect_mac(priv); ++ rtnl_unlock(); + dpaa2_eth_free_rings(priv); + free_percpu(priv->fd); + free_percpu(priv->sgt_cache); diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0010-net-dpaa2-eth-serialize-changes-to-priv-mac-with-a-m.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0010-net-dpaa2-eth-serialize-changes-to-priv-mac-with-a-m.patch new file mode 100644 index 00000000000..89f58412d40 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0010-net-dpaa2-eth-serialize-changes-to-priv-mac-with-a-m.patch @@ -0,0 +1,320 @@ +From 5e448a17dfa2e95166534df7f677a3694ef6187d Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:19 +0200 +Subject: [PATCH 12/14] net: dpaa2-eth: serialize changes to priv->mac with a + mutex + +The dpaa2 architecture permits dynamic connections between objects on +the fsl-mc bus, specifically between a DPNI object (represented by a +struct net_device) and a DPMAC object (represented by a struct phylink). + +The DPNI driver is notified when those connections are created/broken +through the dpni_irq0_handler_thread() method. To ensure that ethtool +operations, as well as netdev up/down operations serialize with the +connection/disconnection of the DPNI with a DPMAC, +dpni_irq0_handler_thread() takes the rtnl_lock() to block those other +operations from taking place. + +There is code called by dpaa2_mac_connect() which wants to acquire the +rtnl_mutex once again, see phylink_create() -> phylink_register_sfp() -> +sfp_bus_add_upstream() -> rtnl_lock(). So the strategy doesn't quite +work out, even though it's fairly simple. + +Create a different strategy, where all code paths in the dpaa2-eth +driver access priv->mac only while they are holding priv->mac_lock. +The phylink instance is not created or connected to the PHY under the +priv->mac_lock, but only assigned to priv->mac then. This will eliminate +the reliance on the rtnl_mutex. + +Add lockdep annotations and put comments where holding the lock is not +necessary, and priv->mac can be dereferenced freely. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 43 ++++++++++++-- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 6 ++ + .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 58 +++++++++++++++---- + 3 files changed, 91 insertions(+), 16 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -2020,8 +2020,11 @@ static int dpaa2_eth_link_state_update(s + + /* When we manage the MAC/PHY using phylink there is no need + * to manually update the netif_carrier. ++ * We can avoid locking because we are called from the "link changed" ++ * IRQ handler, which is the same as the "endpoint changed" IRQ handler ++ * (the writer to priv->mac), so we cannot race with it. + */ +- if (dpaa2_eth_is_type_phy(priv)) ++ if (dpaa2_mac_is_type_phy(priv->mac)) + goto out; + + /* Chech link state; speed / duplex changes are not treated yet */ +@@ -2060,6 +2063,8 @@ static int dpaa2_eth_open(struct net_dev + priv->dpbp_dev->obj_desc.id, priv->bpid); + } + ++ mutex_lock(&priv->mac_lock); ++ + if (!dpaa2_eth_is_type_phy(priv)) { + /* We'll only start the txqs when the link is actually ready; + * make sure we don't race against the link up notification, +@@ -2078,6 +2083,7 @@ static int dpaa2_eth_open(struct net_dev + + err = dpni_enable(priv->mc_io, 0, priv->mc_token); + if (err < 0) { ++ mutex_unlock(&priv->mac_lock); + netdev_err(net_dev, "dpni_enable() failed\n"); + goto enable_err; + } +@@ -2085,6 +2091,8 @@ static int dpaa2_eth_open(struct net_dev + if (dpaa2_eth_is_type_phy(priv)) + dpaa2_mac_start(priv->mac); + ++ mutex_unlock(&priv->mac_lock); ++ + return 0; + + enable_err: +@@ -2156,6 +2164,8 @@ static int dpaa2_eth_stop(struct net_dev + int dpni_enabled = 0; + int retries = 10; + ++ mutex_lock(&priv->mac_lock); ++ + if (dpaa2_eth_is_type_phy(priv)) { + dpaa2_mac_stop(priv->mac); + } else { +@@ -2163,6 +2173,8 @@ static int dpaa2_eth_stop(struct net_dev + netif_carrier_off(net_dev); + } + ++ mutex_unlock(&priv->mac_lock); ++ + /* On dpni_disable(), the MC firmware will: + * - stop MAC Rx and wait for all Rx frames to be enqueued to software + * - cut off WRIOP dequeues from egress FQs and wait until transmission +@@ -2488,12 +2500,20 @@ static int dpaa2_eth_ts_ioctl(struct net + static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + { + struct dpaa2_eth_priv *priv = netdev_priv(dev); ++ int err; + + if (cmd == SIOCSHWTSTAMP) + return dpaa2_eth_ts_ioctl(dev, rq, cmd); + +- if (dpaa2_eth_is_type_phy(priv)) +- return phylink_mii_ioctl(priv->mac->phylink, rq, cmd); ++ mutex_lock(&priv->mac_lock); ++ ++ if (dpaa2_eth_is_type_phy(priv)) { ++ err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd); ++ mutex_unlock(&priv->mac_lock); ++ return err; ++ } ++ ++ mutex_unlock(&priv->mac_lock); + + return -EOPNOTSUPP; + } +@@ -4453,7 +4473,9 @@ static int dpaa2_eth_connect_mac(struct + goto err_close_mac; + } + ++ mutex_lock(&priv->mac_lock); + priv->mac = mac; ++ mutex_unlock(&priv->mac_lock); + + return 0; + +@@ -4466,9 +4488,12 @@ err_free_mac: + + static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) + { +- struct dpaa2_mac *mac = priv->mac; ++ struct dpaa2_mac *mac; + ++ mutex_lock(&priv->mac_lock); ++ mac = priv->mac; + priv->mac = NULL; ++ mutex_unlock(&priv->mac_lock); + + if (!mac) + return; +@@ -4487,6 +4512,7 @@ static irqreturn_t dpni_irq0_handler_thr + struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); + struct net_device *net_dev = dev_get_drvdata(dev); + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); ++ bool had_mac; + int err; + + err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, +@@ -4504,7 +4530,12 @@ static irqreturn_t dpni_irq0_handler_thr + dpaa2_eth_update_tx_fqids(priv); + + rtnl_lock(); +- if (dpaa2_eth_has_mac(priv)) ++ /* We can avoid locking because the "endpoint changed" IRQ ++ * handler is the only one who changes priv->mac at runtime, ++ * so we are not racing with anyone. ++ */ ++ had_mac = !!priv->mac; ++ if (had_mac) + dpaa2_eth_disconnect_mac(priv); + else + dpaa2_eth_connect_mac(priv); +@@ -4605,6 +4636,8 @@ static int dpaa2_eth_probe(struct fsl_mc + priv = netdev_priv(net_dev); + priv->net_dev = net_dev; + ++ mutex_init(&priv->mac_lock); ++ + priv->iommu_domain = iommu_get_domain_for_dev(dev); + + priv->tx_tstamp_type = HWTSTAMP_TX_OFF; +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +@@ -580,6 +580,8 @@ struct dpaa2_eth_priv { + #endif + + struct dpaa2_mac *mac; ++ /* Serializes changes to priv->mac */ ++ struct mutex mac_lock; + struct workqueue_struct *dpaa2_ptp_wq; + struct work_struct tx_onestep_tstamp; + struct sk_buff_head tx_skbs; +@@ -733,11 +735,15 @@ static inline unsigned int dpaa2_eth_rx_ + + static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv) + { ++ lockdep_assert_held(&priv->mac_lock); ++ + return dpaa2_mac_is_type_phy(priv->mac); + } + + static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv) + { ++ lockdep_assert_held(&priv->mac_lock); ++ + return priv->mac ? true : false; + } + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +@@ -86,11 +86,16 @@ static void dpaa2_eth_get_drvinfo(struct + static int dpaa2_eth_nway_reset(struct net_device *net_dev) + { + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); ++ int err = -EOPNOTSUPP; ++ ++ mutex_lock(&priv->mac_lock); + + if (dpaa2_eth_is_type_phy(priv)) +- return phylink_ethtool_nway_reset(priv->mac->phylink); ++ err = phylink_ethtool_nway_reset(priv->mac->phylink); ++ ++ mutex_unlock(&priv->mac_lock); + +- return -EOPNOTSUPP; ++ return err; + } + + static int +@@ -98,10 +103,18 @@ dpaa2_eth_get_link_ksettings(struct net_ + struct ethtool_link_ksettings *link_settings) + { + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); ++ int err; + +- if (dpaa2_eth_is_type_phy(priv)) +- return phylink_ethtool_ksettings_get(priv->mac->phylink, +- link_settings); ++ mutex_lock(&priv->mac_lock); ++ ++ if (dpaa2_eth_is_type_phy(priv)) { ++ err = phylink_ethtool_ksettings_get(priv->mac->phylink, ++ link_settings); ++ mutex_unlock(&priv->mac_lock); ++ return err; ++ } ++ ++ mutex_unlock(&priv->mac_lock); + + link_settings->base.autoneg = AUTONEG_DISABLE; + if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX)) +@@ -116,11 +129,17 @@ dpaa2_eth_set_link_ksettings(struct net_ + const struct ethtool_link_ksettings *link_settings) + { + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); ++ int err = -EOPNOTSUPP; + +- if (!dpaa2_eth_is_type_phy(priv)) +- return -EOPNOTSUPP; ++ mutex_lock(&priv->mac_lock); ++ ++ if (dpaa2_eth_is_type_phy(priv)) ++ err = phylink_ethtool_ksettings_set(priv->mac->phylink, ++ link_settings); + +- return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings); ++ mutex_unlock(&priv->mac_lock); ++ ++ return err; + } + + static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, +@@ -129,11 +148,16 @@ static void dpaa2_eth_get_pauseparam(str + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u64 link_options = priv->link_state.options; + ++ mutex_lock(&priv->mac_lock); ++ + if (dpaa2_eth_is_type_phy(priv)) { + phylink_ethtool_get_pauseparam(priv->mac->phylink, pause); ++ mutex_unlock(&priv->mac_lock); + return; + } + ++ mutex_unlock(&priv->mac_lock); ++ + pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options); + pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options); + pause->autoneg = AUTONEG_DISABLE; +@@ -152,9 +176,17 @@ static int dpaa2_eth_set_pauseparam(stru + return -EOPNOTSUPP; + } + +- if (dpaa2_eth_is_type_phy(priv)) +- return phylink_ethtool_set_pauseparam(priv->mac->phylink, +- pause); ++ mutex_lock(&priv->mac_lock); ++ ++ if (dpaa2_eth_is_type_phy(priv)) { ++ err = phylink_ethtool_set_pauseparam(priv->mac->phylink, ++ pause); ++ mutex_unlock(&priv->mac_lock); ++ return err; ++ } ++ ++ mutex_unlock(&priv->mac_lock); ++ + if (pause->autoneg) + return -EOPNOTSUPP; + +@@ -309,8 +341,12 @@ static void dpaa2_eth_get_ethtool_stats( + } + *(data + i++) = buf_cnt; + ++ mutex_lock(&priv->mac_lock); ++ + if (dpaa2_eth_has_mac(priv)) + dpaa2_mac_get_ethtool_stats(priv->mac, data + i); ++ ++ mutex_unlock(&priv->mac_lock); + } + + static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0011-net-dpaa2-switch-serialize-changes-to-priv-mac-with.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0011-net-dpaa2-switch-serialize-changes-to-priv-mac-with.patch new file mode 100644 index 00000000000..7ea446516bf --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0011-net-dpaa2-switch-serialize-changes-to-priv-mac-with.patch @@ -0,0 +1,203 @@ +From 80d12452a5f160c39d63efc1be07df36f9d07133 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:20 +0200 +Subject: [PATCH 13/14] net: dpaa2-switch: serialize changes to priv->mac with + a mutex + +The dpaa2-switch driver uses a DPMAC in the same way as the dpaa2-eth +driver, so we need to duplicate the locking solution established by the +previous change to the switch driver as well. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + .../freescale/dpaa2/dpaa2-switch-ethtool.c | 32 +++++++++++++++---- + .../ethernet/freescale/dpaa2/dpaa2-switch.c | 31 ++++++++++++++++-- + .../ethernet/freescale/dpaa2/dpaa2-switch.h | 2 ++ + 3 files changed, 55 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +@@ -60,11 +60,18 @@ dpaa2_switch_get_link_ksettings(struct n + { + struct ethsw_port_priv *port_priv = netdev_priv(netdev); + struct dpsw_link_state state = {0}; +- int err = 0; ++ int err; + +- if (dpaa2_switch_port_is_type_phy(port_priv)) +- return phylink_ethtool_ksettings_get(port_priv->mac->phylink, +- link_ksettings); ++ mutex_lock(&port_priv->mac_lock); ++ ++ if (dpaa2_switch_port_is_type_phy(port_priv)) { ++ err = phylink_ethtool_ksettings_get(port_priv->mac->phylink, ++ link_ksettings); ++ mutex_unlock(&port_priv->mac_lock); ++ return err; ++ } ++ ++ mutex_unlock(&port_priv->mac_lock); + + err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, + port_priv->ethsw_data->dpsw_handle, +@@ -99,9 +106,16 @@ dpaa2_switch_set_link_ksettings(struct n + bool if_running; + int err = 0, ret; + +- if (dpaa2_switch_port_is_type_phy(port_priv)) +- return phylink_ethtool_ksettings_set(port_priv->mac->phylink, +- link_ksettings); ++ mutex_lock(&port_priv->mac_lock); ++ ++ if (dpaa2_switch_port_is_type_phy(port_priv)) { ++ err = phylink_ethtool_ksettings_set(port_priv->mac->phylink, ++ link_ksettings); ++ mutex_unlock(&port_priv->mac_lock); ++ return err; ++ } ++ ++ mutex_unlock(&port_priv->mac_lock); + + /* Interface needs to be down to change link settings */ + if_running = netif_running(netdev); +@@ -189,8 +203,12 @@ static void dpaa2_switch_ethtool_get_sta + dpaa2_switch_ethtool_counters[i].name, err); + } + ++ mutex_lock(&port_priv->mac_lock); ++ + if (dpaa2_switch_port_has_mac(port_priv)) + dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); ++ ++ mutex_unlock(&port_priv->mac_lock); + } + + const struct ethtool_ops dpaa2_switch_port_ethtool_ops = { +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +@@ -603,8 +603,11 @@ static int dpaa2_switch_port_link_state_ + + /* When we manage the MAC/PHY using phylink there is no need + * to manually update the netif_carrier. ++ * We can avoid locking because we are called from the "link changed" ++ * IRQ handler, which is the same as the "endpoint changed" IRQ handler ++ * (the writer to port_priv->mac), so we cannot race with it. + */ +- if (dpaa2_switch_port_is_type_phy(port_priv)) ++ if (dpaa2_mac_is_type_phy(port_priv->mac)) + return 0; + + /* Interrupts are received even though no one issued an 'ifconfig up' +@@ -684,6 +687,8 @@ static int dpaa2_switch_port_open(struct + struct ethsw_core *ethsw = port_priv->ethsw_data; + int err; + ++ mutex_lock(&port_priv->mac_lock); ++ + if (!dpaa2_switch_port_is_type_phy(port_priv)) { + /* Explicitly set carrier off, otherwise + * netif_carrier_ok() will return true and cause 'ip link show' +@@ -697,6 +702,7 @@ static int dpaa2_switch_port_open(struct + port_priv->ethsw_data->dpsw_handle, + port_priv->idx); + if (err) { ++ mutex_unlock(&port_priv->mac_lock); + netdev_err(netdev, "dpsw_if_enable err %d\n", err); + return err; + } +@@ -706,6 +712,8 @@ static int dpaa2_switch_port_open(struct + if (dpaa2_switch_port_is_type_phy(port_priv)) + dpaa2_mac_start(port_priv->mac); + ++ mutex_unlock(&port_priv->mac_lock); ++ + return 0; + } + +@@ -715,6 +723,8 @@ static int dpaa2_switch_port_stop(struct + struct ethsw_core *ethsw = port_priv->ethsw_data; + int err; + ++ mutex_lock(&port_priv->mac_lock); ++ + if (dpaa2_switch_port_is_type_phy(port_priv)) { + dpaa2_mac_stop(port_priv->mac); + } else { +@@ -722,6 +732,8 @@ static int dpaa2_switch_port_stop(struct + netif_carrier_off(netdev); + } + ++ mutex_unlock(&port_priv->mac_lock); ++ + err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0, + port_priv->ethsw_data->dpsw_handle, + port_priv->idx); +@@ -1461,7 +1473,9 @@ static int dpaa2_switch_port_connect_mac + } + } + ++ mutex_lock(&port_priv->mac_lock); + port_priv->mac = mac; ++ mutex_unlock(&port_priv->mac_lock); + + return 0; + +@@ -1474,9 +1488,12 @@ err_free_mac: + + static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) + { +- struct dpaa2_mac *mac = port_priv->mac; ++ struct dpaa2_mac *mac; + ++ mutex_lock(&port_priv->mac_lock); ++ mac = port_priv->mac; + port_priv->mac = NULL; ++ mutex_unlock(&port_priv->mac_lock); + + if (!mac) + return; +@@ -1495,6 +1512,7 @@ static irqreturn_t dpaa2_switch_irq0_han + struct ethsw_port_priv *port_priv; + u32 status = ~0; + int err, if_id; ++ bool had_mac; + + err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, + DPSW_IRQ_INDEX_IF, &status); +@@ -1513,7 +1531,12 @@ static irqreturn_t dpaa2_switch_irq0_han + + if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { + rtnl_lock(); +- if (dpaa2_switch_port_has_mac(port_priv)) ++ /* We can avoid locking because the "endpoint changed" IRQ ++ * handler is the only one who changes priv->mac at runtime, ++ * so we are not racing with anyone. ++ */ ++ had_mac = !!port_priv->mac; ++ if (had_mac) + dpaa2_switch_port_disconnect_mac(port_priv); + else + dpaa2_switch_port_connect_mac(port_priv); +@@ -3256,6 +3279,8 @@ static int dpaa2_switch_probe_port(struc + port_priv->netdev = port_netdev; + port_priv->ethsw_data = ethsw; + ++ mutex_init(&port_priv->mac_lock); ++ + port_priv->idx = port_idx; + port_priv->stp_state = BR_STATE_FORWARDING; + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h +@@ -161,6 +161,8 @@ struct ethsw_port_priv { + + struct dpaa2_switch_filter_block *filter_block; + struct dpaa2_mac *mac; ++ /* Protects against changes to port_priv->mac */ ++ struct mutex mac_lock; + }; + + /* Switch data */ diff --git a/target/linux/armvirt/patches-6.1/701-v6.2-0012-net-dpaa2-mac-move-rtnl_lock-only-around-phylink.patch b/target/linux/armvirt/patches-6.1/701-v6.2-0012-net-dpaa2-mac-move-rtnl_lock-only-around-phylink.patch new file mode 100644 index 00000000000..976c2a03354 --- /dev/null +++ b/target/linux/armvirt/patches-6.1/701-v6.2-0012-net-dpaa2-mac-move-rtnl_lock-only-around-phylink.patch @@ -0,0 +1,113 @@ +From 4ea2faf5bb13d9ba9f07e996d495c4cbe34a4236 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 29 Nov 2022 16:12:21 +0200 +Subject: [PATCH 14/14] net: dpaa2-mac: move rtnl_lock() only around + phylink_{,dis}connect_phy() + +After the introduction of a private mac_lock that serializes access to +priv->mac (and port_priv->mac in the switch), the only remaining purpose +of rtnl_lock() is to satisfy the locking requirements of +phylink_fwnode_phy_connect() and phylink_disconnect_phy(). + +But the functions these live in, dpaa2_mac_connect() and +dpaa2_mac_disconnect(), have contradictory locking requirements. +While phylink_fwnode_phy_connect() wants rtnl_lock() to be held, +phylink_create() wants it to not be held. + +Move the rtnl_lock() from top-level (in the dpaa2-eth and dpaa2-switch +drivers) to only surround the phylink calls that require it, in the +dpaa2-mac library code. + +This is possible because dpaa2_mac_connect() and dpaa2_mac_disconnect() +run unlocked, and there isn't any danger of an AB/BA deadlock between +the rtnl_mutex and other private locks. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Ioana Ciornei +Tested-by: Ioana Ciornei +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 4 ---- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++++ + drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 4 ---- + 3 files changed, 5 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -4529,7 +4529,6 @@ static irqreturn_t dpni_irq0_handler_thr + dpaa2_eth_set_mac_addr(netdev_priv(net_dev)); + dpaa2_eth_update_tx_fqids(priv); + +- rtnl_lock(); + /* We can avoid locking because the "endpoint changed" IRQ + * handler is the only one who changes priv->mac at runtime, + * so we are not racing with anyone. +@@ -4539,7 +4538,6 @@ static irqreturn_t dpni_irq0_handler_thr + dpaa2_eth_disconnect_mac(priv); + else + dpaa2_eth_connect_mac(priv); +- rtnl_unlock(); + } + + return IRQ_HANDLED; +@@ -4856,9 +4854,7 @@ static int dpaa2_eth_remove(struct fsl_m + else + fsl_mc_free_irqs(ls_dev); + +- rtnl_lock(); + dpaa2_eth_disconnect_mac(priv); +- rtnl_unlock(); + dpaa2_eth_free_rings(priv); + free_percpu(priv->fd); + free_percpu(priv->sgt_cache); +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -428,7 +428,9 @@ int dpaa2_mac_connect(struct dpaa2_mac * + } + mac->phylink = phylink; + ++ rtnl_lock(); + err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0); ++ rtnl_unlock(); + if (err) { + netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err); + goto err_phylink_destroy; +@@ -446,7 +448,10 @@ err_pcs_destroy: + + void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + { ++ rtnl_lock(); + phylink_disconnect_phy(mac->phylink); ++ rtnl_unlock(); ++ + phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); + of_phy_put(mac->serdes_phy); +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +@@ -1530,7 +1530,6 @@ static irqreturn_t dpaa2_switch_irq0_han + } + + if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { +- rtnl_lock(); + /* We can avoid locking because the "endpoint changed" IRQ + * handler is the only one who changes priv->mac at runtime, + * so we are not racing with anyone. +@@ -1540,7 +1539,6 @@ static irqreturn_t dpaa2_switch_irq0_han + dpaa2_switch_port_disconnect_mac(port_priv); + else + dpaa2_switch_port_connect_mac(port_priv); +- rtnl_unlock(); + } + + out: +@@ -2958,9 +2956,7 @@ static void dpaa2_switch_remove_port(str + { + struct ethsw_port_priv *port_priv = ethsw->ports[port_idx]; + +- rtnl_lock(); + dpaa2_switch_port_disconnect_mac(port_priv); +- rtnl_unlock(); + free_netdev(port_priv->netdev); + ethsw->ports[port_idx] = NULL; + } diff --git a/target/linux/bcm27xx/patches-5.15/950-0144-bcmgenet-Better-coalescing-parameter-defaults.patch b/target/linux/bcm27xx/patches-5.15/950-0144-bcmgenet-Better-coalescing-parameter-defaults.patch index 243605680b3..69e99c5f99b 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0144-bcmgenet-Better-coalescing-parameter-defaults.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0144-bcmgenet-Better-coalescing-parameter-defaults.patch @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell /* Disable rate control for now */ bcmgenet_tdma_ring_writel(priv, index, flow_period_val, TDMA_FLOW_PERIOD); -@@ -4083,9 +4083,12 @@ static int bcmgenet_probe(struct platfor +@@ -4084,9 +4084,12 @@ static int bcmgenet_probe(struct platfor netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1); /* Set default coalescing parameters */ diff --git a/target/linux/bcm27xx/patches-5.15/950-0151-usb-add-plumbing-for-updating-interrupt-endpoint-int.patch b/target/linux/bcm27xx/patches-5.15/950-0151-usb-add-plumbing-for-updating-interrupt-endpoint-int.patch index 857221d8b53..68c2c6d7281 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0151-usb-add-plumbing-for-updating-interrupt-endpoint-int.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0151-usb-add-plumbing-for-updating-interrupt-endpoint-int.patch @@ -70,7 +70,7 @@ Signed-off-by: Jonathan Bell * @dev: the device whose endpoint is being disabled --- a/include/linux/usb.h +++ b/include/linux/usb.h -@@ -1845,6 +1845,8 @@ extern int usb_clear_halt(struct usb_dev +@@ -1850,6 +1850,8 @@ extern int usb_clear_halt(struct usb_dev extern int usb_reset_configuration(struct usb_device *dev); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); diff --git a/target/linux/bcm27xx/patches-5.15/950-0292-net-bcmgenet-Reset-RBUF-on-first-open.patch b/target/linux/bcm27xx/patches-5.15/950-0292-net-bcmgenet-Reset-RBUF-on-first-open.patch index cd50ba1802a..254376ee052 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0292-net-bcmgenet-Reset-RBUF-on-first-open.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0292-net-bcmgenet-Reset-RBUF-on-first-open.patch @@ -59,7 +59,7 @@ Signed-off-by: Phil Elwell /* Reinitialize TDMA and RDMA and SW housekeeping */ ret = bcmgenet_init_dma(priv); -@@ -4212,7 +4220,7 @@ static int bcmgenet_resume(struct device +@@ -4213,7 +4221,7 @@ static int bcmgenet_resume(struct device bcmgenet_hfb_create_rxnfc_filter(priv, rule); /* Disable RX/TX DMA and flush TX queues */ diff --git a/target/linux/bcm27xx/patches-5.15/950-0468-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch b/target/linux/bcm27xx/patches-5.15/950-0468-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch index f9e24ab3ea3..974094cebf2 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0468-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0468-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch @@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -677,9 +677,9 @@ deq_found: +@@ -697,9 +697,9 @@ deq_found: } if ((ep->ep_state & SET_DEQ_PENDING)) { diff --git a/target/linux/bcm27xx/patches-5.15/950-0600-xhci-quirks-add-link-TRB-quirk-for-VL805.patch b/target/linux/bcm27xx/patches-5.15/950-0600-xhci-quirks-add-link-TRB-quirk-for-VL805.patch index 3ef184fd786..cf9e3f512ea 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0600-xhci-quirks-add-link-TRB-quirk-for-VL805.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0600-xhci-quirks-add-link-TRB-quirk-for-VL805.patch @@ -22,7 +22,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c -@@ -296,6 +296,7 @@ static void xhci_pci_quirks(struct devic +@@ -297,6 +297,7 @@ static void xhci_pci_quirks(struct devic if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; @@ -32,7 +32,7 @@ Signed-off-by: Jonathan Bell if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -667,6 +667,15 @@ static int xhci_move_dequeue_past_td(str +@@ -687,6 +687,15 @@ static int xhci_move_dequeue_past_td(str } while (!cycle_found || !td_last_trb_found); deq_found: diff --git a/target/linux/bcm27xx/patches-5.15/950-0601-xhci-correct-room_on_ring-for-cases-where-there-is-a.patch b/target/linux/bcm27xx/patches-5.15/950-0601-xhci-correct-room_on_ring-for-cases-where-there-is-a.patch index 26e4f7ae78e..49db546b3dc 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0601-xhci-correct-room_on_ring-for-cases-where-there-is-a.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0601-xhci-correct-room_on_ring-for-cases-where-there-is-a.patch @@ -14,7 +14,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -289,6 +289,12 @@ static inline int room_on_ring(struct xh +@@ -309,6 +309,12 @@ static inline int room_on_ring(struct xh return 0; if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) { diff --git a/target/linux/bcm27xx/patches-5.15/950-0605-xhci-refactor-out-TRBS_PER_SEGMENT-define-in-runtime.patch b/target/linux/bcm27xx/patches-5.15/950-0605-xhci-refactor-out-TRBS_PER_SEGMENT-define-in-runtime.patch index a26619d1cca..4cb33c75297 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0605-xhci-refactor-out-TRBS_PER_SEGMENT-define-in-runtime.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0605-xhci-refactor-out-TRBS_PER_SEGMENT-define-in-runtime.patch @@ -205,7 +205,7 @@ Signed-off-by: Jonathan Bell xhci_err(xhci, "Tried to move enqueue past ring segment\n"); return; } -@@ -3162,7 +3165,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd +@@ -3189,7 +3192,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd * that clears the EHB. */ while (xhci_handle_event(xhci) > 0) { @@ -214,7 +214,7 @@ Signed-off-by: Jonathan Bell continue; xhci_update_erst_dequeue(xhci, event_ring_deq); event_ring_deq = xhci->event_ring->dequeue; -@@ -3304,7 +3307,8 @@ static int prepare_ring(struct xhci_hcd +@@ -3331,7 +3334,8 @@ static int prepare_ring(struct xhci_hcd } } diff --git a/target/linux/bcm27xx/patches-5.15/950-0606-usb-xhci-add-VLI_TRB_CACHE_BUG-quirk.patch b/target/linux/bcm27xx/patches-5.15/950-0606-usb-xhci-add-VLI_TRB_CACHE_BUG-quirk.patch index b27f505e329..2b0030bd91d 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0606-usb-xhci-add-VLI_TRB_CACHE_BUG-quirk.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0606-usb-xhci-add-VLI_TRB_CACHE_BUG-quirk.patch @@ -53,7 +53,7 @@ Signed-off-by: Jonathan Bell cycle_state, type, max_packet, flags); --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c -@@ -297,6 +297,7 @@ static void xhci_pci_quirks(struct devic +@@ -298,6 +298,7 @@ static void xhci_pci_quirks(struct devic xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; xhci->quirks |= XHCI_AVOID_DQ_ON_LINK; diff --git a/target/linux/bcm27xx/patches-5.15/950-0717-usb-xhci-add-a-quirk-for-Superspeed-bulk-OUT-transfe.patch b/target/linux/bcm27xx/patches-5.15/950-0717-usb-xhci-add-a-quirk-for-Superspeed-bulk-OUT-transfe.patch index ef95a78b3e4..5771cfb3d41 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0717-usb-xhci-add-a-quirk-for-Superspeed-bulk-OUT-transfe.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0717-usb-xhci-add-a-quirk-for-Superspeed-bulk-OUT-transfe.patch @@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c -@@ -298,6 +298,7 @@ static void xhci_pci_quirks(struct devic +@@ -299,6 +299,7 @@ static void xhci_pci_quirks(struct devic xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; xhci->quirks |= XHCI_AVOID_DQ_ON_LINK; xhci->quirks |= XHCI_VLI_TRB_CACHE_BUG; @@ -36,7 +36,7 @@ Signed-off-by: Jonathan Bell if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -3617,14 +3617,15 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3644,14 +3644,15 @@ int xhci_queue_bulk_tx(struct xhci_hcd * unsigned int num_trbs; unsigned int start_cycle, num_sgs = 0; unsigned int enqd_len, block_len, trb_buff_len, full_len; @@ -54,7 +54,7 @@ Signed-off-by: Jonathan Bell full_len = urb->transfer_buffer_length; /* If we have scatter/gather list, we use it. */ if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) { -@@ -3661,6 +3662,17 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3688,6 +3689,17 @@ int xhci_queue_bulk_tx(struct xhci_hcd * start_cycle = ring->cycle_state; send_addr = addr; @@ -72,7 +72,7 @@ Signed-off-by: Jonathan Bell /* Queue the TRBs, even if they are zero-length */ for (enqd_len = 0; first_trb || enqd_len < full_len; enqd_len += trb_buff_len) { -@@ -3673,6 +3685,11 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3700,6 +3712,11 @@ int xhci_queue_bulk_tx(struct xhci_hcd * if (enqd_len + trb_buff_len > full_len) trb_buff_len = full_len - enqd_len; diff --git a/target/linux/bcm27xx/patches-5.15/950-0747-usb-xhci-rework-XHCI_VLI_SS_BULK_OUT_BUG-quirk.patch b/target/linux/bcm27xx/patches-5.15/950-0747-usb-xhci-rework-XHCI_VLI_SS_BULK_OUT_BUG-quirk.patch index 49638b3a057..526fa29bcc6 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0747-usb-xhci-rework-XHCI_VLI_SS_BULK_OUT_BUG-quirk.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0747-usb-xhci-rework-XHCI_VLI_SS_BULK_OUT_BUG-quirk.patch @@ -13,7 +13,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -3617,7 +3617,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3644,7 +3644,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * unsigned int num_trbs; unsigned int start_cycle, num_sgs = 0; unsigned int enqd_len, block_len, trb_buff_len, full_len; @@ -22,7 +22,7 @@ Signed-off-by: Jonathan Bell u32 field, length_field, remainder, maxpacket; u64 addr, send_addr; -@@ -3663,14 +3663,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3690,14 +3690,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd * send_addr = addr; if (xhci->quirks & XHCI_VLI_SS_BULK_OUT_BUG && @@ -40,7 +40,7 @@ Signed-off-by: Jonathan Bell } /* Queue the TRBs, even if they are zero-length */ -@@ -3685,7 +3680,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * +@@ -3712,7 +3707,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * if (enqd_len + trb_buff_len > full_len) trb_buff_len = full_len - enqd_len; diff --git a/target/linux/bmips/Makefile b/target/linux/bmips/Makefile index d5f437fbd41..f45a70c2895 100644 --- a/target/linux/bmips/Makefile +++ b/target/linux/bmips/Makefile @@ -10,6 +10,7 @@ SUBTARGETS:=bcm6318 bcm6328 bcm6358 bcm6362 bcm6368 bcm63268 FEATURES:=gpio squashfs usb KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 define Target/Description Build firmware images for BCM33xx cable modem chips, diff --git a/target/linux/bmips/bcm6318/config-6.1 b/target/linux/bmips/bcm6318/config-6.1 new file mode 100644 index 00000000000..4f54ce55339 --- /dev/null +++ b/target/linux/bmips/bcm6318/config-6.1 @@ -0,0 +1,286 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_MMAP_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +# CONFIG_BCM6348_ENET is not set +CONFIG_BCM6368_ENETSW=y +CONFIG_BCM63XX_POWER=y +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +# CONFIG_CLK_BCM63268_TIMER is not set +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_BCM6318=y +# CONFIG_PCIE_BCM6328 is not set +CONFIG_PCIE_PME=y +# CONFIG_PCI_BCM6348 is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_BCM6318=y +# CONFIG_PINCTRL_BCM63268 is not set +# CONFIG_PINCTRL_BCM6328 is not set +# CONFIG_PINCTRL_BCM6358 is not set +# CONFIG_PINCTRL_BCM6362 is not set +# CONFIG_PINCTRL_BCM6368 is not set +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BCM63XX=y +CONFIG_SPI=y +# CONFIG_SPI_BCM63XX is not set +CONFIG_SPI_BCM63XX_HSSPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/bcm63268/config-6.1 b/target/linux/bmips/bcm63268/config-6.1 new file mode 100644 index 00000000000..2ed4eba2f6e --- /dev/null +++ b/target/linux/bmips/bcm63268/config-6.1 @@ -0,0 +1,301 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_MMAP_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +# CONFIG_BCM6348_ENET is not set +CONFIG_BCM6368_ENETSW=y +CONFIG_BCM63XX_POWER=y +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +CONFIG_CLK_BCM63268_TIMER=y +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_BCM2835=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_BRCMNAND_BCM63XX=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_MTD_SPLIT_BCM_WFI_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIE_BCM6318 is not set +CONFIG_PCIE_BCM6328=y +CONFIG_PCIE_PME=y +# CONFIG_PCI_BCM6348 is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_BCM6318 is not set +CONFIG_PINCTRL_BCM63268=y +# CONFIG_PINCTRL_BCM6328 is not set +# CONFIG_PINCTRL_BCM6358 is not set +# CONFIG_PINCTRL_BCM6362 is not set +# CONFIG_PINCTRL_BCM6368 is not set +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BCM63XX=y +CONFIG_SPI=y +CONFIG_SPI_BCM63XX=y +CONFIG_SPI_BCM63XX_HSSPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/bcm6328/config-6.1 b/target/linux/bmips/bcm6328/config-6.1 new file mode 100644 index 00000000000..85d5578b8eb --- /dev/null +++ b/target/linux/bmips/bcm6328/config-6.1 @@ -0,0 +1,298 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_MMAP_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +# CONFIG_BCM6348_ENET is not set +CONFIG_BCM6368_ENETSW=y +CONFIG_BCM63XX_POWER=y +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +# CONFIG_CLK_BCM63268_TIMER is not set +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_MTD_SPLIT_BCM_WFI_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIE_BCM6318 is not set +CONFIG_PCIE_BCM6328=y +CONFIG_PCIE_PME=y +CONFIG_PCI_BCM6348=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_BCM6318 is not set +# CONFIG_PINCTRL_BCM63268 is not set +CONFIG_PINCTRL_BCM6328=y +# CONFIG_PINCTRL_BCM6358 is not set +# CONFIG_PINCTRL_BCM6362 is not set +# CONFIG_PINCTRL_BCM6368 is not set +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BCM63XX=y +CONFIG_SPI=y +# CONFIG_SPI_BCM63XX is not set +CONFIG_SPI_BCM63XX_HSSPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/bcm6358/config-6.1 b/target/linux/bmips/bcm6358/config-6.1 new file mode 100644 index 00000000000..1d95a92cbc3 --- /dev/null +++ b/target/linux/bmips/bcm6358/config-6.1 @@ -0,0 +1,280 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +CONFIG_BCM6348_ENET=y +# CONFIG_BCM6368_ENETSW is not set +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +# CONFIG_CLK_BCM63268_TIMER is not set +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +# CONFIG_MDIO_BUS_MUX_BCM6368 is not set +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +# CONFIG_PCIE_BCM6318 is not set +# CONFIG_PCIE_BCM6328 is not set +CONFIG_PCI_BCM6348=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_BCM6318 is not set +# CONFIG_PINCTRL_BCM63268 is not set +# CONFIG_PINCTRL_BCM6328 is not set +CONFIG_PINCTRL_BCM6358=y +# CONFIG_PINCTRL_BCM6362 is not set +# CONFIG_PINCTRL_BCM6368 is not set +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +# CONFIG_SOC_BCM63XX is not set +CONFIG_SPI=y +CONFIG_SPI_BCM63XX=y +# CONFIG_SPI_BCM63XX_HSSPI is not set +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/bcm6362/config-6.1 b/target/linux/bmips/bcm6362/config-6.1 new file mode 100644 index 00000000000..b51f7e4166a --- /dev/null +++ b/target/linux/bmips/bcm6362/config-6.1 @@ -0,0 +1,300 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_MMAP_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +# CONFIG_BCM6348_ENET is not set +CONFIG_BCM6368_ENETSW=y +CONFIG_BCM63XX_POWER=y +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +# CONFIG_CLK_BCM63268_TIMER is not set +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_BCM2835=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_MTD_SPLIT_BCM_WFI_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIE_BCM6318 is not set +CONFIG_PCIE_BCM6328=y +CONFIG_PCIE_PME=y +# CONFIG_PCI_BCM6348 is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_BCM6318 is not set +# CONFIG_PINCTRL_BCM63268 is not set +# CONFIG_PINCTRL_BCM6328 is not set +# CONFIG_PINCTRL_BCM6358 is not set +CONFIG_PINCTRL_BCM6362=y +# CONFIG_PINCTRL_BCM6368 is not set +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BCM63XX=y +CONFIG_SPI=y +CONFIG_SPI_BCM63XX=y +CONFIG_SPI_BCM63XX_HSSPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/bcm6368/config-6.1 b/target/linux/bmips/bcm6368/config-6.1 new file mode 100644 index 00000000000..6fd4fe03bbe --- /dev/null +++ b/target/linux/bmips/bcm6368/config-6.1 @@ -0,0 +1,296 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_B53=y +CONFIG_B53_MDIO_DRIVER=y +CONFIG_B53_MMAP_DRIVER=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_BCM6345_EXT_IRQ=y +CONFIG_BCM6345_L1_IRQ=y +# CONFIG_BCM6348_ENET is not set +CONFIG_BCM6368_ENETSW=y +CONFIG_BCM7038_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BMIPS_GENERIC=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CEVT_R4K=y +# CONFIG_CLK_BCM63268_TIMER is not set +CONFIG_CLK_BCM_63XX_GATE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_BMIPS=y +CONFIG_CPU_BMIPS32_3300=y +CONFIG_CPU_BMIPS4350=y +CONFIG_CPU_BMIPS4380=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +# CONFIG_DT_BCM93384WVG is not set +# CONFIG_DT_BCM93384WVG_VIPER is not set +# CONFIG_DT_BCM96368MVWG is not set +# CONFIG_DT_BCM97125CBMB is not set +# CONFIG_DT_BCM97346DBSMB is not set +# CONFIG_DT_BCM97358SVMB is not set +# CONFIG_DT_BCM97360SVMB is not set +# CONFIG_DT_BCM97362SVMB is not set +# CONFIG_DT_BCM97420C is not set +# CONFIG_DT_BCM97425SVMB is not set +# CONFIG_DT_BCM97435SVMB is not set +# CONFIG_DT_BCM9EJTAGPRB is not set +# CONFIG_DT_COMTREND_VR3032U is not set +# CONFIG_DT_NETGEAR_CVG834G is not set +CONFIG_DT_NONE=y +# CONFIG_DT_SFR_NEUFBOX4_SERCOMM is not set +# CONFIG_DT_SFR_NEUFBOX6_SERCOMM is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CFE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=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_TIME_VSYSCALL=y +# CONFIG_GPIO_BRCMSTB is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_REGMAP=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_BCM2835=y +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_SERCOMM_MSP430 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_BCM6368=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_4=y +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_O32_FP64_SUPPORT=y +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MTD_BCM63XX_PARTS is not set +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_NOSWAP is not set +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +# CONFIG_MTD_PARSER_IMAGETAG is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BCM63XX_FW=y +CONFIG_MTD_SPLIT_BCM_WFI_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_TAG_BRCM=y +CONFIG_NET_DSA_TAG_BRCM_COMMON=y +CONFIG_NET_DSA_TAG_BRCM_LEGACY=y +CONFIG_NET_DSA_TAG_BRCM_PREPEND=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +# CONFIG_PCIE_BCM6318 is not set +# CONFIG_PCIE_BCM6328 is not set +CONFIG_PCI_BCM6348=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYSICAL_START=0x80010000 +CONFIG_PHY_BCM63XX_USBH=y +# CONFIG_PHY_BRCM_SATA is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_BCM6318 is not set +# CONFIG_PINCTRL_BCM63268 is not set +# CONFIG_PINCTRL_BCM6328 is not set +# CONFIG_PINCTRL_BCM6358 is not set +# CONFIG_PINCTRL_BCM6362 is not set +CONFIG_PINCTRL_BCM6368=y +CONFIG_PINCTRL_BCM63XX=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RESET_BCM6345=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +# CONFIG_SOC_BCM63XX is not set +CONFIG_SPI=y +CONFIG_SPI_BCM63XX=y +# CONFIG_SPI_BCM63XX_HSSPI is not set +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_BMIPS=y +CONFIG_SYS_HAS_CPU_BMIPS32_3300=y +CONFIG_SYS_HAS_CPU_BMIPS4350=y +CONFIG_SYS_HAS_CPU_BMIPS4380=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_TARGET_ISA_REV=0 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y +CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c index 63841129a99..21eb6ea0c20 100644 --- a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c +++ b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c @@ -25,6 +25,7 @@ #include #include #include +#include /* DMA channels */ #define DMA_CHAN_WIDTH 0x10 @@ -1503,6 +1504,7 @@ static int bcm6348_emac_probe(struct platform_device *pdev) struct bcm6348_emac *emac; struct bcm6348_iudma *iudma; struct net_device *ndev; + unsigned char dev_addr[ETH_ALEN]; unsigned i; int num_resets; int ret; @@ -1564,12 +1566,13 @@ static int bcm6348_emac_probe(struct platform_device *pdev) emac->old_duplex = -1; emac->old_pause = -1; - of_get_mac_address(node, ndev->dev_addr); - if (is_valid_ether_addr(ndev->dev_addr)) { - dev_info(dev, "mtd mac %pM\n", ndev->dev_addr); + of_get_mac_address(node, dev_addr); + if (is_valid_ether_addr(dev_addr)) { + dev_addr_set(ndev, dev_addr); + dev_info(dev, "mtd mac %pM\n", dev_addr); } else { - random_ether_addr(ndev->dev_addr); - dev_info(dev, "random mac %pM\n", ndev->dev_addr); + eth_hw_addr_random(ndev); + dev_info(dev, "random mac\n"); } emac->rx_skb_size = ALIGN(ndev->mtu + ENET_MTU_OVERHEAD, @@ -1644,7 +1647,11 @@ static int bcm6348_emac_probe(struct platform_device *pdev) ndev->min_mtu = ETH_ZLEN - ETH_HLEN; ndev->mtu = ETH_DATA_LEN - VLAN_ETH_HLEN; ndev->max_mtu = ENET_MAX_MTU - VLAN_ETH_HLEN; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) + netif_napi_add(ndev, &emac->napi, bcm6348_emac_poll); +#else netif_napi_add(ndev, &emac->napi, bcm6348_emac_poll, 16); +#endif SET_NETDEV_DEV(ndev, dev); ret = devm_register_netdev(dev, ndev); diff --git a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c index e200b991416..321e95dbbb3 100644 --- a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c +++ b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6368-enetsw.c @@ -20,6 +20,7 @@ #include #include #include +#include /* MTU */ #define ENETSW_TAG_SIZE (6 + VLAN_HLEN) @@ -908,6 +909,7 @@ static int bcm6368_enetsw_probe(struct platform_device *pdev) struct bcm6368_enetsw *priv; struct net_device *ndev; struct resource *res; + unsigned char dev_addr[ETH_ALEN]; unsigned i; int num_resets; int ret; @@ -995,12 +997,13 @@ static int bcm6368_enetsw_probe(struct platform_device *pdev) priv->tx_ring_size = ENETSW_DEF_TX_DESC; priv->copybreak = ENETSW_DEF_CPY_BREAK; - of_get_mac_address(node, ndev->dev_addr); - if (is_valid_ether_addr(ndev->dev_addr)) { - dev_info(dev, "mtd mac %pM\n", ndev->dev_addr); + of_get_mac_address(node, dev_addr); + if (is_valid_ether_addr(dev_addr)) { + dev_addr_set(ndev, dev_addr); + dev_info(dev, "mtd mac %pM\n", dev_addr); } else { - random_ether_addr(ndev->dev_addr); - dev_info(dev, "random mac %pM\n", ndev->dev_addr); + eth_hw_addr_random(ndev); + dev_info(dev, "random mac\n"); } priv->rx_buf_size = ALIGN(ndev->mtu + ENETSW_MTU_OVERHEAD, @@ -1065,7 +1068,11 @@ static int bcm6368_enetsw_probe(struct platform_device *pdev) ndev->min_mtu = ETH_ZLEN; ndev->mtu = ETH_DATA_LEN + ENETSW_TAG_SIZE; ndev->max_mtu = ETH_DATA_LEN + ENETSW_TAG_SIZE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) + netif_napi_add(ndev, &priv->napi, bcm6368_enetsw_poll); +#else netif_napi_add(ndev, &priv->napi, bcm6368_enetsw_poll, 16); +#endif ret = devm_register_netdev(dev, ndev); if (ret) { diff --git a/target/linux/bmips/patches-6.1/100-irqchip-add-support-for-bcm6345-style-external-inter.patch b/target/linux/bmips/patches-6.1/100-irqchip-add-support-for-bcm6345-style-external-inter.patch new file mode 100644 index 00000000000..15d44b34d1c --- /dev/null +++ b/target/linux/bmips/patches-6.1/100-irqchip-add-support-for-bcm6345-style-external-inter.patch @@ -0,0 +1,373 @@ +From cf908990d4a8ccdb73ee4484aa8cadad379ca314 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 30 Nov 2014 14:54:27 +0100 +Subject: [PATCH] irqchip: add support for bcm6345-style external interrupt + controller + +Signed-off-by: Jonas Gorski +--- + .../brcm,bcm6345-ext-intc.txt | 29 ++ + drivers/irqchip/Kconfig | 4 + + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irq-bcm6345-ext.c | 280 ++++++++++++++++++ + include/linux/irqchip/irq-bcm6345-ext.h | 14 + + 5 files changed, 328 insertions(+) + create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-ext-intc.txt + create mode 100644 drivers/irqchip/irq-bcm6345-ext.c + create mode 100644 include/linux/irqchip/irq-bcm6345-ext.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-ext-intc.txt +@@ -0,0 +1,29 @@ ++Broadcom BCM6345-style external interrupt controller ++ ++Required properties: ++ ++- compatible: Should be "brcm,bcm6345-ext-intc" or "brcm,bcm6318-ext-intc". ++- reg: Specifies the base physical addresses and size of the registers. ++- interrupt-controller: identifies the node as an interrupt controller. ++- #interrupt-cells: Specifies the number of cells needed to encode an interrupt ++ source, Should be 2. ++- interrupt-parent: Specifies the phandle to the parent interrupt controller ++ this one is cascaded from. ++- interrupts: Specifies the interrupt line(s) in the interrupt-parent controller ++ node, valid values depend on the type of parent interrupt controller. ++ ++Optional properties: ++ ++- brcm,field-width: Size of each field (mask, clear, sense, ...) in bits in the ++ register. Defaults to 4. ++ ++Example: ++ ++ext_intc: interrupt-controller@10000018 { ++ compatible = "brcm,bcm6345-ext-intc"; ++ interrupt-parent = <&periph_intc>; ++ #interrupt-cells = <2>; ++ reg = <0x10000018 0x4>; ++ interrupt-controller; ++ interrupts = <24>, <25>, <26>, <27>; ++}; +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -108,6 +108,10 @@ config I8259 + bool + select IRQ_DOMAIN + ++config BCM6345_EXT_IRQ ++ bool "BCM6345 External IRQ Controller" ++ select IRQ_DOMAIN ++ + config BCM6345_L1_IRQ + bool + select GENERIC_IRQ_CHIP +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa- + obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o + obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o + obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o ++obj-$(CONFIG_BCM6345_EXT_IRQ) += irq-bcm6345-ext.o + obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o + obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o + obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o +--- /dev/null ++++ b/drivers/irqchip/irq-bcm6345-ext.c +@@ -0,0 +1,280 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2014 Jonas Gorski ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_IRQS 4 ++ ++#define EXTIRQ_CFG_SENSE 0 ++#define EXTIRQ_CFG_STAT 1 ++#define EXTIRQ_CFG_CLEAR 2 ++#define EXTIRQ_CFG_MASK 3 ++#define EXTIRQ_CFG_BOTHEDGE 4 ++#define EXTIRQ_CFG_LEVELSENSE 5 ++ ++struct intc_data { ++ struct irq_chip chip; ++ struct irq_domain *domain; ++ raw_spinlock_t lock; ++ ++ int parent_irq[MAX_IRQS]; ++ void __iomem *reg; ++ int shift; ++ unsigned int toggle_clear_on_ack:1; ++}; ++ ++static void bcm6345_ext_intc_irq_handle(struct irq_desc *desc) ++{ ++ struct intc_data *data = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ unsigned int irq = irq_desc_get_irq(desc); ++ unsigned int idx; ++ ++ chained_irq_enter(chip, desc); ++ ++ for (idx = 0; idx < MAX_IRQS; idx++) { ++ if (data->parent_irq[idx] != irq) ++ continue; ++ ++ generic_handle_irq(irq_find_mapping(data->domain, idx)); ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void bcm6345_ext_intc_irq_ack(struct irq_data *data) ++{ ++ struct intc_data *priv = data->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(data); ++ u32 reg; ++ ++ raw_spin_lock(&priv->lock); ++ reg = __raw_readl(priv->reg); ++ __raw_writel(reg | (1 << (hwirq + EXTIRQ_CFG_CLEAR * priv->shift)), ++ priv->reg); ++ if (priv->toggle_clear_on_ack) ++ __raw_writel(reg, priv->reg); ++ raw_spin_unlock(&priv->lock); ++} ++ ++static void bcm6345_ext_intc_irq_mask(struct irq_data *data) ++{ ++ struct intc_data *priv = data->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(data); ++ u32 reg; ++ ++ raw_spin_lock(&priv->lock); ++ reg = __raw_readl(priv->reg); ++ reg &= ~(1 << (hwirq + EXTIRQ_CFG_MASK * priv->shift)); ++ __raw_writel(reg, priv->reg); ++ raw_spin_unlock(&priv->lock); ++} ++ ++static void bcm6345_ext_intc_irq_unmask(struct irq_data *data) ++{ ++ struct intc_data *priv = data->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(data); ++ u32 reg; ++ ++ raw_spin_lock(&priv->lock); ++ reg = __raw_readl(priv->reg); ++ reg |= 1 << (hwirq + EXTIRQ_CFG_MASK * priv->shift); ++ __raw_writel(reg, priv->reg); ++ raw_spin_unlock(&priv->lock); ++} ++ ++static int bcm6345_ext_intc_set_type(struct irq_data *data, ++ unsigned int flow_type) ++{ ++ struct intc_data *priv = data->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(data); ++ bool levelsense = 0, sense = 0, bothedge = 0; ++ u32 reg; ++ ++ flow_type &= IRQ_TYPE_SENSE_MASK; ++ ++ if (flow_type == IRQ_TYPE_NONE) ++ flow_type = IRQ_TYPE_LEVEL_LOW; ++ ++ switch (flow_type) { ++ case IRQ_TYPE_EDGE_BOTH: ++ bothedge = 1; ++ break; ++ ++ case IRQ_TYPE_EDGE_RISING: ++ sense = 1; ++ break; ++ ++ case IRQ_TYPE_EDGE_FALLING: ++ break; ++ ++ case IRQ_TYPE_LEVEL_HIGH: ++ levelsense = 1; ++ sense = 1; ++ break; ++ ++ case IRQ_TYPE_LEVEL_LOW: ++ levelsense = 1; ++ break; ++ ++ default: ++ pr_err("bogus flow type combination given!\n"); ++ return -EINVAL; ++ } ++ ++ raw_spin_lock(&priv->lock); ++ reg = __raw_readl(priv->reg); ++ ++ if (levelsense) ++ reg |= 1 << (hwirq + EXTIRQ_CFG_LEVELSENSE * priv->shift); ++ else ++ reg &= ~(1 << (hwirq + EXTIRQ_CFG_LEVELSENSE * priv->shift)); ++ if (sense) ++ reg |= 1 << (hwirq + EXTIRQ_CFG_SENSE * priv->shift); ++ else ++ reg &= ~(1 << (hwirq + EXTIRQ_CFG_SENSE * priv->shift)); ++ if (bothedge) ++ reg |= 1 << (hwirq + EXTIRQ_CFG_BOTHEDGE * priv->shift); ++ else ++ reg &= ~(1 << (hwirq + EXTIRQ_CFG_BOTHEDGE * priv->shift)); ++ ++ __raw_writel(reg, priv->reg); ++ raw_spin_unlock(&priv->lock); ++ ++ irqd_set_trigger_type(data, flow_type); ++ if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) ++ irq_set_handler_locked(data, handle_level_irq); ++ else ++ irq_set_handler_locked(data, handle_edge_irq); ++ ++ return 0; ++} ++ ++static int bcm6345_ext_intc_map(struct irq_domain *d, unsigned int irq, ++ irq_hw_number_t hw) ++{ ++ struct intc_data *priv = d->host_data; ++ ++ irq_set_chip_and_handler(irq, &priv->chip, handle_level_irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops bcm6345_ext_domain_ops = { ++ .xlate = irq_domain_xlate_twocell, ++ .map = bcm6345_ext_intc_map, ++}; ++ ++static int __init __bcm6345_ext_intc_init(struct device_node *node, ++ int num_irqs, int *irqs, ++ void __iomem *reg, int shift, ++ bool toggle_clear_on_ack) ++{ ++ struct intc_data *data; ++ unsigned int i; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ raw_spin_lock_init(&data->lock); ++ ++ for (i = 0; i < num_irqs; i++) { ++ data->parent_irq[i] = irqs[i]; ++ ++ irq_set_handler_data(irqs[i], data); ++ irq_set_chained_handler(irqs[i], bcm6345_ext_intc_irq_handle); ++ } ++ ++ data->reg = reg; ++ data->shift = shift; ++ data->toggle_clear_on_ack = toggle_clear_on_ack; ++ ++ data->chip.name = "bcm6345-ext-intc"; ++ data->chip.irq_ack = bcm6345_ext_intc_irq_ack; ++ data->chip.irq_mask = bcm6345_ext_intc_irq_mask; ++ data->chip.irq_unmask = bcm6345_ext_intc_irq_unmask; ++ data->chip.irq_set_type = bcm6345_ext_intc_set_type; ++ ++ data->domain = irq_domain_add_linear(node, num_irqs, ++ &bcm6345_ext_domain_ops, data); ++ if (!data->domain) { ++ kfree(data); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void __init bcm6345_ext_intc_init(int num_irqs, int *irqs, void __iomem *reg, ++ int shift) ++{ ++ __bcm6345_ext_intc_init(NULL, num_irqs, irqs, reg, shift, false); ++} ++ ++#ifdef CONFIG_OF ++static int __init bcm6345_ext_intc_of_init(struct device_node *node, ++ struct device_node *parent) ++{ ++ int num_irqs, ret = -EINVAL; ++ unsigned i; ++ void __iomem *base; ++ int irqs[MAX_IRQS] = { 0 }; ++ u32 shift; ++ bool toggle_clear_on_ack = false; ++ ++ num_irqs = of_irq_count(node); ++ ++ if (!num_irqs || num_irqs > MAX_IRQS) ++ return -EINVAL; ++ ++ if (of_property_read_u32(node, "brcm,field-width", &shift)) ++ shift = 4; ++ ++ /* on BCM6318 setting CLEAR seems to continuously mask interrupts */ ++ if (of_device_is_compatible(node, "brcm,bcm6318-ext-intc")) ++ toggle_clear_on_ack = true; ++ ++ for (i = 0; i < num_irqs; i++) { ++ irqs[i] = irq_of_parse_and_map(node, i); ++ if (!irqs[i]) ++ return -ENOMEM; ++ } ++ ++ base = of_iomap(node, 0); ++ if (!base) ++ return -ENXIO; ++ ++ ret = __bcm6345_ext_intc_init(node, num_irqs, irqs, base, shift, ++ toggle_clear_on_ack); ++ if (!ret) ++ return 0; ++ ++ iounmap(base); ++ ++ for (i = 0; i < num_irqs; i++) ++ irq_dispose_mapping(irqs[i]); ++ ++ return ret; ++} ++ ++IRQCHIP_DECLARE(bcm6318_ext_intc, "brcm,bcm6318-ext-intc", ++ bcm6345_ext_intc_of_init); ++IRQCHIP_DECLARE(bcm6345_ext_intc, "brcm,bcm6345-ext-intc", ++ bcm6345_ext_intc_of_init); ++#endif +--- /dev/null ++++ b/include/linux/irqchip/irq-bcm6345-ext.h +@@ -0,0 +1,14 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2014 Jonas Gorski ++ */ ++ ++#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H ++#define __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H ++ ++void bcm6345_ext_intc_init(int n_irqs, int *irqs, void __iomem *reg, int shift); ++ ++#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H */ diff --git a/target/linux/bmips/patches-6.1/200-mips-bmips-automatically-detect-CPU-frequency.patch b/target/linux/bmips/patches-6.1/200-mips-bmips-automatically-detect-CPU-frequency.patch new file mode 100644 index 00000000000..df553f002e9 --- /dev/null +++ b/target/linux/bmips/patches-6.1/200-mips-bmips-automatically-detect-CPU-frequency.patch @@ -0,0 +1,239 @@ +From 0377ad93031d3e51c2afe44231241185f684b6af Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Fri, 5 Mar 2021 15:14:32 +0100 +Subject: [PATCH] mips: bmips: automatically detect CPU frequency +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some BCM63xx SoCs support multiple CPU frequencies depending on HW config. + +Signed-off-by: Álvaro Fernández Rojas +--- + arch/mips/bmips/setup.c | 197 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 190 insertions(+), 7 deletions(-) + +--- a/arch/mips/bmips/setup.c ++++ b/arch/mips/bmips/setup.c +@@ -32,13 +32,52 @@ + + #define RELO_NORMAL_VEC BIT(18) + ++#define REG_BCM6318_SOB ((void __iomem *)CKSEG1ADDR(0x10000900)) ++#define BCM6318_FREQ_SHIFT 23 ++#define BCM6318_FREQ_MASK (0x3 << BCM6318_FREQ_SHIFT) ++ + #define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c)) + #define BCM6328_TP1_DISABLED BIT(9) ++#define REG_BCM6328_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001a40)) ++#define BCM6328_FCVO_SHIFT 7 ++#define BCM6328_FCVO_MASK (0x1f << BCM6328_FCVO_SHIFT) ++ ++#define REG_BCM6358_DDR_PLLC ((void __iomem *)0xfffe12b8) ++#define BCM6358_PLLC_M1_SHIFT 0 ++#define BCM6358_PLLC_M1_MASK (0xff << BCM6358_PLLC_M1_SHIFT) ++#define BCM6358_PLLC_N1_SHIFT 23 ++#define BCM6358_PLLC_N1_MASK (0x3f << BCM6358_PLLC_N1_SHIFT) ++#define BCM6358_PLLC_N2_SHIFT 29 ++#define BCM6358_PLLC_N2_MASK (0x7 << BCM6358_PLLC_N2_SHIFT) ++ ++#define REG_BCM6362_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001814)) ++#define BCM6362_FCVO_SHIFT 1 ++#define BCM6362_FCVO_MASK (0x1f << BCM6362_FCVO_SHIFT) ++ ++#define REG_BCM6368_DDR_PLLC ((void __iomem *)CKSEG1ADDR(0x100012a0)) ++#define BCM6368_PLLC_P1_SHIFT 0 ++#define BCM6368_PLLC_P1_MASK (0xf << BCM6368_PLLC_P1_SHIFT) ++#define BCM6368_PLLC_P2_SHIFT 4 ++#define BCM6368_PLLC_P2_MASK (0xf << BCM6368_PLLC_P2_SHIFT) ++#define BCM6368_PLLC_NDIV_SHIFT 16 ++#define BCM6368_PLLC_NDIV_MASK (0x1ff << BCM6368_PLLC_NDIV_SHIFT) ++#define REG_BCM6368_DDR_PLLD ((void __iomem *)CKSEG1ADDR(0x100012a4)) ++#define BCM6368_PLLD_MDIV_SHIFT 0 ++#define BCM6368_PLLD_MDIV_MASK (0xff << BCM6368_PLLD_MDIV_SHIFT) ++ ++#define REG_BCM63268_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001814)) ++#define BCM63268_FCVO_SHIFT 21 ++#define BCM63268_FCVO_MASK (0xf << BCM63268_FCVO_SHIFT) + + extern bool bmips_rac_flush_disable; + + static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000; + ++struct bmips_cpufreq { ++ const char *compatible; ++ u32 (*cpu_freq)(void); ++}; ++ + struct bmips_quirk { + const char *compatible; + void (*quirk_fn)(void); +@@ -154,17 +193,161 @@ const char *get_system_type(void) + return "Generic BMIPS kernel"; + } + ++static u32 bcm6318_cpufreq(void) ++{ ++ u32 val = __raw_readl(REG_BCM6318_SOB); ++ ++ switch ((val & BCM6318_FREQ_MASK) >> BCM6318_FREQ_SHIFT) { ++ case 0: ++ return 166000000; ++ case 2: ++ return 250000000; ++ case 3: ++ return 333000000; ++ case 1: ++ return 400000000; ++ default: ++ return 0; ++ } ++} ++ ++static u32 bcm6328_cpufreq(void) ++{ ++ u32 val = __raw_readl(REG_BCM6328_MISC_SB); ++ ++ switch ((val & BCM6328_FCVO_MASK) >> BCM6328_FCVO_SHIFT) { ++ case 0x12: ++ case 0x14: ++ case 0x19: ++ return 160000000; ++ case 0x1c: ++ return 192000000; ++ case 0x13: ++ case 0x15: ++ return 200000000; ++ case 0x1a: ++ return 384000000; ++ case 0x16: ++ return 400000000; ++ default: ++ return 320000000; ++ } ++} ++ ++static u32 bcm6358_cpufreq(void) ++{ ++ u32 val, n1, n2, m1; ++ ++ val = __raw_readl(REG_BCM6358_DDR_PLLC); ++ n1 = (val & BCM6358_PLLC_N1_MASK) >> BCM6358_PLLC_N1_SHIFT; ++ n2 = (val & BCM6358_PLLC_N2_MASK) >> BCM6358_PLLC_N2_SHIFT; ++ m1 = (val & BCM6358_PLLC_M1_MASK) >> BCM6358_PLLC_M1_SHIFT; ++ ++ return (16 * 1000000 * n1 * n2) / m1; ++} ++ ++static u32 bcm6362_cpufreq(void) ++{ ++ u32 val = __raw_readl(REG_BCM6362_MISC_SB); ++ ++ switch ((val & BCM6362_FCVO_MASK) >> BCM6362_FCVO_SHIFT) { ++ case 0x04: ++ case 0x0c: ++ case 0x14: ++ case 0x1c: ++ return 160000000; ++ case 0x15: ++ case 0x1d: ++ return 200000000; ++ case 0x03: ++ case 0x0b: ++ case 0x13: ++ case 0x1b: ++ return 240000000; ++ case 0x07: ++ case 0x17: ++ return 384000000; ++ case 0x05: ++ case 0x0e: ++ case 0x16: ++ case 0x1e: ++ case 0x1f: ++ return 400000000; ++ case 0x06: ++ return 440000000; ++ default: ++ return 320000000; ++ } ++} ++ ++static u32 bcm6368_cpufreq(void) ++{ ++ u32 val, p1, p2, ndiv, m1; ++ ++ val = __raw_readl(REG_BCM6368_DDR_PLLC); ++ p1 = (val & BCM6368_PLLC_P1_MASK) >> BCM6368_PLLC_P1_SHIFT; ++ p2 = (val & BCM6368_PLLC_P2_MASK) >> BCM6368_PLLC_P2_SHIFT; ++ ndiv = (val & BCM6368_PLLC_NDIV_MASK) >> ++ BCM6368_PLLC_NDIV_SHIFT; ++ ++ val = __raw_readl(REG_BCM6368_DDR_PLLD); ++ m1 = (val & BCM6368_PLLD_MDIV_MASK) >> BCM6368_PLLD_MDIV_SHIFT; ++ ++ return (((64 * 1000000) / p1) * p2 * ndiv) / m1; ++} ++ ++static u32 bcm63268_cpufreq(void) ++{ ++ u32 val = __raw_readl(REG_BCM63268_MISC_SB); ++ ++ switch ((val & BCM63268_FCVO_MASK) >> BCM63268_FCVO_SHIFT) { ++ case 0x3: ++ case 0xe: ++ return 320000000; ++ case 0xa: ++ return 333000000; ++ case 0x2: ++ case 0xb: ++ case 0xf: ++ return 400000000; ++ default: ++ return 0; ++ } ++} ++ ++static const struct bmips_cpufreq bmips_cpufreq_list[] = { ++ { "brcm,bcm6318", &bcm6318_cpufreq }, ++ { "brcm,bcm6328", &bcm6328_cpufreq }, ++ { "brcm,bcm6358", &bcm6358_cpufreq }, ++ { "brcm,bcm6362", &bcm6362_cpufreq }, ++ { "brcm,bcm6368", &bcm6368_cpufreq }, ++ { "brcm,bcm63268", &bcm63268_cpufreq }, ++ { /* sentinel */ } ++}; ++ + void __init plat_time_init(void) + { ++ const struct bmips_cpufreq *cf; + struct device_node *np; +- u32 freq; ++ u32 freq = 0; + +- np = of_find_node_by_name(NULL, "cpus"); +- if (!np) +- panic("missing 'cpus' DT node"); +- if (of_property_read_u32(np, "mips-hpt-frequency", &freq) < 0) +- panic("missing 'mips-hpt-frequency' property"); +- of_node_put(np); ++ for (cf = bmips_cpufreq_list; cf->cpu_freq; cf++) { ++ if (of_flat_dt_is_compatible(of_get_flat_dt_root(), ++ cf->compatible)) { ++ freq = cf->cpu_freq() / 2; ++ printk("%s detected @ %u MHz\n", cf->compatible, freq / 500000); ++ break; ++ } ++ } ++ ++ if (!freq) { ++ np = of_find_node_by_name(NULL, "cpus"); ++ if (!np) ++ panic("missing 'cpus' DT node"); ++ if (of_property_read_u32(np, "mips-hpt-frequency", &freq) < 0) ++ panic("missing 'mips-hpt-frequency' property"); ++ of_node_put(np); ++ } + + mips_hpt_frequency = freq; + } diff --git a/target/linux/bmips/patches-6.1/201-mips-bmips-automatically-detect-RAM-size.patch b/target/linux/bmips/patches-6.1/201-mips-bmips-automatically-detect-RAM-size.patch new file mode 100644 index 00000000000..68afc399309 --- /dev/null +++ b/target/linux/bmips/patches-6.1/201-mips-bmips-automatically-detect-RAM-size.patch @@ -0,0 +1,196 @@ +From f9ee3f28ecb979c77423be965ef9dd313bdb9e9b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 8 Mar 2021 16:58:34 +0100 +Subject: [PATCH] mips: bmips: automatically detect RAM size +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some devices have different amounts of RAM installed depending on HW revision. + +Signed-off-by: Álvaro Fernández Rojas +--- + arch/mips/bmips/setup.c | 119 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 119 insertions(+) + +--- a/arch/mips/bmips/setup.c ++++ b/arch/mips/bmips/setup.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -35,13 +36,16 @@ + #define REG_BCM6318_SOB ((void __iomem *)CKSEG1ADDR(0x10000900)) + #define BCM6318_FREQ_SHIFT 23 + #define BCM6318_FREQ_MASK (0x3 << BCM6318_FREQ_SHIFT) ++#define BCM6318_SDRAM_ADDR ((void __iomem *)CKSEG1ADDR(0x10004000)) + + #define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c)) + #define BCM6328_TP1_DISABLED BIT(9) + #define REG_BCM6328_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001a40)) + #define BCM6328_FCVO_SHIFT 7 + #define BCM6328_FCVO_MASK (0x1f << BCM6328_FCVO_SHIFT) ++#define BCM6328_MEMC_ADDR ((void __iomem *)CKSEG1ADDR(0x10003000)) + ++#define BCM6358_MEMC_ADDR ((void __iomem *)0xfffe1200) + #define REG_BCM6358_DDR_PLLC ((void __iomem *)0xfffe12b8) + #define BCM6358_PLLC_M1_SHIFT 0 + #define BCM6358_PLLC_M1_MASK (0xff << BCM6358_PLLC_M1_SHIFT) +@@ -53,7 +57,9 @@ + #define REG_BCM6362_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001814)) + #define BCM6362_FCVO_SHIFT 1 + #define BCM6362_FCVO_MASK (0x1f << BCM6362_FCVO_SHIFT) ++#define BCM6362_MEMC_ADDR ((void __iomem *)CKSEG1ADDR(0x10003000)) + ++#define BCM6368_MEMC_ADDR ((void __iomem *)CKSEG1ADDR(0x10001200)) + #define REG_BCM6368_DDR_PLLC ((void __iomem *)CKSEG1ADDR(0x100012a0)) + #define BCM6368_PLLC_P1_SHIFT 0 + #define BCM6368_PLLC_P1_MASK (0xf << BCM6368_PLLC_P1_SHIFT) +@@ -68,6 +74,21 @@ + #define REG_BCM63268_MISC_SB ((void __iomem *)CKSEG1ADDR(0x10001814)) + #define BCM63268_FCVO_SHIFT 21 + #define BCM63268_FCVO_MASK (0xf << BCM63268_FCVO_SHIFT) ++#define BCM63268_MEMC_ADDR ((void __iomem *)CKSEG1ADDR(0x10003000)) ++ ++#define SDRAM_CFG_REG 0x0 ++#define SDRAM_SPACE_SHIFT 4 ++#define SDRAM_SPACE_MASK (0xf << SDRAM_SPACE_SHIFT) ++ ++#define MEMC_CFG_REG 0x4 ++#define MEMC_CFG_32B_SHIFT 1 ++#define MEMC_CFG_32B_MASK (1 << MEMC_CFG_32B_SHIFT) ++#define MEMC_CFG_COL_SHIFT 3 ++#define MEMC_CFG_COL_MASK (0x3 << MEMC_CFG_COL_SHIFT) ++#define MEMC_CFG_ROW_SHIFT 6 ++#define MEMC_CFG_ROW_MASK (0x3 << MEMC_CFG_ROW_SHIFT) ++ ++#define DDR_CSEND_REG 0x8 + + extern bool bmips_rac_flush_disable; + +@@ -78,6 +99,11 @@ struct bmips_cpufreq { + u32 (*cpu_freq)(void); + }; + ++struct bmips_memsize { ++ const char *compatible; ++ phys_addr_t (*mem_size)(void); ++}; ++ + struct bmips_quirk { + const char *compatible; + void (*quirk_fn)(void); +@@ -352,9 +378,90 @@ void __init plat_time_init(void) + mips_hpt_frequency = freq; + } + ++static inline phys_addr_t bmips_dram_size(unsigned int cols, ++ unsigned int rows, ++ unsigned int is_32b, ++ unsigned int banks) ++{ ++ rows += 11; /* 0 => 11 address bits ... 2 => 13 address bits */ ++ cols += 8; /* 0 => 8 address bits ... 2 => 10 address bits */ ++ is_32b += 1; ++ ++ return 1 << (cols + rows + is_32b + banks); ++} ++ ++static phys_addr_t _bcm6318_memsize(void __iomem *addr) ++{ ++ u32 val; ++ ++ val = __raw_readl(addr + SDRAM_CFG_REG); ++ val = (val & SDRAM_SPACE_MASK) >> SDRAM_SPACE_SHIFT; ++ ++ return (1 << (val + 20)); ++} ++ ++static phys_addr_t _bcm6328_memsize(void __iomem *addr) ++{ ++ return __raw_readl(addr + DDR_CSEND_REG) << 24; ++} ++ ++static phys_addr_t _bcm6358_memsize(void __iomem *addr) ++{ ++ unsigned int cols, rows, is_32b; ++ u32 val; ++ ++ val = __raw_readl(addr + MEMC_CFG_REG); ++ rows = (val & MEMC_CFG_ROW_MASK) >> MEMC_CFG_ROW_SHIFT; ++ cols = (val & MEMC_CFG_COL_MASK) >> MEMC_CFG_COL_SHIFT; ++ is_32b = (val & MEMC_CFG_32B_MASK) ? 0 : 1; ++ ++ return bmips_dram_size(cols, rows, is_32b, 2); ++} ++ ++static phys_addr_t bcm6318_memsize(void) ++{ ++ return _bcm6318_memsize(BCM6318_SDRAM_ADDR); ++} ++ ++static phys_addr_t bcm6328_memsize(void) ++{ ++ return _bcm6328_memsize(BCM6328_MEMC_ADDR); ++} ++ ++static phys_addr_t bcm6358_memsize(void) ++{ ++ return _bcm6358_memsize(BCM6358_MEMC_ADDR); ++} ++ ++static phys_addr_t bcm6362_memsize(void) ++{ ++ return _bcm6328_memsize(BCM6362_MEMC_ADDR); ++} ++ ++static phys_addr_t bcm6368_memsize(void) ++{ ++ return _bcm6358_memsize(BCM6368_MEMC_ADDR); ++} ++ ++static phys_addr_t bcm63268_memsize(void) ++{ ++ return _bcm6328_memsize(BCM63268_MEMC_ADDR); ++} ++ ++static const struct bmips_memsize bmips_memsize_list[] = { ++ { "brcm,bcm6318", &bcm6318_memsize }, ++ { "brcm,bcm6328", &bcm6328_memsize }, ++ { "brcm,bcm6358", &bcm6358_memsize }, ++ { "brcm,bcm6362", &bcm6362_memsize }, ++ { "brcm,bcm6368", &bcm6368_memsize }, ++ { "brcm,bcm63268", &bcm63268_memsize }, ++ { /* sentinel */ } ++}; ++ + void __init plat_mem_setup(void) + { + void *dtb; ++ const struct bmips_memsize *ms; + const struct bmips_quirk *q; + + set_io_port_base(0); +@@ -372,6 +479,18 @@ void __init plat_mem_setup(void) + + __dt_setup_arch(dtb); + ++ for (ms = bmips_memsize_list; ms->mem_size; ms++) { ++ if (of_flat_dt_is_compatible(of_get_flat_dt_root(), ++ ms->compatible)) { ++ phys_addr_t mem = ms->mem_size(); ++ if (mem) { ++ memblock_add(0, mem); ++ printk("%uMB of RAM installed\n", mem >> 20); ++ break; ++ } ++ } ++ } ++ + for (q = bmips_quirk_list; q->quirk_fn; q++) { + if (of_flat_dt_is_compatible(of_get_flat_dt_root(), + q->compatible)) { diff --git a/target/linux/bmips/patches-6.1/202-mips-bmips-tweak-Kconfig-options.patch b/target/linux/bmips/patches-6.1/202-mips-bmips-tweak-Kconfig-options.patch new file mode 100644 index 00000000000..3e0c67d9a44 --- /dev/null +++ b/target/linux/bmips/patches-6.1/202-mips-bmips-tweak-Kconfig-options.patch @@ -0,0 +1,67 @@ +From 20a4b57c0fafd23ae0f6bcab5b5adf4af4c80280 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Thu, 16 Mar 2023 19:31:21 +0100 +Subject: [PATCH] mips: bmips: tweak Kconfig options +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + arch/mips/Kconfig | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -272,19 +272,13 @@ config BMIPS_GENERIC + select SYNC_R4K + select COMMON_CLK + select BCM6345_L1_IRQ +- select BCM7038_L1_IRQ +- select BCM7120_L2_IRQ +- select BRCMSTB_L2_IRQ + select IRQ_MIPS_CPU + select DMA_NONCOHERENT + select SYS_SUPPORTS_32BIT_KERNEL +- select SYS_SUPPORTS_LITTLE_ENDIAN + select SYS_SUPPORTS_BIG_ENDIAN +- select SYS_SUPPORTS_HIGHMEM + select SYS_HAS_CPU_BMIPS32_3300 + select SYS_HAS_CPU_BMIPS4350 + select SYS_HAS_CPU_BMIPS4380 +- select SYS_HAS_CPU_BMIPS5000 + select SWAP_IO_SPACE + select USB_EHCI_BIG_ENDIAN_DESC if CPU_BIG_ENDIAN + select USB_EHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN +@@ -294,6 +288,7 @@ config BMIPS_GENERIC + select HAVE_PCI + select PCI_DRIVERS_GENERIC + select FW_CFE ++ select MIPS_L1_CACHE_SHIFT_4 + help + Build a generic DT-based kernel image that boots on select + BCM33xx cable modem chips, BCM63xx DSL chips, and BCM7xxx set-top +--- a/drivers/irqchip/Kconfig 2023-05-30 19:59:04.671531242 +0200 ++++ b/drivers/irqchip/Kconfig 2023-05-30 19:59:55.203880697 +0200 +@@ -121,7 +121,6 @@ config BCM6345_L1_IRQ + config BCM7038_L1_IRQ + tristate "Broadcom STB 7038-style L1/L2 interrupt controller driver" + depends on ARCH_BRCMSTB || BMIPS_GENERIC +- default ARCH_BRCMSTB || BMIPS_GENERIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP +@@ -129,14 +128,12 @@ config BCM7038_L1_IRQ + config BCM7120_L2_IRQ + tristate "Broadcom STB 7120-style L2 interrupt controller driver" + depends on ARCH_BRCMSTB || BMIPS_GENERIC +- default ARCH_BRCMSTB || BMIPS_GENERIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + + config BRCMSTB_L2_IRQ + tristate "Broadcom STB generic L2 interrupt controller driver" + depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC +- default ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN diff --git a/target/linux/bmips/patches-6.1/210-revert-macronix-nand-block-protection.patch b/target/linux/bmips/patches-6.1/210-revert-macronix-nand-block-protection.patch new file mode 100644 index 00000000000..25a16084efe --- /dev/null +++ b/target/linux/bmips/patches-6.1/210-revert-macronix-nand-block-protection.patch @@ -0,0 +1,114 @@ +From 5a37811de679bff03e9c5a746f75574910ede964 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 22 Mar 2023 20:52:13 +0100 +Subject: [PATCH] Revert "mtd: rawnand: Macronix: Add support for block + protection" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 03a539c7a118427a6609a26461358c56ac8f3a06. + +Macronix block protection doesn't seem to be supported on Sercomm H-500s +devices since it hangs the device. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/mtd/nand/raw/nand_macronix.c | 72 ---------------------------- + 1 file changed, 72 deletions(-) + +--- a/drivers/mtd/nand/raw/nand_macronix.c ++++ b/drivers/mtd/nand/raw/nand_macronix.c +@@ -12,10 +12,6 @@ + #define MACRONIX_READ_RETRY_BIT BIT(0) + #define MACRONIX_NUM_READ_RETRY_MODES 6 + +-#define ONFI_FEATURE_ADDR_MXIC_PROTECTION 0xA0 +-#define MXIC_BLOCK_PROTECTION_ALL_LOCK 0x38 +-#define MXIC_BLOCK_PROTECTION_ALL_UNLOCK 0x0 +- + #define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 + #define MACRONIX_RANDOMIZER_BIT BIT(1) + #define MACRONIX_RANDOMIZER_ENPGM BIT(0) +@@ -179,73 +175,6 @@ static void macronix_nand_fix_broken_get + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + } + +-/* +- * Macronix NAND supports Block Protection by Protectoin(PT) pin; +- * active high at power-on which protects the entire chip even the #WP is +- * disabled. Lock/unlock protection area can be partition according to +- * protection bits, i.e. upper 1/2 locked, upper 1/4 locked and so on. +- */ +-static int mxic_nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len) +-{ +- u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; +- int ret; +- +- feature[0] = MXIC_BLOCK_PROTECTION_ALL_LOCK; +- nand_select_target(chip, 0); +- ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, +- feature); +- nand_deselect_target(chip); +- if (ret) +- pr_err("%s all blocks failed\n", __func__); +- +- return ret; +-} +- +-static int mxic_nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len) +-{ +- u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; +- int ret; +- +- feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; +- nand_select_target(chip, 0); +- ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, +- feature); +- nand_deselect_target(chip); +- if (ret) +- pr_err("%s all blocks failed\n", __func__); +- +- return ret; +-} +- +-static void macronix_nand_block_protection_support(struct nand_chip *chip) +-{ +- u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; +- int ret; +- +- bitmap_set(chip->parameters.get_feature_list, +- ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); +- +- feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; +- nand_select_target(chip, 0); +- ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, +- feature); +- nand_deselect_target(chip); +- if (ret || feature[0] != MXIC_BLOCK_PROTECTION_ALL_LOCK) { +- if (ret) +- pr_err("Block protection check failed\n"); +- +- bitmap_clear(chip->parameters.get_feature_list, +- ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); +- return; +- } +- +- bitmap_set(chip->parameters.set_feature_list, +- ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); +- +- chip->ops.lock_area = mxic_nand_lock; +- chip->ops.unlock_area = mxic_nand_unlock; +-} +- + static int nand_power_down_op(struct nand_chip *chip) + { + int ret; +@@ -323,7 +252,6 @@ static int macronix_nand_init(struct nan + + macronix_nand_fix_broken_get_timings(chip); + macronix_nand_onfi_init(chip); +- macronix_nand_block_protection_support(chip); + macronix_nand_deep_power_down_support(chip); + + return 0; diff --git a/target/linux/bmips/patches-6.1/500-net-broadcom-add-BCM6368-enetsw-controller-driver.patch b/target/linux/bmips/patches-6.1/500-net-broadcom-add-BCM6368-enetsw-controller-driver.patch new file mode 100644 index 00000000000..71ed53235fc --- /dev/null +++ b/target/linux/bmips/patches-6.1/500-net-broadcom-add-BCM6368-enetsw-controller-driver.patch @@ -0,0 +1,44 @@ +From 590b60fb08cb1e70fe02d3f407c6b3dbe9ad06ff Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 07:34:39 +0100 +Subject: [PATCH] net: broadcom: add BCM6368 enetsw controller driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This controller is present on BCM6318, BCM6328, BCM6362, BCM6368 and BCM63268 +SoCs. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/ethernet/broadcom/Kconfig | 8 ++++++++ + drivers/net/ethernet/broadcom/Makefile | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/net/ethernet/broadcom/Kconfig ++++ b/drivers/net/ethernet/broadcom/Kconfig +@@ -68,6 +68,14 @@ config BCM63XX_ENET + This driver supports the ethernet MACs in the Broadcom 63xx + MIPS chipset family (BCM63XX). + ++config BCM6368_ENETSW ++ tristate "Broadcom BCM6368 internal mac support" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ default y ++ help ++ This driver supports Ethernet controller integrated into Broadcom ++ BCM6368 family SoCs. ++ + config BCMGENET + tristate "Broadcom GENET internal MAC support" + depends on HAS_IOMEM +--- a/drivers/net/ethernet/broadcom/Makefile ++++ b/drivers/net/ethernet/broadcom/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_B44) += b44.o + obj-$(CONFIG_BCM4908_ENET) += bcm4908_enet.o + obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o ++obj-$(CONFIG_BCM6368_ENETSW) += bcm6368-enetsw.o + obj-$(CONFIG_BCMGENET) += genet/ + obj-$(CONFIG_BNX2) += bnx2.o + obj-$(CONFIG_CNIC) += cnic.o diff --git a/target/linux/bmips/patches-6.1/501-net-broadcom-add-BCM6348-enet-controller-driver.patch b/target/linux/bmips/patches-6.1/501-net-broadcom-add-BCM6348-enet-controller-driver.patch new file mode 100644 index 00000000000..af09f4dd4f5 --- /dev/null +++ b/target/linux/bmips/patches-6.1/501-net-broadcom-add-BCM6348-enet-controller-driver.patch @@ -0,0 +1,43 @@ +From 590b60fb08cb1e70fe02d3f407c6b3dbe9ad06ff Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 1 Mar 2021 07:34:39 +0100 +Subject: [PATCH] net: broadcom: add BCM6348 enetsw controller driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This controller is present on BCM6338, BCM6348 and BCM6358 SoCs. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/ethernet/broadcom/Kconfig | 8 ++++++++ + drivers/net/ethernet/broadcom/Makefile | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/net/ethernet/broadcom/Kconfig ++++ b/drivers/net/ethernet/broadcom/Kconfig +@@ -68,6 +68,14 @@ config BCM63XX_ENET + This driver supports the ethernet MACs in the Broadcom 63xx + MIPS chipset family (BCM63XX). + ++config BCM6348_ENET ++ tristate "Broadcom BCM6348 internal mac support" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ default y ++ help ++ This driver supports Ethernet controller integrated into Broadcom ++ BCM6348 family SoCs. ++ + config BCM6368_ENETSW + tristate "Broadcom BCM6368 internal mac support" + depends on BMIPS_GENERIC || COMPILE_TEST +--- a/drivers/net/ethernet/broadcom/Makefile ++++ b/drivers/net/ethernet/broadcom/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_B44) += b44.o + obj-$(CONFIG_BCM4908_ENET) += bcm4908_enet.o + obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o ++obj-$(CONFIG_BCM6348_ENET) += bcm6348-enet.o + obj-$(CONFIG_BCM6368_ENETSW) += bcm6368-enetsw.o + obj-$(CONFIG_BCMGENET) += genet/ + obj-$(CONFIG_BNX2) += bnx2.o diff --git a/target/linux/bmips/patches-6.1/502-net-mdio-mux-bcm6368-allow-disabling.patch b/target/linux/bmips/patches-6.1/502-net-mdio-mux-bcm6368-allow-disabling.patch new file mode 100644 index 00000000000..6071bea42d2 --- /dev/null +++ b/target/linux/bmips/patches-6.1/502-net-mdio-mux-bcm6368-allow-disabling.patch @@ -0,0 +1,23 @@ +From 21145a89c79a22c4fb719cce5a2f4e3373d39756 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 May 2023 18:16:46 +0200 +Subject: [PATCH] net: mdio: mux-bcm6368: allow disabling for bmips +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/net/mdio/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -219,7 +219,6 @@ config MDIO_BUS_MUX_BCM6368 + tristate "Broadcom BCM6368 MDIO bus multiplexers" + depends on OF && OF_MDIO && (BMIPS_GENERIC || COMPILE_TEST) + select MDIO_BUS_MUX +- default BMIPS_GENERIC + help + This module provides a driver for MDIO bus multiplexers found in + BCM6368 based Broadcom SoCs. This multiplexer connects one of several diff --git a/target/linux/bmips/patches-6.1/600-mips-bmips-add-pci-support.patch b/target/linux/bmips/patches-6.1/600-mips-bmips-add-pci-support.patch new file mode 100644 index 00000000000..f28dda908d7 --- /dev/null +++ b/target/linux/bmips/patches-6.1/600-mips-bmips-add-pci-support.patch @@ -0,0 +1,34 @@ +From 7742c1ba191a005a1356ff89b5fe2279d6f0ec4d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 May 2023 18:18:43 +0200 +Subject: [PATCH] mips: bmips: add PCI support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + arch/mips/Kconfig | 1 + + arch/mips/pci/Makefile | 1 + + 2 files changed, 1 insertions(+), 1 deletions(-) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -286,7 +286,6 @@ config BMIPS_GENERIC + select USB_OHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN + select HARDIRQS_SW_RESEND + select HAVE_PCI +- select PCI_DRIVERS_GENERIC + select FW_CFE + select MIPS_L1_CACHE_SHIFT_4 + help +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_PCI_XTALK_BRIDGE) += pci-xt + # These are still pretty much in the old state, watch, go blind. + # + obj-$(CONFIG_ATH79) += fixup-ath79.o ++obj-$(CONFIG_BMIPS_GENERIC) += fixup-bmips.o + obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o + obj-$(CONFIG_LEMOTE_FULOONG2E) += fixup-fuloong2e.o ops-loongson2.o + obj-$(CONFIG_LEMOTE_MACH2F) += fixup-lemote2f.o ops-loongson2.o diff --git a/target/linux/bmips/patches-6.1/601-pci-controllers-add-bcm6328-pcie-support.patch b/target/linux/bmips/patches-6.1/601-pci-controllers-add-bcm6328-pcie-support.patch new file mode 100644 index 00000000000..0eb973ce30e --- /dev/null +++ b/target/linux/bmips/patches-6.1/601-pci-controllers-add-bcm6328-pcie-support.patch @@ -0,0 +1,36 @@ +From 49133041e0a5770decf1a25f575764d13a0d425c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 May 2023 18:20:10 +0200 +Subject: [PATCH] pci: add bcm6328-pcie support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/pci/controller/Kconfig | 5 +++++ + drivers/pci/controller/Makefile | 1 + + 2 files changed, 6 insertions(+) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -3,6 +3,11 @@ + menu "PCI controller drivers" + depends on PCI + ++config PCIE_BCM6328 ++ bool "BCM6328 PCIe controller" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ depends on OF ++ + config PCI_MVEBU + tristate "Marvell EBU PCIe controller" + depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_BCM6328) += pcie-bcm6328.o + obj-$(CONFIG_PCIE_CADENCE) += cadence/ + obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o + obj-$(CONFIG_PCI_IXP4XX) += pci-ixp4xx.o diff --git a/target/linux/bmips/patches-6.1/602-pci-controllers-add-bcm6318-pcie-support.patch b/target/linux/bmips/patches-6.1/602-pci-controllers-add-bcm6318-pcie-support.patch new file mode 100644 index 00000000000..1a3ec1db2a6 --- /dev/null +++ b/target/linux/bmips/patches-6.1/602-pci-controllers-add-bcm6318-pcie-support.patch @@ -0,0 +1,36 @@ +From cc3c30bdc98eabbaa07c64302eb5124a0f4a74f0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 May 2023 18:20:46 +0200 +Subject: [PATCH] pci: add bcm6318-pcie support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/pci/controller/Kconfig | 5 +++++ + drivers/pci/controller/Makefile | 1 + + 2 files changed, 6 insertions(+) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -3,6 +3,11 @@ + menu "PCI controller drivers" + depends on PCI + ++config PCIE_BCM6318 ++ bool "BCM6318 PCIe controller" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ depends on OF ++ + config PCIE_BCM6328 + bool "BCM6328 PCIe controller" + depends on BMIPS_GENERIC || COMPILE_TEST +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_BCM6318) += pcie-bcm6318.o + obj-$(CONFIG_PCIE_BCM6328) += pcie-bcm6328.o + obj-$(CONFIG_PCIE_CADENCE) += cadence/ + obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o diff --git a/target/linux/bmips/patches-6.1/603-pci-controllers-add-bcm6348-pci-support.patch b/target/linux/bmips/patches-6.1/603-pci-controllers-add-bcm6348-pci-support.patch new file mode 100644 index 00000000000..32aeea6a74d --- /dev/null +++ b/target/linux/bmips/patches-6.1/603-pci-controllers-add-bcm6348-pci-support.patch @@ -0,0 +1,36 @@ +From 5e7813e5725d79d00e0988472c306490fc48b3e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 May 2023 18:21:19 +0200 +Subject: [PATCH] pci: add bcm6348-pci support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/pci/controller/Kconfig | 5 +++++ + drivers/pci/controller/Makefile | 1 + + 2 files changed, 6 insertions(+) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -3,6 +3,11 @@ + menu "PCI controller drivers" + depends on PCI + ++config PCI_BCM6348 ++ bool "BCM6348 PCI controller" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ depends on OF ++ + config PCIE_BCM6318 + bool "BCM6318 PCIe controller" + depends on BMIPS_GENERIC || COMPILE_TEST +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCI_BCM6348) += pci-bcm6348.o + obj-$(CONFIG_PCIE_BCM6318) += pcie-bcm6318.o + obj-$(CONFIG_PCIE_BCM6328) += pcie-bcm6328.o + obj-$(CONFIG_PCIE_CADENCE) += cadence/ diff --git a/target/linux/bmips/patches-6.1/700-leds-add-support-for-Sercomm-MSP430-LED-controller.patch b/target/linux/bmips/patches-6.1/700-leds-add-support-for-Sercomm-MSP430-LED-controller.patch new file mode 100644 index 00000000000..ca3c5c74891 --- /dev/null +++ b/target/linux/bmips/patches-6.1/700-leds-add-support-for-Sercomm-MSP430-LED-controller.patch @@ -0,0 +1,45 @@ +From 1a5f2263d388016c88d39e141c7eb8085c9313fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 5 Apr 2023 08:07:00 +0200 +Subject: [PATCH] leds: add support for Sercomm MSP430 LED controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Sercomm added an external MSP430G2513 for controlling LEDs through SPI on some +boards. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/leds/Kconfig | 9 +++++++++ + drivers/leds/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -281,6 +281,15 @@ config LEDS_COBALT_RAQ + help + This option enables support for the Cobalt Raq series LEDs. + ++config LEDS_SERCOMM_MSP430 ++ tristate "LED support for Sercomm MSP430 SPI LED controllers" ++ depends on LEDS_CLASS ++ depends on SPI ++ depends on OF ++ help ++ This option enables support for the Sercomm MSP430G2513 SPI LED ++ controllers. ++ + config LEDS_SUNFIRE + tristate "LED support for SunFire servers." + depends on LEDS_CLASS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o + obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o + obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o ++obj-$(CONFIG_LEDS_SERCOMM_MSP430) += leds-sercomm-msp430.o + obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o + obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o + obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/target/linux/bmips/patches-6.1/800-jffs2-work-around-unaligned-accesses-failing-on-bcm6.patch b/target/linux/bmips/patches-6.1/800-jffs2-work-around-unaligned-accesses-failing-on-bcm6.patch new file mode 100644 index 00000000000..d98d27c6b43 --- /dev/null +++ b/target/linux/bmips/patches-6.1/800-jffs2-work-around-unaligned-accesses-failing-on-bcm6.patch @@ -0,0 +1,26 @@ +From ff3409ab17d56450943364ba49a16960e3cdda9b Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 6 Apr 2014 22:33:16 +0200 +Subject: [RFC] jffs2: work around unaligned accesses failing on bcm63xx/smp + +Unligned memcpy_fromio randomly fails with an unaligned dst. Work around +it by ensuring we are always doing aligned copies. + +Should fix filename corruption in jffs2 with SMP. + +Signed-off-by: Jonas Gorski +--- + fs/jffs2/nodelist.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/jffs2/nodelist.h ++++ b/fs/jffs2/nodelist.h +@@ -259,7 +259,7 @@ struct jffs2_full_dirent + uint32_t ino; /* == zero for unlink */ + unsigned int nhash; + unsigned char type; +- unsigned char name[]; ++ unsigned char name[] __attribute__((aligned((sizeof(long))))); + }; + + /* diff --git a/target/linux/gemini/Makefile b/target/linux/gemini/Makefile index 4266db16cd3..284d1247cb2 100644 --- a/target/linux/gemini/Makefile +++ b/target/linux/gemini/Makefile @@ -12,6 +12,7 @@ CPU_TYPE:=fa526 SUBTARGETS:=generic KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 define Target/Description Build firmware images for the StorLink/Cortina Gemini CS351x ARM FA526 CPU diff --git a/target/linux/gemini/config-6.1 b/target/linux/gemini/config-6.1 new file mode 100644 index 00000000000..41f7093c1ce --- /dev/null +++ b/target/linux/gemini/config-6.1 @@ -0,0 +1,427 @@ +CONFIG_ALIGNMENT_TRAP=y +CONFIG_AMBA_PL08X=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_GEMINI=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_ARCH_MOXART is not set +CONFIG_ARCH_MULTIPLATFORM=y +CONFIG_ARCH_MULTI_V4=y +# CONFIG_ARCH_MULTI_V4T is not set +CONFIG_ARCH_MULTI_V4_V5=y +# CONFIG_ARCH_MULTI_V5 is not set +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_APPENDED_DTB=y +# CONFIG_ARM_ATAG_DTB_COMPAT is not set +CONFIG_ARM_CRYPTO=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_PATCH_PHYS_VIRT=y +# CONFIG_ARM_SMMU is not set +CONFIG_ARM_UNWIND=y +CONFIG_ATA=y +CONFIG_ATAGS=y +CONFIG_ATA_FORCE=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_AUTO_ZRELADDR=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BOUNCE=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_PERCENTAGE=10 +# CONFIG_CMA_SIZE_SEL_MAX is not set +# CONFIG_CMA_SIZE_SEL_MBYTES is not set +# CONFIG_CMA_SIZE_SEL_MIN is not set +CONFIG_CMA_SIZE_SEL_PERCENTAGE=y +# CONFIG_CMA_SYSFS is not set +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_GEMINI=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONTIG_ALLOC=y +CONFIG_COREDUMP=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_CPU_32v4=y +CONFIG_CPU_ABRT_EV4=y +CONFIG_CPU_CACHE_FA=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_FA=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +CONFIG_CPU_FA526=y +CONFIG_CPU_NO_EFFICIENT_FFS=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_THERMAL=y +CONFIG_CPU_TLB_FA=y +CONFIG_CPU_USE_DOMAINS=y +CONFIG_CRASH_CORE=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_ITU_T=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_DEV_SL3516=y +# CONFIG_CRYPTO_DEV_SL3516_DEBUG is not set +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ENGINE=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DMADEVICES=y +CONFIG_DMATEST=y +CONFIG_DMA_CMA=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_ENGINE_RAID=y +CONFIG_DMA_OF=y +CONFIG_DMA_OPS=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DRM=y +CONFIG_DRM_BRIDGE=y +CONFIG_DRM_FBDEV_EMULATION=y +CONFIG_DRM_FBDEV_OVERALLOC=100 +CONFIG_DRM_GEM_CMA_HELPER=y +CONFIG_DRM_KMS_CMA_HELPER=y +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_PANEL=y +CONFIG_DRM_PANEL_BRIDGE=y +CONFIG_DRM_PANEL_ILITEK_IL9322=y +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y +CONFIG_DRM_TVE200=y +CONFIG_DTC=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EEPROM_93CX6=y +CONFIG_ELF_CORE=y +# CONFIG_EMBEDDED is not set +# CONFIG_EXPERT is not set +CONFIG_EXT4_FS=y +CONFIG_FARADAY_FTINTC010=y +CONFIG_FB=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_CMDLINE=y +CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_SYS_COPYAREA=y +CONFIG_FB_SYS_FILLRECT=y +CONFIG_FB_SYS_FOPS=y +CONFIG_FB_SYS_IMAGEBLIT=y +CONFIG_FHANDLE=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FONT_8x16=y +CONFIG_FONT_8x8=y +CONFIG_FONT_SUPPORT=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FTTMR010_TIMER=y +CONFIG_FTWDT010_WATCHDOG=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_GEMINI_ETHERNET=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GLOB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_FTGPIO010=y +CONFIG_GPIO_GENERIC=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HDMI=y +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +CONFIG_HWMON=y +CONFIG_HW_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_HZ_FIXED=0 +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT=y +CONFIG_INPUT_KEYBOARD=y +# 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_IPC_NS=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +# CONFIG_ISDN is not set +CONFIG_JBD2=y +CONFIG_KALLSYMS=y +CONFIG_KCMP=y +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +CONFIG_KEXEC=y +CONFIG_KEXEC_CORE=y +CONFIG_KEYBOARD_DLINK_DIR685=y +CONFIG_KMAP_LOCAL=y +CONFIG_KMAP_LOCAL_NON_LINEAR_PTE_ARRAY=y +# CONFIG_LDM_DEBUG is not set +CONFIG_LDM_PARTITION=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_LOGO_LINUX_MONO is not set +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_GPIO=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_MQ_IOSCHED_DEADLINE=y +CONFIG_MQ_IOSCHED_KYBER=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_GEMINI=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_WRGG_FW=y +CONFIG_NAMESPACES=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_KUSER_HELPERS=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_REALTEK_SMI=y +CONFIG_NET_DSA_TAG_RTL4_A=y +CONFIG_NET_NS=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_PATA_FTIDE010=y +CONFIG_PCI=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_FTPCI100=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PID_NS=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_GEMINI=y +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GEMINI_POWEROFF=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_PREEMPT=y +CONFIG_PREEMPTION=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_RATIONAL=y +CONFIG_RD_BZIP2=y +CONFIG_RD_GZIP=y +CONFIG_RD_LZ4=y +CONFIG_RD_LZMA=y +CONFIG_RD_LZO=y +CONFIG_RD_XZ=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_RELAY=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RSEQ=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_FTRTC010=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RTC_MC146818_LIB=y +CONFIG_RTC_NVMEM=y +CONFIG_SATA_GEMINI=y +CONFIG_SATA_HOST=y +CONFIG_SATA_PMP=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_PROC_FS is not set +CONFIG_SENSORS_DRIVETEMP=y +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_SENSORS_LM75=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_EXAR=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=1 +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_SERPORT=y +CONFIG_SG_POOL=y +CONFIG_SLUB_DEBUG=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_MASTER=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_SRCU=y +# CONFIG_STRIP_ASM_SYMS is not set +CONFIG_SWPHY=y +CONFIG_SYNC_FILE=y +CONFIG_SYSFS_SYSCALL=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_TMPFS_POSIX_ACL=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_UNUSED_BOARD_FILES=n +CONFIG_UNWINDER_ARM=y +CONFIG_USB_FOTG210=m +CONFIG_USB_FOTG210_HCD=y +CONFIG_USB_FOTG210_UDC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_SUPPORT=y +CONFIG_USER_NS=y +CONFIG_USE_OF=y +CONFIG_UTS_NS=y +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +CONFIG_VITESSE_PHY=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_X86=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/gemini/modules.mk b/target/linux/gemini/modules.mk new file mode 100644 index 00000000000..104ad902799 --- /dev/null +++ b/target/linux/gemini/modules.mk @@ -0,0 +1,14 @@ +define KernelPackage/usb-fotg210 + TITLE:=Support for FOTG210 USB host and device controllers + DEPENDS:=@USB_SUPPORT @TARGET_gemini + KCONFIG:=CONFIG_USB_FOTG210 \ + CONFIG_USB_FOTG210_HCD + FILES:=$(if $(CONFIG_USB_FOTG210_HCD),$(LINUX_DIR)/drivers/usb/host/fotg210-hcd.ko@lt6.1) \ + $(if $(CONFIG_USB_FOTG210),$(LINUX_DIR)/drivers/usb/fotg210/fotg210.ko@ge6.1) + AUTOLOAD:=$(call AutoLoad,50, \ + $(if $(CONFIG_USB_FOTG210_HCD),fotg210-hcd@lt6.1) \ + $(if $(CONFIG_USB_FOTG210),fotg210@ge6.1),1) + $(call AddDepends/usb) +endef + +$(eval $(call KernelPackage,usb-fotg210)) diff --git a/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch b/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch new file mode 100644 index 00000000000..943b166d7e3 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0001-usb-phy-phy-gpio-vbus-usb-Add-device-tree-probing.patch @@ -0,0 +1,67 @@ +From d5a026cc8306ccd3e99e1455c87e38f8e6fa18df Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 7 Nov 2022 00:05:06 +0100 +Subject: [PATCH 01/29] usb: phy: phy-gpio-vbus-usb: Add device tree probing + +Make it possible to probe the GPIO VBUS detection driver +from the device tree compatible for GPIO USB B connectors. + +Since this driver is using the "gpio-usb-b-connector" +compatible, it is important to discern it from the role +switch connector driver (which does not provide a phy), +so we add some Kconfig text and depend on !USB_CONN_GPIO. + +Cc: Rob Herring +Cc: Prashant Malani +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221106230506.1646101-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -93,12 +93,16 @@ config USB_GPIO_VBUS + tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" + depends on GPIOLIB || COMPILE_TEST + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' ++ depends on !USB_CONN_GPIO + select USB_PHY + help + Provides simple GPIO VBUS sensing for controllers with an + internal transceiver via the usb_phy interface, and + optionally control of a D+ pullup GPIO as well as a VBUS +- current limit regulator. ++ current limit regulator. This driver is for devices that do ++ NOT support role switch. OTG devices that can do role switch ++ (master/peripheral) shall use the USB based connection ++ detection driver USB_CONN_GPIO. + + config OMAP_OTG + tristate "OMAP USB OTG controller driver" +--- a/drivers/usb/phy/phy-gpio-vbus-usb.c ++++ b/drivers/usb/phy/phy-gpio-vbus-usb.c +@@ -366,12 +366,24 @@ static const struct dev_pm_ops gpio_vbus + + MODULE_ALIAS("platform:gpio-vbus"); + ++/* ++ * NOTE: this driver matches against "gpio-usb-b-connector" for ++ * devices that do NOT support role switch. ++ */ ++static const struct of_device_id gpio_vbus_of_match[] = { ++ { ++ .compatible = "gpio-usb-b-connector", ++ }, ++ {}, ++}; ++ + static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + #ifdef CONFIG_PM + .pm = &gpio_vbus_dev_pm_ops, + #endif ++ .of_match_table = gpio_vbus_of_match, + }, + .probe = gpio_vbus_probe, + .remove = gpio_vbus_remove, diff --git a/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch b/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch new file mode 100644 index 00000000000..902bf4c68fe --- /dev/null +++ b/target/linux/gemini/patches-6.1/0002-usb-fotg210-Collect-pieces-of-dual-mode-controller.patch @@ -0,0 +1,15993 @@ +From 30367636930864f71b2bd462adedcf8484313864 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:06 +0200 +Subject: [PATCH 02/29] usb: fotg210: Collect pieces of dual mode controller + +The Faraday FOTG210 is a dual-mode OTG USB controller that can +act as host, peripheral or both. To be able to probe from one +hardware description and to follow the pattern of other dual- +mode controllers such as MUSB or MTU3 we need to collect the +two, currently completely separate drivers in the same +directory. + +After this, users need to select the main symbol USB_FOTG210 +and then each respective subdriver. We pave the road to +compile both drivers into the same kernel and select the +one we want to use at probe() time, and possibly add OTG +support in the end. + +This patch doesn't do much more than create the new symbol +and collect the drivers in one place. We also add a comment +for the section of dual-mode controllers in the Kconfig +file so people can see what these selections are about. + +Also add myself as maintainer as there has been little +response on my patches to these drivers. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -111,8 +111,12 @@ source "drivers/usb/usbip/Kconfig" + + endif + ++comment "USB dual-mode controller drivers" ++ + source "drivers/usb/cdns3/Kconfig" + ++source "drivers/usb/fotg210/Kconfig" ++ + source "drivers/usb/mtu3/Kconfig" + + source "drivers/usb/musb/Kconfig" +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ + obj-$(CONFIG_USB_CDNS3) += cdns3/ + obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ + ++obj-$(CONFIG_USB_FOTG210) += fotg210/ ++ + obj-$(CONFIG_USB_MON) += mon/ + obj-$(CONFIG_USB_MTU3) += mtu3/ + +--- /dev/null ++++ b/drivers/usb/fotg210/Kconfig +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config USB_FOTG210 ++ tristate "Faraday FOTG210 USB2 Dual Role controller" ++ depends on USB || USB_GADGET ++ depends on HAS_DMA && HAS_IOMEM ++ default ARCH_GEMINI ++ help ++ Faraday FOTG210 is a dual-mode USB controller that can act ++ in both host controller and peripheral controller mode. ++ ++if USB_FOTG210 ++ ++config USB_FOTG210_HCD ++ tristate "Faraday FOTG210 USB Host Controller support" ++ depends on USB ++ help ++ Faraday FOTG210 is an OTG controller which can be configured as ++ an USB2.0 host. It is designed to meet USB2.0 EHCI specification ++ with minor modification. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called fotg210-hcd. ++ ++config USB_FOTG210_UDC ++ depends on USB_GADGET ++ tristate "Faraday FOTG210 USB Peripheral Controller support" ++ help ++ Faraday USB2.0 OTG controller which can be configured as ++ high speed or full speed USB device. This driver suppports ++ Bulk Transfer so far. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "fotg210-udc". ++ ++endif +--- /dev/null ++++ b/drivers/usb/fotg210/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o ++obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o +--- a/drivers/usb/host/fotg210-hcd.c ++++ /dev/null +@@ -1,5727 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* Faraday FOTG210 EHCI-like driver +- * +- * Copyright (c) 2013 Faraday Technology Corporation +- * +- * Author: Yuan-Hsin Chen +- * Feng-Hsin Chiang +- * Po-Yu Chuang +- * +- * Most of code borrowed from the Linux-3.7 EHCI driver +- */ +-#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 +- +-#define DRIVER_AUTHOR "Yuan-Hsin Chen" +-#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" +-static const char hcd_name[] = "fotg210_hcd"; +- +-#undef FOTG210_URB_TRACE +-#define FOTG210_STATS +- +-/* magic numbers that can affect system performance */ +-#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +-#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +-#define FOTG210_TUNE_RL_TT 0 +-#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +-#define FOTG210_TUNE_MULT_TT 1 +- +-/* Some drivers think it's safe to schedule isochronous transfers more than 256 +- * ms into the future (partly as a result of an old bug in the scheduling +- * code). In an attempt to avoid trouble, we will use a minimum scheduling +- * length of 512 frames instead of 256. +- */ +-#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ +- +-/* Initial IRQ latency: faster than hw default */ +-static int log2_irq_thresh; /* 0 to 6 */ +-module_param(log2_irq_thresh, int, S_IRUGO); +-MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); +- +-/* initial park setting: slower than hw default */ +-static unsigned park; +-module_param(park, uint, S_IRUGO); +-MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); +- +-/* for link power management(LPM) feature */ +-static unsigned int hird; +-module_param(hird, int, S_IRUGO); +-MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); +- +-#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) +- +-#include "fotg210.h" +- +-#define fotg210_dbg(fotg210, fmt, args...) \ +- dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_err(fotg210, fmt, args...) \ +- dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_info(fotg210, fmt, args...) \ +- dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +-#define fotg210_warn(fotg210, fmt, args...) \ +- dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) +- +-/* check the values in the HCSPARAMS register (host controller _Structural_ +- * parameters) see EHCI spec, Table 2-4 for each value +- */ +-static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) +-{ +- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); +- +- fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, +- HCS_N_PORTS(params)); +-} +- +-/* check the values in the HCCPARAMS register (host controller _Capability_ +- * parameters) see EHCI Spec, Table 2-5 for each value +- */ +-static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) +-{ +- u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); +- +- fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, +- params, +- HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", +- HCC_CANPARK(params) ? " park" : ""); +-} +- +-static void __maybe_unused +-dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) +-{ +- fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, +- hc32_to_cpup(fotg210, &qtd->hw_next), +- hc32_to_cpup(fotg210, &qtd->hw_alt_next), +- hc32_to_cpup(fotg210, &qtd->hw_token), +- hc32_to_cpup(fotg210, &qtd->hw_buf[0])); +- if (qtd->hw_buf[1]) +- fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", +- hc32_to_cpup(fotg210, &qtd->hw_buf[1]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[2]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[3]), +- hc32_to_cpup(fotg210, &qtd->hw_buf[4])); +-} +- +-static void __maybe_unused +-dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- struct fotg210_qh_hw *hw = qh->hw; +- +- fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, +- hw->hw_next, hw->hw_info1, hw->hw_info2, +- hw->hw_current); +- +- dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); +-} +- +-static void __maybe_unused +-dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +-{ +- fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, +- itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), +- itd->urb); +- +- fotg210_dbg(fotg210, +- " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", +- hc32_to_cpu(fotg210, itd->hw_transaction[0]), +- hc32_to_cpu(fotg210, itd->hw_transaction[1]), +- hc32_to_cpu(fotg210, itd->hw_transaction[2]), +- hc32_to_cpu(fotg210, itd->hw_transaction[3]), +- hc32_to_cpu(fotg210, itd->hw_transaction[4]), +- hc32_to_cpu(fotg210, itd->hw_transaction[5]), +- hc32_to_cpu(fotg210, itd->hw_transaction[6]), +- hc32_to_cpu(fotg210, itd->hw_transaction[7])); +- +- fotg210_dbg(fotg210, +- " buf: %08x %08x %08x %08x %08x %08x %08x\n", +- hc32_to_cpu(fotg210, itd->hw_bufp[0]), +- hc32_to_cpu(fotg210, itd->hw_bufp[1]), +- hc32_to_cpu(fotg210, itd->hw_bufp[2]), +- hc32_to_cpu(fotg210, itd->hw_bufp[3]), +- hc32_to_cpu(fotg210, itd->hw_bufp[4]), +- hc32_to_cpu(fotg210, itd->hw_bufp[5]), +- hc32_to_cpu(fotg210, itd->hw_bufp[6])); +- +- fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", +- itd->index[0], itd->index[1], itd->index[2], +- itd->index[3], itd->index[4], itd->index[5], +- itd->index[6], itd->index[7]); +-} +- +-static int __maybe_unused +-dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +-{ +- return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", +- label, label[0] ? " " : "", status, +- (status & STS_ASS) ? " Async" : "", +- (status & STS_PSS) ? " Periodic" : "", +- (status & STS_RECL) ? " Recl" : "", +- (status & STS_HALT) ? " Halt" : "", +- (status & STS_IAA) ? " IAA" : "", +- (status & STS_FATAL) ? " FATAL" : "", +- (status & STS_FLR) ? " FLR" : "", +- (status & STS_PCD) ? " PCD" : "", +- (status & STS_ERR) ? " ERR" : "", +- (status & STS_INT) ? " INT" : ""); +-} +- +-static int __maybe_unused +-dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +-{ +- return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", +- label, label[0] ? " " : "", enable, +- (enable & STS_IAA) ? " IAA" : "", +- (enable & STS_FATAL) ? " FATAL" : "", +- (enable & STS_FLR) ? " FLR" : "", +- (enable & STS_PCD) ? " PCD" : "", +- (enable & STS_ERR) ? " ERR" : "", +- (enable & STS_INT) ? " INT" : ""); +-} +- +-static const char *const fls_strings[] = { "1024", "512", "256", "??" }; +- +-static int dbg_command_buf(char *buf, unsigned len, const char *label, +- u32 command) +-{ +- return scnprintf(buf, len, +- "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", +- label, label[0] ? " " : "", command, +- (command & CMD_PARK) ? " park" : "(park)", +- CMD_PARK_CNT(command), +- (command >> 16) & 0x3f, +- (command & CMD_IAAD) ? " IAAD" : "", +- (command & CMD_ASE) ? " Async" : "", +- (command & CMD_PSE) ? " Periodic" : "", +- fls_strings[(command >> 2) & 0x3], +- (command & CMD_RESET) ? " Reset" : "", +- (command & CMD_RUN) ? "RUN" : "HALT"); +-} +- +-static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, +- u32 status) +-{ +- char *sig; +- +- /* signaling state */ +- switch (status & (3 << 10)) { +- case 0 << 10: +- sig = "se0"; +- break; +- case 1 << 10: +- sig = "k"; +- break; /* low speed */ +- case 2 << 10: +- sig = "j"; +- break; +- default: +- sig = "?"; +- break; +- } +- +- scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", +- label, label[0] ? " " : "", port, status, +- status >> 25, /*device address */ +- sig, +- (status & PORT_RESET) ? " RESET" : "", +- (status & PORT_SUSPEND) ? " SUSPEND" : "", +- (status & PORT_RESUME) ? " RESUME" : "", +- (status & PORT_PEC) ? " PEC" : "", +- (status & PORT_PE) ? " PE" : "", +- (status & PORT_CSC) ? " CSC" : "", +- (status & PORT_CONNECT) ? " CONNECT" : ""); +- +- return buf; +-} +- +-/* functions have the "wrong" filename when they're output... */ +-#define dbg_status(fotg210, label, status) { \ +- char _buf[80]; \ +- dbg_status_buf(_buf, sizeof(_buf), label, status); \ +- fotg210_dbg(fotg210, "%s\n", _buf); \ +-} +- +-#define dbg_cmd(fotg210, label, command) { \ +- char _buf[80]; \ +- dbg_command_buf(_buf, sizeof(_buf), label, command); \ +- fotg210_dbg(fotg210, "%s\n", _buf); \ +-} +- +-#define dbg_port(fotg210, label, port, status) { \ +- char _buf[80]; \ +- fotg210_dbg(fotg210, "%s\n", \ +- dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ +-} +- +-/* troubleshooting help: expose state in debugfs */ +-static int debug_async_open(struct inode *, struct file *); +-static int debug_periodic_open(struct inode *, struct file *); +-static int debug_registers_open(struct inode *, struct file *); +-static int debug_async_open(struct inode *, struct file *); +- +-static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +-static int debug_close(struct inode *, struct file *); +- +-static const struct file_operations debug_async_fops = { +- .owner = THIS_MODULE, +- .open = debug_async_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +-static const struct file_operations debug_periodic_fops = { +- .owner = THIS_MODULE, +- .open = debug_periodic_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +-static const struct file_operations debug_registers_fops = { +- .owner = THIS_MODULE, +- .open = debug_registers_open, +- .read = debug_output, +- .release = debug_close, +- .llseek = default_llseek, +-}; +- +-static struct dentry *fotg210_debug_root; +- +-struct debug_buffer { +- ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ +- struct usb_bus *bus; +- struct mutex mutex; /* protect filling of buffer */ +- size_t count; /* number of characters filled into buffer */ +- char *output_buf; +- size_t alloc_size; +-}; +- +-static inline char speed_char(u32 scratch) +-{ +- switch (scratch & (3 << 12)) { +- case QH_FULL_SPEED: +- return 'f'; +- +- case QH_LOW_SPEED: +- return 'l'; +- +- case QH_HIGH_SPEED: +- return 'h'; +- +- default: +- return '?'; +- } +-} +- +-static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) +-{ +- __u32 v = hc32_to_cpu(fotg210, token); +- +- if (v & QTD_STS_ACTIVE) +- return '*'; +- if (v & QTD_STS_HALT) +- return '-'; +- if (!IS_SHORT_READ(v)) +- return ' '; +- /* tries to advance through hw_alt_next */ +- return '/'; +-} +- +-static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, +- char **nextp, unsigned *sizep) +-{ +- u32 scratch; +- u32 hw_curr; +- struct fotg210_qtd *td; +- unsigned temp; +- unsigned size = *sizep; +- char *next = *nextp; +- char mark; +- __le32 list_end = FOTG210_LIST_END(fotg210); +- struct fotg210_qh_hw *hw = qh->hw; +- +- if (hw->hw_qtd_next == list_end) /* NEC does this */ +- mark = '@'; +- else +- mark = token_mark(fotg210, hw->hw_token); +- if (mark == '/') { /* qh_alt_next controls qh advance? */ +- if ((hw->hw_alt_next & QTD_MASK(fotg210)) == +- fotg210->async->hw->hw_alt_next) +- mark = '#'; /* blocked */ +- else if (hw->hw_alt_next == list_end) +- mark = '.'; /* use hw_qtd_next */ +- /* else alt_next points to some other qtd */ +- } +- scratch = hc32_to_cpup(fotg210, &hw->hw_info1); +- hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; +- temp = scnprintf(next, size, +- "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", +- qh, scratch & 0x007f, +- speed_char(scratch), +- (scratch >> 8) & 0x000f, +- scratch, hc32_to_cpup(fotg210, &hw->hw_info2), +- hc32_to_cpup(fotg210, &hw->hw_token), mark, +- (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) +- ? "data1" : "data0", +- (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); +- size -= temp; +- next += temp; +- +- /* hc may be modifying the list as we read it ... */ +- list_for_each_entry(td, &qh->qtd_list, qtd_list) { +- scratch = hc32_to_cpup(fotg210, &td->hw_token); +- mark = ' '; +- if (hw_curr == td->qtd_dma) +- mark = '*'; +- else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) +- mark = '+'; +- else if (QTD_LENGTH(scratch)) { +- if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) +- mark = '#'; +- else if (td->hw_alt_next != list_end) +- mark = '/'; +- } +- temp = snprintf(next, size, +- "\n\t%p%c%s len=%d %08x urb %p", +- td, mark, ({ char *tmp; +- switch ((scratch>>8)&0x03) { +- case 0: +- tmp = "out"; +- break; +- case 1: +- tmp = "in"; +- break; +- case 2: +- tmp = "setup"; +- break; +- default: +- tmp = "?"; +- break; +- } tmp; }), +- (scratch >> 16) & 0x7fff, +- scratch, +- td->urb); +- if (size < temp) +- temp = size; +- size -= temp; +- next += temp; +- if (temp == size) +- goto done; +- } +- +- temp = snprintf(next, size, "\n"); +- if (size < temp) +- temp = size; +- +- size -= temp; +- next += temp; +- +-done: +- *sizep = size; +- *nextp = next; +-} +- +-static ssize_t fill_async_buffer(struct debug_buffer *buf) +-{ +- struct usb_hcd *hcd; +- struct fotg210_hcd *fotg210; +- unsigned long flags; +- unsigned temp, size; +- char *next; +- struct fotg210_qh *qh; +- +- hcd = bus_to_hcd(buf->bus); +- fotg210 = hcd_to_fotg210(hcd); +- next = buf->output_buf; +- size = buf->alloc_size; +- +- *next = 0; +- +- /* dumps a snapshot of the async schedule. +- * usually empty except for long-term bulk reads, or head. +- * one QH per line, and TDs we know about +- */ +- spin_lock_irqsave(&fotg210->lock, flags); +- for (qh = fotg210->async->qh_next.qh; size > 0 && qh; +- qh = qh->qh_next.qh) +- qh_lines(fotg210, qh, &next, &size); +- if (fotg210->async_unlink && size > 0) { +- temp = scnprintf(next, size, "\nunlink =\n"); +- size -= temp; +- next += temp; +- +- for (qh = fotg210->async_unlink; size > 0 && qh; +- qh = qh->unlink_next) +- qh_lines(fotg210, qh, &next, &size); +- } +- spin_unlock_irqrestore(&fotg210->lock, flags); +- +- return strlen(buf->output_buf); +-} +- +-/* count tds, get ep direction */ +-static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, +- struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) +-{ +- u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); +- struct fotg210_qtd *qtd; +- char *type = ""; +- unsigned temp = 0; +- +- /* count tds, get ep direction */ +- list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { +- temp++; +- switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { +- case 0: +- type = "out"; +- continue; +- case 1: +- type = "in"; +- continue; +- } +- } +- +- return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", +- speed_char(scratch), scratch & 0x007f, +- (scratch >> 8) & 0x000f, type, qh->usecs, +- qh->c_usecs, temp, (scratch >> 16) & 0x7ff); +-} +- +-#define DBG_SCHED_LIMIT 64 +-static ssize_t fill_periodic_buffer(struct debug_buffer *buf) +-{ +- struct usb_hcd *hcd; +- struct fotg210_hcd *fotg210; +- unsigned long flags; +- union fotg210_shadow p, *seen; +- unsigned temp, size, seen_count; +- char *next; +- unsigned i; +- __hc32 tag; +- +- seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); +- if (!seen) +- return 0; +- +- seen_count = 0; +- +- hcd = bus_to_hcd(buf->bus); +- fotg210 = hcd_to_fotg210(hcd); +- next = buf->output_buf; +- size = buf->alloc_size; +- +- temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); +- size -= temp; +- next += temp; +- +- /* dump a snapshot of the periodic schedule. +- * iso changes, interrupt usually doesn't. +- */ +- spin_lock_irqsave(&fotg210->lock, flags); +- for (i = 0; i < fotg210->periodic_size; i++) { +- p = fotg210->pshadow[i]; +- if (likely(!p.ptr)) +- continue; +- +- tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); +- +- temp = scnprintf(next, size, "%4d: ", i); +- size -= temp; +- next += temp; +- +- do { +- struct fotg210_qh_hw *hw; +- +- switch (hc32_to_cpu(fotg210, tag)) { +- case Q_TYPE_QH: +- hw = p.qh->hw; +- temp = scnprintf(next, size, " qh%d-%04x/%p", +- p.qh->period, +- hc32_to_cpup(fotg210, +- &hw->hw_info2) +- /* uframe masks */ +- & (QH_CMASK | QH_SMASK), +- p.qh); +- size -= temp; +- next += temp; +- /* don't repeat what follows this qh */ +- for (temp = 0; temp < seen_count; temp++) { +- if (seen[temp].ptr != p.ptr) +- continue; +- if (p.qh->qh_next.ptr) { +- temp = scnprintf(next, size, +- " ..."); +- size -= temp; +- next += temp; +- } +- break; +- } +- /* show more info the first time around */ +- if (temp == seen_count) { +- temp = output_buf_tds_dir(next, +- fotg210, hw, +- p.qh, size); +- +- if (seen_count < DBG_SCHED_LIMIT) +- seen[seen_count++].qh = p.qh; +- } else +- temp = 0; +- tag = Q_NEXT_TYPE(fotg210, hw->hw_next); +- p = p.qh->qh_next; +- break; +- case Q_TYPE_FSTN: +- temp = scnprintf(next, size, +- " fstn-%8x/%p", +- p.fstn->hw_prev, p.fstn); +- tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); +- p = p.fstn->fstn_next; +- break; +- case Q_TYPE_ITD: +- temp = scnprintf(next, size, +- " itd/%p", p.itd); +- tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); +- p = p.itd->itd_next; +- break; +- } +- size -= temp; +- next += temp; +- } while (p.ptr); +- +- temp = scnprintf(next, size, "\n"); +- size -= temp; +- next += temp; +- } +- spin_unlock_irqrestore(&fotg210->lock, flags); +- kfree(seen); +- +- return buf->alloc_size - size; +-} +-#undef DBG_SCHED_LIMIT +- +-static const char *rh_state_string(struct fotg210_hcd *fotg210) +-{ +- switch (fotg210->rh_state) { +- case FOTG210_RH_HALTED: +- return "halted"; +- case FOTG210_RH_SUSPENDED: +- return "suspended"; +- case FOTG210_RH_RUNNING: +- return "running"; +- case FOTG210_RH_STOPPING: +- return "stopping"; +- } +- return "?"; +-} +- +-static ssize_t fill_registers_buffer(struct debug_buffer *buf) +-{ +- struct usb_hcd *hcd; +- struct fotg210_hcd *fotg210; +- unsigned long flags; +- unsigned temp, size, i; +- char *next, scratch[80]; +- static const char fmt[] = "%*s\n"; +- static const char label[] = ""; +- +- hcd = bus_to_hcd(buf->bus); +- fotg210 = hcd_to_fotg210(hcd); +- next = buf->output_buf; +- size = buf->alloc_size; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- +- if (!HCD_HW_ACCESSIBLE(hcd)) { +- size = scnprintf(next, size, +- "bus %s, device %s\n" +- "%s\n" +- "SUSPENDED(no register access)\n", +- hcd->self.controller->bus->name, +- dev_name(hcd->self.controller), +- hcd->product_desc); +- goto done; +- } +- +- /* Capability Registers */ +- i = HC_VERSION(fotg210, fotg210_readl(fotg210, +- &fotg210->caps->hc_capbase)); +- temp = scnprintf(next, size, +- "bus %s, device %s\n" +- "%s\n" +- "EHCI %x.%02x, rh state %s\n", +- hcd->self.controller->bus->name, +- dev_name(hcd->self.controller), +- hcd->product_desc, +- i >> 8, i & 0x0ff, rh_state_string(fotg210)); +- size -= temp; +- next += temp; +- +- /* FIXME interpret both types of params */ +- i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); +- temp = scnprintf(next, size, "structural params 0x%08x\n", i); +- size -= temp; +- next += temp; +- +- i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); +- temp = scnprintf(next, size, "capability params 0x%08x\n", i); +- size -= temp; +- next += temp; +- +- /* Operational Registers */ +- temp = dbg_status_buf(scratch, sizeof(scratch), label, +- fotg210_readl(fotg210, &fotg210->regs->status)); +- temp = scnprintf(next, size, fmt, temp, scratch); +- size -= temp; +- next += temp; +- +- temp = dbg_command_buf(scratch, sizeof(scratch), label, +- fotg210_readl(fotg210, &fotg210->regs->command)); +- temp = scnprintf(next, size, fmt, temp, scratch); +- size -= temp; +- next += temp; +- +- temp = dbg_intr_buf(scratch, sizeof(scratch), label, +- fotg210_readl(fotg210, &fotg210->regs->intr_enable)); +- temp = scnprintf(next, size, fmt, temp, scratch); +- size -= temp; +- next += temp; +- +- temp = scnprintf(next, size, "uframe %04x\n", +- fotg210_read_frame_index(fotg210)); +- size -= temp; +- next += temp; +- +- if (fotg210->async_unlink) { +- temp = scnprintf(next, size, "async unlink qh %p\n", +- fotg210->async_unlink); +- size -= temp; +- next += temp; +- } +- +-#ifdef FOTG210_STATS +- temp = scnprintf(next, size, +- "irq normal %ld err %ld iaa %ld(lost %ld)\n", +- fotg210->stats.normal, fotg210->stats.error, +- fotg210->stats.iaa, fotg210->stats.lost_iaa); +- size -= temp; +- next += temp; +- +- temp = scnprintf(next, size, "complete %ld unlink %ld\n", +- fotg210->stats.complete, fotg210->stats.unlink); +- size -= temp; +- next += temp; +-#endif +- +-done: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- +- return buf->alloc_size - size; +-} +- +-static struct debug_buffer +-*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) +-{ +- struct debug_buffer *buf; +- +- buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); +- +- if (buf) { +- buf->bus = bus; +- buf->fill_func = fill_func; +- mutex_init(&buf->mutex); +- buf->alloc_size = PAGE_SIZE; +- } +- +- return buf; +-} +- +-static int fill_buffer(struct debug_buffer *buf) +-{ +- int ret = 0; +- +- if (!buf->output_buf) +- buf->output_buf = vmalloc(buf->alloc_size); +- +- if (!buf->output_buf) { +- ret = -ENOMEM; +- goto out; +- } +- +- ret = buf->fill_func(buf); +- +- if (ret >= 0) { +- buf->count = ret; +- ret = 0; +- } +- +-out: +- return ret; +-} +- +-static ssize_t debug_output(struct file *file, char __user *user_buf, +- size_t len, loff_t *offset) +-{ +- struct debug_buffer *buf = file->private_data; +- int ret = 0; +- +- mutex_lock(&buf->mutex); +- if (buf->count == 0) { +- ret = fill_buffer(buf); +- if (ret != 0) { +- mutex_unlock(&buf->mutex); +- goto out; +- } +- } +- mutex_unlock(&buf->mutex); +- +- ret = simple_read_from_buffer(user_buf, len, offset, +- buf->output_buf, buf->count); +- +-out: +- return ret; +- +-} +- +-static int debug_close(struct inode *inode, struct file *file) +-{ +- struct debug_buffer *buf = file->private_data; +- +- if (buf) { +- vfree(buf->output_buf); +- kfree(buf); +- } +- +- return 0; +-} +-static int debug_async_open(struct inode *inode, struct file *file) +-{ +- file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); +- +- return file->private_data ? 0 : -ENOMEM; +-} +- +-static int debug_periodic_open(struct inode *inode, struct file *file) +-{ +- struct debug_buffer *buf; +- +- buf = alloc_buffer(inode->i_private, fill_periodic_buffer); +- if (!buf) +- return -ENOMEM; +- +- buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; +- file->private_data = buf; +- return 0; +-} +- +-static int debug_registers_open(struct inode *inode, struct file *file) +-{ +- file->private_data = alloc_buffer(inode->i_private, +- fill_registers_buffer); +- +- return file->private_data ? 0 : -ENOMEM; +-} +- +-static inline void create_debug_files(struct fotg210_hcd *fotg210) +-{ +- struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; +- struct dentry *root; +- +- root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); +- +- debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); +- debugfs_create_file("periodic", S_IRUGO, root, bus, +- &debug_periodic_fops); +- debugfs_create_file("registers", S_IRUGO, root, bus, +- &debug_registers_fops); +-} +- +-static inline void remove_debug_files(struct fotg210_hcd *fotg210) +-{ +- struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; +- +- debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root); +-} +- +-/* handshake - spin reading hc until handshake completes or fails +- * @ptr: address of hc register to be read +- * @mask: bits to look at in result of read +- * @done: value of those bits when handshake succeeds +- * @usec: timeout in microseconds +- * +- * Returns negative errno, or zero on success +- * +- * Success happens when the "mask" bits have the specified value (hardware +- * handshake done). There are two failure modes: "usec" have passed (major +- * hardware flakeout), or the register reads as all-ones (hardware removed). +- * +- * That last failure should_only happen in cases like physical cardbus eject +- * before driver shutdown. But it also seems to be caused by bugs in cardbus +- * bridge shutdown: shutting down the bridge before the devices using it. +- */ +-static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, +- u32 mask, u32 done, int usec) +-{ +- u32 result; +- int ret; +- +- ret = readl_poll_timeout_atomic(ptr, result, +- ((result & mask) == done || +- result == U32_MAX), 1, usec); +- if (result == U32_MAX) /* card removed */ +- return -ENODEV; +- +- return ret; +-} +- +-/* Force HC to halt state from unknown (EHCI spec section 2.3). +- * Must be called with interrupts enabled and the lock not held. +- */ +-static int fotg210_halt(struct fotg210_hcd *fotg210) +-{ +- u32 temp; +- +- spin_lock_irq(&fotg210->lock); +- +- /* disable any irqs left enabled by previous code */ +- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); +- +- /* +- * This routine gets called during probe before fotg210->command +- * has been initialized, so we can't rely on its value. +- */ +- fotg210->command &= ~CMD_RUN; +- temp = fotg210_readl(fotg210, &fotg210->regs->command); +- temp &= ~(CMD_RUN | CMD_IAAD); +- fotg210_writel(fotg210, temp, &fotg210->regs->command); +- +- spin_unlock_irq(&fotg210->lock); +- synchronize_irq(fotg210_to_hcd(fotg210)->irq); +- +- return handshake(fotg210, &fotg210->regs->status, +- STS_HALT, STS_HALT, 16 * 125); +-} +- +-/* Reset a non-running (STS_HALT == 1) controller. +- * Must be called with interrupts enabled and the lock not held. +- */ +-static int fotg210_reset(struct fotg210_hcd *fotg210) +-{ +- int retval; +- u32 command = fotg210_readl(fotg210, &fotg210->regs->command); +- +- /* If the EHCI debug controller is active, special care must be +- * taken before and after a host controller reset +- */ +- if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) +- fotg210->debug = NULL; +- +- command |= CMD_RESET; +- dbg_cmd(fotg210, "reset", command); +- fotg210_writel(fotg210, command, &fotg210->regs->command); +- fotg210->rh_state = FOTG210_RH_HALTED; +- fotg210->next_statechange = jiffies; +- retval = handshake(fotg210, &fotg210->regs->command, +- CMD_RESET, 0, 250 * 1000); +- +- if (retval) +- return retval; +- +- if (fotg210->debug) +- dbgp_external_startup(fotg210_to_hcd(fotg210)); +- +- fotg210->port_c_suspend = fotg210->suspended_ports = +- fotg210->resuming_ports = 0; +- return retval; +-} +- +-/* Idle the controller (turn off the schedules). +- * Must be called with interrupts enabled and the lock not held. +- */ +-static void fotg210_quiesce(struct fotg210_hcd *fotg210) +-{ +- u32 temp; +- +- if (fotg210->rh_state != FOTG210_RH_RUNNING) +- return; +- +- /* wait for any schedule enables/disables to take effect */ +- temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); +- handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, +- 16 * 125); +- +- /* then disable anything that's still active */ +- spin_lock_irq(&fotg210->lock); +- fotg210->command &= ~(CMD_ASE | CMD_PSE); +- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +- spin_unlock_irq(&fotg210->lock); +- +- /* hardware can take 16 microframes to turn off ... */ +- handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, +- 16 * 125); +-} +- +-static void end_unlink_async(struct fotg210_hcd *fotg210); +-static void unlink_empty_async(struct fotg210_hcd *fotg210); +-static void fotg210_work(struct fotg210_hcd *fotg210); +-static void start_unlink_intr(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh); +-static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); +- +-/* Set a bit in the USBCMD register */ +-static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) +-{ +- fotg210->command |= bit; +- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +- +- /* unblock posted write */ +- fotg210_readl(fotg210, &fotg210->regs->command); +-} +- +-/* Clear a bit in the USBCMD register */ +-static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) +-{ +- fotg210->command &= ~bit; +- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +- +- /* unblock posted write */ +- fotg210_readl(fotg210, &fotg210->regs->command); +-} +- +-/* EHCI timer support... Now using hrtimers. +- * +- * Lots of different events are triggered from fotg210->hrtimer. Whenever +- * the timer routine runs, it checks each possible event; events that are +- * currently enabled and whose expiration time has passed get handled. +- * The set of enabled events is stored as a collection of bitflags in +- * fotg210->enabled_hrtimer_events, and they are numbered in order of +- * increasing delay values (ranging between 1 ms and 100 ms). +- * +- * Rather than implementing a sorted list or tree of all pending events, +- * we keep track only of the lowest-numbered pending event, in +- * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its +- * expiration time is set to the timeout value for this event. +- * +- * As a result, events might not get handled right away; the actual delay +- * could be anywhere up to twice the requested delay. This doesn't +- * matter, because none of the events are especially time-critical. The +- * ones that matter most all have a delay of 1 ms, so they will be +- * handled after 2 ms at most, which is okay. In addition to this, we +- * allow for an expiration range of 1 ms. +- */ +- +-/* Delay lengths for the hrtimer event types. +- * Keep this list sorted by delay length, in the same order as +- * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. +- */ +-static unsigned event_delays_ns[] = { +- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ +- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ +- 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ +- 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ +- 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ +- 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ +- 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ +- 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ +- 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ +- 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ +-}; +- +-/* Enable a pending hrtimer event */ +-static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, +- bool resched) +-{ +- ktime_t *timeout = &fotg210->hr_timeouts[event]; +- +- if (resched) +- *timeout = ktime_add(ktime_get(), event_delays_ns[event]); +- fotg210->enabled_hrtimer_events |= (1 << event); +- +- /* Track only the lowest-numbered pending event */ +- if (event < fotg210->next_hrtimer_event) { +- fotg210->next_hrtimer_event = event; +- hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, +- NSEC_PER_MSEC, HRTIMER_MODE_ABS); +- } +-} +- +- +-/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ +-static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) +-{ +- unsigned actual, want; +- +- /* Don't enable anything if the controller isn't running (e.g., died) */ +- if (fotg210->rh_state != FOTG210_RH_RUNNING) +- return; +- +- want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; +- actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; +- +- if (want != actual) { +- +- /* Poll again later, but give up after about 20 ms */ +- if (fotg210->ASS_poll_count++ < 20) { +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, +- true); +- return; +- } +- fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", +- want, actual); +- } +- fotg210->ASS_poll_count = 0; +- +- /* The status is up-to-date; restart or stop the schedule as needed */ +- if (want == 0) { /* Stopped */ +- if (fotg210->async_count > 0) +- fotg210_set_command_bit(fotg210, CMD_ASE); +- +- } else { /* Running */ +- if (fotg210->async_count == 0) { +- +- /* Turn off the schedule after a while */ +- fotg210_enable_event(fotg210, +- FOTG210_HRTIMER_DISABLE_ASYNC, +- true); +- } +- } +-} +- +-/* Turn off the async schedule after a brief delay */ +-static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) +-{ +- fotg210_clear_command_bit(fotg210, CMD_ASE); +-} +- +- +-/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ +-static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) +-{ +- unsigned actual, want; +- +- /* Don't do anything if the controller isn't running (e.g., died) */ +- if (fotg210->rh_state != FOTG210_RH_RUNNING) +- return; +- +- want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; +- actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; +- +- if (want != actual) { +- +- /* Poll again later, but give up after about 20 ms */ +- if (fotg210->PSS_poll_count++ < 20) { +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, +- true); +- return; +- } +- fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", +- want, actual); +- } +- fotg210->PSS_poll_count = 0; +- +- /* The status is up-to-date; restart or stop the schedule as needed */ +- if (want == 0) { /* Stopped */ +- if (fotg210->periodic_count > 0) +- fotg210_set_command_bit(fotg210, CMD_PSE); +- +- } else { /* Running */ +- if (fotg210->periodic_count == 0) { +- +- /* Turn off the schedule after a while */ +- fotg210_enable_event(fotg210, +- FOTG210_HRTIMER_DISABLE_PERIODIC, +- true); +- } +- } +-} +- +-/* Turn off the periodic schedule after a brief delay */ +-static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) +-{ +- fotg210_clear_command_bit(fotg210, CMD_PSE); +-} +- +- +-/* Poll the STS_HALT status bit; see when a dead controller stops */ +-static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) +-{ +- if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { +- +- /* Give up after a few milliseconds */ +- if (fotg210->died_poll_count++ < 5) { +- /* Try again later */ +- fotg210_enable_event(fotg210, +- FOTG210_HRTIMER_POLL_DEAD, true); +- return; +- } +- fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); +- } +- +- /* Clean up the mess */ +- fotg210->rh_state = FOTG210_RH_HALTED; +- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); +- fotg210_work(fotg210); +- end_unlink_async(fotg210); +- +- /* Not in process context, so don't try to reset the controller */ +-} +- +- +-/* Handle unlinked interrupt QHs once they are gone from the hardware */ +-static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) +-{ +- bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); +- +- /* +- * Process all the QHs on the intr_unlink list that were added +- * before the current unlink cycle began. The list is in +- * temporal order, so stop when we reach the first entry in the +- * current cycle. But if the root hub isn't running then +- * process all the QHs on the list. +- */ +- fotg210->intr_unlinking = true; +- while (fotg210->intr_unlink) { +- struct fotg210_qh *qh = fotg210->intr_unlink; +- +- if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) +- break; +- fotg210->intr_unlink = qh->unlink_next; +- qh->unlink_next = NULL; +- end_unlink_intr(fotg210, qh); +- } +- +- /* Handle remaining entries later */ +- if (fotg210->intr_unlink) { +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, +- true); +- ++fotg210->intr_unlink_cycle; +- } +- fotg210->intr_unlinking = false; +-} +- +- +-/* Start another free-iTDs/siTDs cycle */ +-static void start_free_itds(struct fotg210_hcd *fotg210) +-{ +- if (!(fotg210->enabled_hrtimer_events & +- BIT(FOTG210_HRTIMER_FREE_ITDS))) { +- fotg210->last_itd_to_free = list_entry( +- fotg210->cached_itd_list.prev, +- struct fotg210_itd, itd_list); +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); +- } +-} +- +-/* Wait for controller to stop using old iTDs and siTDs */ +-static void end_free_itds(struct fotg210_hcd *fotg210) +-{ +- struct fotg210_itd *itd, *n; +- +- if (fotg210->rh_state < FOTG210_RH_RUNNING) +- fotg210->last_itd_to_free = NULL; +- +- list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { +- list_del(&itd->itd_list); +- dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); +- if (itd == fotg210->last_itd_to_free) +- break; +- } +- +- if (!list_empty(&fotg210->cached_itd_list)) +- start_free_itds(fotg210); +-} +- +- +-/* Handle lost (or very late) IAA interrupts */ +-static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) +-{ +- if (fotg210->rh_state != FOTG210_RH_RUNNING) +- return; +- +- /* +- * Lost IAA irqs wedge things badly; seen first with a vt8235. +- * So we need this watchdog, but must protect it against both +- * (a) SMP races against real IAA firing and retriggering, and +- * (b) clean HC shutdown, when IAA watchdog was pending. +- */ +- if (fotg210->async_iaa) { +- u32 cmd, status; +- +- /* If we get here, IAA is *REALLY* late. It's barely +- * conceivable that the system is so busy that CMD_IAAD +- * is still legitimately set, so let's be sure it's +- * clear before we read STS_IAA. (The HC should clear +- * CMD_IAAD when it sets STS_IAA.) +- */ +- cmd = fotg210_readl(fotg210, &fotg210->regs->command); +- +- /* +- * If IAA is set here it either legitimately triggered +- * after the watchdog timer expired (_way_ late, so we'll +- * still count it as lost) ... or a silicon erratum: +- * - VIA seems to set IAA without triggering the IRQ; +- * - IAAD potentially cleared without setting IAA. +- */ +- status = fotg210_readl(fotg210, &fotg210->regs->status); +- if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { +- INCR(fotg210->stats.lost_iaa); +- fotg210_writel(fotg210, STS_IAA, +- &fotg210->regs->status); +- } +- +- fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", +- status, cmd); +- end_unlink_async(fotg210); +- } +-} +- +- +-/* Enable the I/O watchdog, if appropriate */ +-static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) +-{ +- /* Not needed if the controller isn't running or it's already enabled */ +- if (fotg210->rh_state != FOTG210_RH_RUNNING || +- (fotg210->enabled_hrtimer_events & +- BIT(FOTG210_HRTIMER_IO_WATCHDOG))) +- return; +- +- /* +- * Isochronous transfers always need the watchdog. +- * For other sorts we use it only if the flag is set. +- */ +- if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && +- fotg210->async_count + fotg210->intr_count > 0)) +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, +- true); +-} +- +- +-/* Handler functions for the hrtimer event types. +- * Keep this array in the same order as the event types indexed by +- * enum fotg210_hrtimer_event in fotg210.h. +- */ +-static void (*event_handlers[])(struct fotg210_hcd *) = { +- fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ +- fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ +- fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ +- fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ +- end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ +- unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ +- fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ +- fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ +- fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ +- fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ +-}; +- +-static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) +-{ +- struct fotg210_hcd *fotg210 = +- container_of(t, struct fotg210_hcd, hrtimer); +- ktime_t now; +- unsigned long events; +- unsigned long flags; +- unsigned e; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- +- events = fotg210->enabled_hrtimer_events; +- fotg210->enabled_hrtimer_events = 0; +- fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; +- +- /* +- * Check each pending event. If its time has expired, handle +- * the event; otherwise re-enable it. +- */ +- now = ktime_get(); +- for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { +- if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0) +- event_handlers[e](fotg210); +- else +- fotg210_enable_event(fotg210, e, false); +- } +- +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return HRTIMER_NORESTART; +-} +- +-#define fotg210_bus_suspend NULL +-#define fotg210_bus_resume NULL +- +-static int check_reset_complete(struct fotg210_hcd *fotg210, int index, +- u32 __iomem *status_reg, int port_status) +-{ +- if (!(port_status & PORT_CONNECT)) +- return port_status; +- +- /* if reset finished and it's still not enabled -- handoff */ +- if (!(port_status & PORT_PE)) +- /* with integrated TT, there's nobody to hand it to! */ +- fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", +- index + 1); +- else +- fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", +- index + 1); +- +- return port_status; +-} +- +- +-/* build "status change" packet (one or two bytes) from HC registers */ +- +-static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- u32 temp, status; +- u32 mask; +- int retval = 1; +- unsigned long flags; +- +- /* init status to no-changes */ +- buf[0] = 0; +- +- /* Inform the core about resumes-in-progress by returning +- * a non-zero value even if there are no status changes. +- */ +- status = fotg210->resuming_ports; +- +- mask = PORT_CSC | PORT_PEC; +- /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ +- +- /* no hub change reports (bit 0) for now (power, ...) */ +- +- /* port N changes (bit N)? */ +- spin_lock_irqsave(&fotg210->lock, flags); +- +- temp = fotg210_readl(fotg210, &fotg210->regs->port_status); +- +- /* +- * Return status information even for ports with OWNER set. +- * Otherwise hub_wq wouldn't see the disconnect event when a +- * high-speed device is switched over to the companion +- * controller by the user. +- */ +- +- if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || +- (fotg210->reset_done[0] && +- time_after_eq(jiffies, fotg210->reset_done[0]))) { +- buf[0] |= 1 << 1; +- status = STS_PCD; +- } +- /* FIXME autosuspend idle root hubs */ +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return status ? retval : 0; +-} +- +-static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, +- struct usb_hub_descriptor *desc) +-{ +- int ports = HCS_N_PORTS(fotg210->hcs_params); +- u16 temp; +- +- desc->bDescriptorType = USB_DT_HUB; +- desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ +- desc->bHubContrCurrent = 0; +- +- desc->bNbrPorts = ports; +- temp = 1 + (ports / 8); +- desc->bDescLength = 7 + 2 * temp; +- +- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ +- memset(&desc->u.hs.DeviceRemovable[0], 0, temp); +- memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); +- +- temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ +- temp |= HUB_CHAR_NO_LPSM; /* no power switching */ +- desc->wHubCharacteristics = cpu_to_le16(temp); +-} +- +-static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, +- u16 wIndex, char *buf, u16 wLength) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- int ports = HCS_N_PORTS(fotg210->hcs_params); +- u32 __iomem *status_reg = &fotg210->regs->port_status; +- u32 temp, temp1, status; +- unsigned long flags; +- int retval = 0; +- unsigned selector; +- +- /* +- * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. +- * HCS_INDICATOR may say we can change LEDs to off/amber/green. +- * (track current state ourselves) ... blink for diagnostics, +- * power, "this is the one", etc. EHCI spec supports this. +- */ +- +- spin_lock_irqsave(&fotg210->lock, flags); +- switch (typeReq) { +- case ClearHubFeature: +- switch (wValue) { +- case C_HUB_LOCAL_POWER: +- case C_HUB_OVER_CURRENT: +- /* no hub-wide feature/status flags */ +- break; +- default: +- goto error; +- } +- break; +- case ClearPortFeature: +- if (!wIndex || wIndex > ports) +- goto error; +- wIndex--; +- temp = fotg210_readl(fotg210, status_reg); +- temp &= ~PORT_RWC_BITS; +- +- /* +- * Even if OWNER is set, so the port is owned by the +- * companion controller, hub_wq needs to be able to clear +- * the port-change status bits (especially +- * USB_PORT_STAT_C_CONNECTION). +- */ +- +- switch (wValue) { +- case USB_PORT_FEAT_ENABLE: +- fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); +- break; +- case USB_PORT_FEAT_C_ENABLE: +- fotg210_writel(fotg210, temp | PORT_PEC, status_reg); +- break; +- case USB_PORT_FEAT_SUSPEND: +- if (temp & PORT_RESET) +- goto error; +- if (!(temp & PORT_SUSPEND)) +- break; +- if ((temp & PORT_PE) == 0) +- goto error; +- +- /* resume signaling for 20 msec */ +- fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); +- fotg210->reset_done[wIndex] = jiffies +- + msecs_to_jiffies(USB_RESUME_TIMEOUT); +- break; +- case USB_PORT_FEAT_C_SUSPEND: +- clear_bit(wIndex, &fotg210->port_c_suspend); +- break; +- case USB_PORT_FEAT_C_CONNECTION: +- fotg210_writel(fotg210, temp | PORT_CSC, status_reg); +- break; +- case USB_PORT_FEAT_C_OVER_CURRENT: +- fotg210_writel(fotg210, temp | OTGISR_OVC, +- &fotg210->regs->otgisr); +- break; +- case USB_PORT_FEAT_C_RESET: +- /* GetPortStatus clears reset */ +- break; +- default: +- goto error; +- } +- fotg210_readl(fotg210, &fotg210->regs->command); +- break; +- case GetHubDescriptor: +- fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) +- buf); +- break; +- case GetHubStatus: +- /* no hub-wide feature/status flags */ +- memset(buf, 0, 4); +- /*cpu_to_le32s ((u32 *) buf); */ +- break; +- case GetPortStatus: +- if (!wIndex || wIndex > ports) +- goto error; +- wIndex--; +- status = 0; +- temp = fotg210_readl(fotg210, status_reg); +- +- /* wPortChange bits */ +- if (temp & PORT_CSC) +- status |= USB_PORT_STAT_C_CONNECTION << 16; +- if (temp & PORT_PEC) +- status |= USB_PORT_STAT_C_ENABLE << 16; +- +- temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); +- if (temp1 & OTGISR_OVC) +- status |= USB_PORT_STAT_C_OVERCURRENT << 16; +- +- /* whoever resumes must GetPortStatus to complete it!! */ +- if (temp & PORT_RESUME) { +- +- /* Remote Wakeup received? */ +- if (!fotg210->reset_done[wIndex]) { +- /* resume signaling for 20 msec */ +- fotg210->reset_done[wIndex] = jiffies +- + msecs_to_jiffies(20); +- /* check the port again */ +- mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, +- fotg210->reset_done[wIndex]); +- } +- +- /* resume completed? */ +- else if (time_after_eq(jiffies, +- fotg210->reset_done[wIndex])) { +- clear_bit(wIndex, &fotg210->suspended_ports); +- set_bit(wIndex, &fotg210->port_c_suspend); +- fotg210->reset_done[wIndex] = 0; +- +- /* stop resume signaling */ +- temp = fotg210_readl(fotg210, status_reg); +- fotg210_writel(fotg210, temp & +- ~(PORT_RWC_BITS | PORT_RESUME), +- status_reg); +- clear_bit(wIndex, &fotg210->resuming_ports); +- retval = handshake(fotg210, status_reg, +- PORT_RESUME, 0, 2000);/* 2ms */ +- if (retval != 0) { +- fotg210_err(fotg210, +- "port %d resume error %d\n", +- wIndex + 1, retval); +- goto error; +- } +- temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); +- } +- } +- +- /* whoever resets must GetPortStatus to complete it!! */ +- if ((temp & PORT_RESET) && time_after_eq(jiffies, +- fotg210->reset_done[wIndex])) { +- status |= USB_PORT_STAT_C_RESET << 16; +- fotg210->reset_done[wIndex] = 0; +- clear_bit(wIndex, &fotg210->resuming_ports); +- +- /* force reset to complete */ +- fotg210_writel(fotg210, +- temp & ~(PORT_RWC_BITS | PORT_RESET), +- status_reg); +- /* REVISIT: some hardware needs 550+ usec to clear +- * this bit; seems too long to spin routinely... +- */ +- retval = handshake(fotg210, status_reg, +- PORT_RESET, 0, 1000); +- if (retval != 0) { +- fotg210_err(fotg210, "port %d reset error %d\n", +- wIndex + 1, retval); +- goto error; +- } +- +- /* see what we found out */ +- temp = check_reset_complete(fotg210, wIndex, status_reg, +- fotg210_readl(fotg210, status_reg)); +- +- /* restart schedule */ +- fotg210->command |= CMD_RUN; +- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +- } +- +- if (!(temp & (PORT_RESUME|PORT_RESET))) { +- fotg210->reset_done[wIndex] = 0; +- clear_bit(wIndex, &fotg210->resuming_ports); +- } +- +- /* transfer dedicated ports to the companion hc */ +- if ((temp & PORT_CONNECT) && +- test_bit(wIndex, &fotg210->companion_ports)) { +- temp &= ~PORT_RWC_BITS; +- fotg210_writel(fotg210, temp, status_reg); +- fotg210_dbg(fotg210, "port %d --> companion\n", +- wIndex + 1); +- temp = fotg210_readl(fotg210, status_reg); +- } +- +- /* +- * Even if OWNER is set, there's no harm letting hub_wq +- * see the wPortStatus values (they should all be 0 except +- * for PORT_POWER anyway). +- */ +- +- if (temp & PORT_CONNECT) { +- status |= USB_PORT_STAT_CONNECTION; +- status |= fotg210_port_speed(fotg210, temp); +- } +- if (temp & PORT_PE) +- status |= USB_PORT_STAT_ENABLE; +- +- /* maybe the port was unsuspended without our knowledge */ +- if (temp & (PORT_SUSPEND|PORT_RESUME)) { +- status |= USB_PORT_STAT_SUSPEND; +- } else if (test_bit(wIndex, &fotg210->suspended_ports)) { +- clear_bit(wIndex, &fotg210->suspended_ports); +- clear_bit(wIndex, &fotg210->resuming_ports); +- fotg210->reset_done[wIndex] = 0; +- if (temp & PORT_PE) +- set_bit(wIndex, &fotg210->port_c_suspend); +- } +- +- temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); +- if (temp1 & OTGISR_OVC) +- status |= USB_PORT_STAT_OVERCURRENT; +- if (temp & PORT_RESET) +- status |= USB_PORT_STAT_RESET; +- if (test_bit(wIndex, &fotg210->port_c_suspend)) +- status |= USB_PORT_STAT_C_SUSPEND << 16; +- +- if (status & ~0xffff) /* only if wPortChange is interesting */ +- dbg_port(fotg210, "GetStatus", wIndex + 1, temp); +- put_unaligned_le32(status, buf); +- break; +- case SetHubFeature: +- switch (wValue) { +- case C_HUB_LOCAL_POWER: +- case C_HUB_OVER_CURRENT: +- /* no hub-wide feature/status flags */ +- break; +- default: +- goto error; +- } +- break; +- case SetPortFeature: +- selector = wIndex >> 8; +- wIndex &= 0xff; +- +- if (!wIndex || wIndex > ports) +- goto error; +- wIndex--; +- temp = fotg210_readl(fotg210, status_reg); +- temp &= ~PORT_RWC_BITS; +- switch (wValue) { +- case USB_PORT_FEAT_SUSPEND: +- if ((temp & PORT_PE) == 0 +- || (temp & PORT_RESET) != 0) +- goto error; +- +- /* After above check the port must be connected. +- * Set appropriate bit thus could put phy into low power +- * mode if we have hostpc feature +- */ +- fotg210_writel(fotg210, temp | PORT_SUSPEND, +- status_reg); +- set_bit(wIndex, &fotg210->suspended_ports); +- break; +- case USB_PORT_FEAT_RESET: +- if (temp & PORT_RESUME) +- goto error; +- /* line status bits may report this as low speed, +- * which can be fine if this root hub has a +- * transaction translator built in. +- */ +- fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); +- temp |= PORT_RESET; +- temp &= ~PORT_PE; +- +- /* +- * caller must wait, then call GetPortStatus +- * usb 2.0 spec says 50 ms resets on root +- */ +- fotg210->reset_done[wIndex] = jiffies +- + msecs_to_jiffies(50); +- fotg210_writel(fotg210, temp, status_reg); +- break; +- +- /* For downstream facing ports (these): one hub port is put +- * into test mode according to USB2 11.24.2.13, then the hub +- * must be reset (which for root hub now means rmmod+modprobe, +- * or else system reboot). See EHCI 2.3.9 and 4.14 for info +- * about the EHCI-specific stuff. +- */ +- case USB_PORT_FEAT_TEST: +- if (!selector || selector > 5) +- goto error; +- spin_unlock_irqrestore(&fotg210->lock, flags); +- fotg210_quiesce(fotg210); +- spin_lock_irqsave(&fotg210->lock, flags); +- +- /* Put all enabled ports into suspend */ +- temp = fotg210_readl(fotg210, status_reg) & +- ~PORT_RWC_BITS; +- if (temp & PORT_PE) +- fotg210_writel(fotg210, temp | PORT_SUSPEND, +- status_reg); +- +- spin_unlock_irqrestore(&fotg210->lock, flags); +- fotg210_halt(fotg210); +- spin_lock_irqsave(&fotg210->lock, flags); +- +- temp = fotg210_readl(fotg210, status_reg); +- temp |= selector << 16; +- fotg210_writel(fotg210, temp, status_reg); +- break; +- +- default: +- goto error; +- } +- fotg210_readl(fotg210, &fotg210->regs->command); +- break; +- +- default: +-error: +- /* "stall" on error */ +- retval = -EPIPE; +- } +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return retval; +-} +- +-static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, +- int portnum) +-{ +- return; +-} +- +-static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, +- int portnum) +-{ +- return 0; +-} +- +-/* There's basically three types of memory: +- * - data used only by the HCD ... kmalloc is fine +- * - async and periodic schedules, shared by HC and HCD ... these +- * need to use dma_pool or dma_alloc_coherent +- * - driver buffers, read/written by HC ... single shot DMA mapped +- * +- * There's also "register" data (e.g. PCI or SOC), which is memory mapped. +- * No memory seen by this driver is pageable. +- */ +- +-/* Allocate the key transfer structures from the previously allocated pool */ +-static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, +- struct fotg210_qtd *qtd, dma_addr_t dma) +-{ +- memset(qtd, 0, sizeof(*qtd)); +- qtd->qtd_dma = dma; +- qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); +- qtd->hw_next = FOTG210_LIST_END(fotg210); +- qtd->hw_alt_next = FOTG210_LIST_END(fotg210); +- INIT_LIST_HEAD(&qtd->qtd_list); +-} +- +-static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, +- gfp_t flags) +-{ +- struct fotg210_qtd *qtd; +- dma_addr_t dma; +- +- qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); +- if (qtd != NULL) +- fotg210_qtd_init(fotg210, qtd, dma); +- +- return qtd; +-} +- +-static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, +- struct fotg210_qtd *qtd) +-{ +- dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); +-} +- +- +-static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- /* clean qtds first, and know this is not linked */ +- if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { +- fotg210_dbg(fotg210, "unused qh not empty!\n"); +- BUG(); +- } +- if (qh->dummy) +- fotg210_qtd_free(fotg210, qh->dummy); +- dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); +- kfree(qh); +-} +- +-static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, +- gfp_t flags) +-{ +- struct fotg210_qh *qh; +- dma_addr_t dma; +- +- qh = kzalloc(sizeof(*qh), GFP_ATOMIC); +- if (!qh) +- goto done; +- qh->hw = (struct fotg210_qh_hw *) +- dma_pool_zalloc(fotg210->qh_pool, flags, &dma); +- if (!qh->hw) +- goto fail; +- qh->qh_dma = dma; +- INIT_LIST_HEAD(&qh->qtd_list); +- +- /* dummy td enables safe urb queuing */ +- qh->dummy = fotg210_qtd_alloc(fotg210, flags); +- if (qh->dummy == NULL) { +- fotg210_dbg(fotg210, "no dummy td\n"); +- goto fail1; +- } +-done: +- return qh; +-fail1: +- dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); +-fail: +- kfree(qh); +- return NULL; +-} +- +-/* The queue heads and transfer descriptors are managed from pools tied +- * to each of the "per device" structures. +- * This is the initialisation and cleanup code. +- */ +- +-static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) +-{ +- if (fotg210->async) +- qh_destroy(fotg210, fotg210->async); +- fotg210->async = NULL; +- +- if (fotg210->dummy) +- qh_destroy(fotg210, fotg210->dummy); +- fotg210->dummy = NULL; +- +- /* DMA consistent memory and pools */ +- dma_pool_destroy(fotg210->qtd_pool); +- fotg210->qtd_pool = NULL; +- +- dma_pool_destroy(fotg210->qh_pool); +- fotg210->qh_pool = NULL; +- +- dma_pool_destroy(fotg210->itd_pool); +- fotg210->itd_pool = NULL; +- +- if (fotg210->periodic) +- dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, +- fotg210->periodic_size * sizeof(u32), +- fotg210->periodic, fotg210->periodic_dma); +- fotg210->periodic = NULL; +- +- /* shadow periodic table */ +- kfree(fotg210->pshadow); +- fotg210->pshadow = NULL; +-} +- +-/* remember to add cleanup code (above) if you add anything here */ +-static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) +-{ +- int i; +- +- /* QTDs for control/bulk/intr transfers */ +- fotg210->qtd_pool = dma_pool_create("fotg210_qtd", +- fotg210_to_hcd(fotg210)->self.controller, +- sizeof(struct fotg210_qtd), +- 32 /* byte alignment (for hw parts) */, +- 4096 /* can't cross 4K */); +- if (!fotg210->qtd_pool) +- goto fail; +- +- /* QHs for control/bulk/intr transfers */ +- fotg210->qh_pool = dma_pool_create("fotg210_qh", +- fotg210_to_hcd(fotg210)->self.controller, +- sizeof(struct fotg210_qh_hw), +- 32 /* byte alignment (for hw parts) */, +- 4096 /* can't cross 4K */); +- if (!fotg210->qh_pool) +- goto fail; +- +- fotg210->async = fotg210_qh_alloc(fotg210, flags); +- if (!fotg210->async) +- goto fail; +- +- /* ITD for high speed ISO transfers */ +- fotg210->itd_pool = dma_pool_create("fotg210_itd", +- fotg210_to_hcd(fotg210)->self.controller, +- sizeof(struct fotg210_itd), +- 64 /* byte alignment (for hw parts) */, +- 4096 /* can't cross 4K */); +- if (!fotg210->itd_pool) +- goto fail; +- +- /* Hardware periodic table */ +- fotg210->periodic = +- dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, +- fotg210->periodic_size * sizeof(__le32), +- &fotg210->periodic_dma, 0); +- if (fotg210->periodic == NULL) +- goto fail; +- +- for (i = 0; i < fotg210->periodic_size; i++) +- fotg210->periodic[i] = FOTG210_LIST_END(fotg210); +- +- /* software shadow of hardware table */ +- fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), +- flags); +- if (fotg210->pshadow != NULL) +- return 0; +- +-fail: +- fotg210_dbg(fotg210, "couldn't init memory\n"); +- fotg210_mem_cleanup(fotg210); +- return -ENOMEM; +-} +-/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. +- * +- * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" +- * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned +- * buffers needed for the larger number). We use one QH per endpoint, queue +- * multiple urbs (all three types) per endpoint. URBs may need several qtds. +- * +- * ISO traffic uses "ISO TD" (itd) records, and (along with +- * interrupts) needs careful scheduling. Performance improvements can be +- * an ongoing challenge. That's in "ehci-sched.c". +- * +- * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, +- * or otherwise through transaction translators (TTs) in USB 2.0 hubs using +- * (b) special fields in qh entries or (c) split iso entries. TTs will +- * buffer low/full speed data so the host collects it at high speed. +- */ +- +-/* fill a qtd, returning how much of the buffer we were able to queue up */ +-static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, +- dma_addr_t buf, size_t len, int token, int maxpacket) +-{ +- int i, count; +- u64 addr = buf; +- +- /* one buffer entry per 4K ... first might be short or unaligned */ +- qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); +- qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); +- count = 0x1000 - (buf & 0x0fff); /* rest of that page */ +- if (likely(len < count)) /* ... iff needed */ +- count = len; +- else { +- buf += 0x1000; +- buf &= ~0x0fff; +- +- /* per-qtd limit: from 16K to 20K (best alignment) */ +- for (i = 1; count < len && i < 5; i++) { +- addr = buf; +- qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); +- qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, +- (u32)(addr >> 32)); +- buf += 0x1000; +- if ((count + 0x1000) < len) +- count += 0x1000; +- else +- count = len; +- } +- +- /* short packets may only terminate transfers */ +- if (count != len) +- count -= (count % maxpacket); +- } +- qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); +- qtd->length = count; +- +- return count; +-} +- +-static inline void qh_update(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh, struct fotg210_qtd *qtd) +-{ +- struct fotg210_qh_hw *hw = qh->hw; +- +- /* writes to an active overlay are unsafe */ +- BUG_ON(qh->qh_state != QH_STATE_IDLE); +- +- hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); +- hw->hw_alt_next = FOTG210_LIST_END(fotg210); +- +- /* Except for control endpoints, we make hardware maintain data +- * toggle (like OHCI) ... here (re)initialize the toggle in the QH, +- * and set the pseudo-toggle in udev. Only usb_clear_halt() will +- * ever clear it. +- */ +- if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { +- unsigned is_out, epnum; +- +- is_out = qh->is_out; +- epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; +- if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { +- hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); +- usb_settoggle(qh->dev, epnum, is_out, 1); +- } +- } +- +- hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); +-} +- +-/* if it weren't for a common silicon quirk (writing the dummy into the qh +- * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault +- * recovery (including urb dequeue) would need software changes to a QH... +- */ +-static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- struct fotg210_qtd *qtd; +- +- if (list_empty(&qh->qtd_list)) +- qtd = qh->dummy; +- else { +- qtd = list_entry(qh->qtd_list.next, +- struct fotg210_qtd, qtd_list); +- /* +- * first qtd may already be partially processed. +- * If we come here during unlink, the QH overlay region +- * might have reference to the just unlinked qtd. The +- * qtd is updated in qh_completions(). Update the QH +- * overlay here. +- */ +- if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { +- qh->hw->hw_qtd_next = qtd->hw_next; +- qtd = NULL; +- } +- } +- +- if (qtd) +- qh_update(fotg210, qh, qtd); +-} +- +-static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); +- +-static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, +- struct usb_host_endpoint *ep) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- struct fotg210_qh *qh = ep->hcpriv; +- unsigned long flags; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- qh->clearing_tt = 0; +- if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) +- && fotg210->rh_state == FOTG210_RH_RUNNING) +- qh_link_async(fotg210, qh); +- spin_unlock_irqrestore(&fotg210->lock, flags); +-} +- +-static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh, struct urb *urb, u32 token) +-{ +- +- /* If an async split transaction gets an error or is unlinked, +- * the TT buffer may be left in an indeterminate state. We +- * have to clear the TT buffer. +- * +- * Note: this routine is never called for Isochronous transfers. +- */ +- if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +- struct usb_device *tt = urb->dev->tt->hub; +- +- dev_dbg(&tt->dev, +- "clear tt buffer port %d, a%d ep%d t%08x\n", +- urb->dev->ttport, urb->dev->devnum, +- usb_pipeendpoint(urb->pipe), token); +- +- if (urb->dev->tt->hub != +- fotg210_to_hcd(fotg210)->self.root_hub) { +- if (usb_hub_clear_tt_buffer(urb) == 0) +- qh->clearing_tt = 1; +- } +- } +-} +- +-static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, +- size_t length, u32 token) +-{ +- int status = -EINPROGRESS; +- +- /* count IN/OUT bytes, not SETUP (even short packets) */ +- if (likely(QTD_PID(token) != 2)) +- urb->actual_length += length - QTD_LENGTH(token); +- +- /* don't modify error codes */ +- if (unlikely(urb->unlinked)) +- return status; +- +- /* force cleanup after short read; not always an error */ +- if (unlikely(IS_SHORT_READ(token))) +- status = -EREMOTEIO; +- +- /* serious "can't proceed" faults reported by the hardware */ +- if (token & QTD_STS_HALT) { +- if (token & QTD_STS_BABBLE) { +- /* FIXME "must" disable babbling device's port too */ +- status = -EOVERFLOW; +- /* CERR nonzero + halt --> stall */ +- } else if (QTD_CERR(token)) { +- status = -EPIPE; +- +- /* In theory, more than one of the following bits can be set +- * since they are sticky and the transaction is retried. +- * Which to test first is rather arbitrary. +- */ +- } else if (token & QTD_STS_MMF) { +- /* fs/ls interrupt xfer missed the complete-split */ +- status = -EPROTO; +- } else if (token & QTD_STS_DBE) { +- status = (QTD_PID(token) == 1) /* IN ? */ +- ? -ENOSR /* hc couldn't read data */ +- : -ECOMM; /* hc couldn't write data */ +- } else if (token & QTD_STS_XACT) { +- /* timeout, bad CRC, wrong PID, etc */ +- fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", +- urb->dev->devpath, +- usb_pipeendpoint(urb->pipe), +- usb_pipein(urb->pipe) ? "in" : "out"); +- status = -EPROTO; +- } else { /* unknown */ +- status = -EPROTO; +- } +- +- fotg210_dbg(fotg210, +- "dev%d ep%d%s qtd token %08x --> status %d\n", +- usb_pipedevice(urb->pipe), +- usb_pipeendpoint(urb->pipe), +- usb_pipein(urb->pipe) ? "in" : "out", +- token, status); +- } +- +- return status; +-} +- +-static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, +- int status) +-__releases(fotg210->lock) +-__acquires(fotg210->lock) +-{ +- if (likely(urb->hcpriv != NULL)) { +- struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; +- +- /* S-mask in a QH means it's an interrupt urb */ +- if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { +- +- /* ... update hc-wide periodic stats (for usbfs) */ +- fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; +- } +- } +- +- if (unlikely(urb->unlinked)) { +- INCR(fotg210->stats.unlink); +- } else { +- /* report non-error and short read status as zero */ +- if (status == -EINPROGRESS || status == -EREMOTEIO) +- status = 0; +- INCR(fotg210->stats.complete); +- } +- +-#ifdef FOTG210_URB_TRACE +- fotg210_dbg(fotg210, +- "%s %s urb %p ep%d%s status %d len %d/%d\n", +- __func__, urb->dev->devpath, urb, +- usb_pipeendpoint(urb->pipe), +- usb_pipein(urb->pipe) ? "in" : "out", +- status, +- urb->actual_length, urb->transfer_buffer_length); +-#endif +- +- /* complete() can reenter this HCD */ +- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +- spin_unlock(&fotg210->lock); +- usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); +- spin_lock(&fotg210->lock); +-} +- +-static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); +- +-/* Process and free completed qtds for a qh, returning URBs to drivers. +- * Chases up to qh->hw_current. Returns number of completions called, +- * indicating how much "real" work we did. +- */ +-static unsigned qh_completions(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh) +-{ +- struct fotg210_qtd *last, *end = qh->dummy; +- struct fotg210_qtd *qtd, *tmp; +- int last_status; +- int stopped; +- unsigned count = 0; +- u8 state; +- struct fotg210_qh_hw *hw = qh->hw; +- +- if (unlikely(list_empty(&qh->qtd_list))) +- return count; +- +- /* completions (or tasks on other cpus) must never clobber HALT +- * till we've gone through and cleaned everything up, even when +- * they add urbs to this qh's queue or mark them for unlinking. +- * +- * NOTE: unlinking expects to be done in queue order. +- * +- * It's a bug for qh->qh_state to be anything other than +- * QH_STATE_IDLE, unless our caller is scan_async() or +- * scan_intr(). +- */ +- state = qh->qh_state; +- qh->qh_state = QH_STATE_COMPLETING; +- stopped = (state == QH_STATE_IDLE); +- +-rescan: +- last = NULL; +- last_status = -EINPROGRESS; +- qh->needs_rescan = 0; +- +- /* remove de-activated QTDs from front of queue. +- * after faults (including short reads), cleanup this urb +- * then let the queue advance. +- * if queue is stopped, handles unlinks. +- */ +- list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { +- struct urb *urb; +- u32 token = 0; +- +- urb = qtd->urb; +- +- /* clean up any state from previous QTD ...*/ +- if (last) { +- if (likely(last->urb != urb)) { +- fotg210_urb_done(fotg210, last->urb, +- last_status); +- count++; +- last_status = -EINPROGRESS; +- } +- fotg210_qtd_free(fotg210, last); +- last = NULL; +- } +- +- /* ignore urbs submitted during completions we reported */ +- if (qtd == end) +- break; +- +- /* hardware copies qtd out of qh overlay */ +- rmb(); +- token = hc32_to_cpu(fotg210, qtd->hw_token); +- +- /* always clean up qtds the hc de-activated */ +-retry_xacterr: +- if ((token & QTD_STS_ACTIVE) == 0) { +- +- /* Report Data Buffer Error: non-fatal but useful */ +- if (token & QTD_STS_DBE) +- fotg210_dbg(fotg210, +- "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", +- urb, usb_endpoint_num(&urb->ep->desc), +- usb_endpoint_dir_in(&urb->ep->desc) +- ? "in" : "out", +- urb->transfer_buffer_length, qtd, qh); +- +- /* on STALL, error, and short reads this urb must +- * complete and all its qtds must be recycled. +- */ +- if ((token & QTD_STS_HALT) != 0) { +- +- /* retry transaction errors until we +- * reach the software xacterr limit +- */ +- if ((token & QTD_STS_XACT) && +- QTD_CERR(token) == 0 && +- ++qh->xacterrs < QH_XACTERR_MAX && +- !urb->unlinked) { +- fotg210_dbg(fotg210, +- "detected XactErr len %zu/%zu retry %d\n", +- qtd->length - QTD_LENGTH(token), +- qtd->length, +- qh->xacterrs); +- +- /* reset the token in the qtd and the +- * qh overlay (which still contains +- * the qtd) so that we pick up from +- * where we left off +- */ +- token &= ~QTD_STS_HALT; +- token |= QTD_STS_ACTIVE | +- (FOTG210_TUNE_CERR << 10); +- qtd->hw_token = cpu_to_hc32(fotg210, +- token); +- wmb(); +- hw->hw_token = cpu_to_hc32(fotg210, +- token); +- goto retry_xacterr; +- } +- stopped = 1; +- +- /* magic dummy for some short reads; qh won't advance. +- * that silicon quirk can kick in with this dummy too. +- * +- * other short reads won't stop the queue, including +- * control transfers (status stage handles that) or +- * most other single-qtd reads ... the queue stops if +- * URB_SHORT_NOT_OK was set so the driver submitting +- * the urbs could clean it up. +- */ +- } else if (IS_SHORT_READ(token) && +- !(qtd->hw_alt_next & +- FOTG210_LIST_END(fotg210))) { +- stopped = 1; +- } +- +- /* stop scanning when we reach qtds the hc is using */ +- } else if (likely(!stopped +- && fotg210->rh_state >= FOTG210_RH_RUNNING)) { +- break; +- +- /* scan the whole queue for unlinks whenever it stops */ +- } else { +- stopped = 1; +- +- /* cancel everything if we halt, suspend, etc */ +- if (fotg210->rh_state < FOTG210_RH_RUNNING) +- last_status = -ESHUTDOWN; +- +- /* this qtd is active; skip it unless a previous qtd +- * for its urb faulted, or its urb was canceled. +- */ +- else if (last_status == -EINPROGRESS && !urb->unlinked) +- continue; +- +- /* qh unlinked; token in overlay may be most current */ +- if (state == QH_STATE_IDLE && +- cpu_to_hc32(fotg210, qtd->qtd_dma) +- == hw->hw_current) { +- token = hc32_to_cpu(fotg210, hw->hw_token); +- +- /* An unlink may leave an incomplete +- * async transaction in the TT buffer. +- * We have to clear it. +- */ +- fotg210_clear_tt_buffer(fotg210, qh, urb, +- token); +- } +- } +- +- /* unless we already know the urb's status, collect qtd status +- * and update count of bytes transferred. in common short read +- * cases with only one data qtd (including control transfers), +- * queue processing won't halt. but with two or more qtds (for +- * example, with a 32 KB transfer), when the first qtd gets a +- * short read the second must be removed by hand. +- */ +- if (last_status == -EINPROGRESS) { +- last_status = qtd_copy_status(fotg210, urb, +- qtd->length, token); +- if (last_status == -EREMOTEIO && +- (qtd->hw_alt_next & +- FOTG210_LIST_END(fotg210))) +- last_status = -EINPROGRESS; +- +- /* As part of low/full-speed endpoint-halt processing +- * we must clear the TT buffer (11.17.5). +- */ +- if (unlikely(last_status != -EINPROGRESS && +- last_status != -EREMOTEIO)) { +- /* The TT's in some hubs malfunction when they +- * receive this request following a STALL (they +- * stop sending isochronous packets). Since a +- * STALL can't leave the TT buffer in a busy +- * state (if you believe Figures 11-48 - 11-51 +- * in the USB 2.0 spec), we won't clear the TT +- * buffer in this case. Strictly speaking this +- * is a violation of the spec. +- */ +- if (last_status != -EPIPE) +- fotg210_clear_tt_buffer(fotg210, qh, +- urb, token); +- } +- } +- +- /* if we're removing something not at the queue head, +- * patch the hardware queue pointer. +- */ +- if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { +- last = list_entry(qtd->qtd_list.prev, +- struct fotg210_qtd, qtd_list); +- last->hw_next = qtd->hw_next; +- } +- +- /* remove qtd; it's recycled after possible urb completion */ +- list_del(&qtd->qtd_list); +- last = qtd; +- +- /* reinit the xacterr counter for the next qtd */ +- qh->xacterrs = 0; +- } +- +- /* last urb's completion might still need calling */ +- if (likely(last != NULL)) { +- fotg210_urb_done(fotg210, last->urb, last_status); +- count++; +- fotg210_qtd_free(fotg210, last); +- } +- +- /* Do we need to rescan for URBs dequeued during a giveback? */ +- if (unlikely(qh->needs_rescan)) { +- /* If the QH is already unlinked, do the rescan now. */ +- if (state == QH_STATE_IDLE) +- goto rescan; +- +- /* Otherwise we have to wait until the QH is fully unlinked. +- * Our caller will start an unlink if qh->needs_rescan is +- * set. But if an unlink has already started, nothing needs +- * to be done. +- */ +- if (state != QH_STATE_LINKED) +- qh->needs_rescan = 0; +- } +- +- /* restore original state; caller must unlink or relink */ +- qh->qh_state = state; +- +- /* be sure the hardware's done with the qh before refreshing +- * it after fault cleanup, or recovering from silicon wrongly +- * overlaying the dummy qtd (which reduces DMA chatter). +- */ +- if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { +- switch (state) { +- case QH_STATE_IDLE: +- qh_refresh(fotg210, qh); +- break; +- case QH_STATE_LINKED: +- /* We won't refresh a QH that's linked (after the HC +- * stopped the queue). That avoids a race: +- * - HC reads first part of QH; +- * - CPU updates that first part and the token; +- * - HC reads rest of that QH, including token +- * Result: HC gets an inconsistent image, and then +- * DMAs to/from the wrong memory (corrupting it). +- * +- * That should be rare for interrupt transfers, +- * except maybe high bandwidth ... +- */ +- +- /* Tell the caller to start an unlink */ +- qh->needs_rescan = 1; +- break; +- /* otherwise, unlink already started */ +- } +- } +- +- return count; +-} +- +-/* reverse of qh_urb_transaction: free a list of TDs. +- * used for cleanup after errors, before HC sees an URB's TDs. +- */ +-static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, +- struct list_head *head) +-{ +- struct fotg210_qtd *qtd, *temp; +- +- list_for_each_entry_safe(qtd, temp, head, qtd_list) { +- list_del(&qtd->qtd_list); +- fotg210_qtd_free(fotg210, qtd); +- } +-} +- +-/* create a list of filled qtds for this URB; won't link into qh. +- */ +-static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, +- struct urb *urb, struct list_head *head, gfp_t flags) +-{ +- struct fotg210_qtd *qtd, *qtd_prev; +- dma_addr_t buf; +- int len, this_sg_len, maxpacket; +- int is_input; +- u32 token; +- int i; +- struct scatterlist *sg; +- +- /* +- * URBs map to sequences of QTDs: one logical transaction +- */ +- qtd = fotg210_qtd_alloc(fotg210, flags); +- if (unlikely(!qtd)) +- return NULL; +- list_add_tail(&qtd->qtd_list, head); +- qtd->urb = urb; +- +- token = QTD_STS_ACTIVE; +- token |= (FOTG210_TUNE_CERR << 10); +- /* for split transactions, SplitXState initialized to zero */ +- +- len = urb->transfer_buffer_length; +- is_input = usb_pipein(urb->pipe); +- if (usb_pipecontrol(urb->pipe)) { +- /* SETUP pid */ +- qtd_fill(fotg210, qtd, urb->setup_dma, +- sizeof(struct usb_ctrlrequest), +- token | (2 /* "setup" */ << 8), 8); +- +- /* ... and always at least one more pid */ +- token ^= QTD_TOGGLE; +- qtd_prev = qtd; +- qtd = fotg210_qtd_alloc(fotg210, flags); +- if (unlikely(!qtd)) +- goto cleanup; +- qtd->urb = urb; +- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); +- list_add_tail(&qtd->qtd_list, head); +- +- /* for zero length DATA stages, STATUS is always IN */ +- if (len == 0) +- token |= (1 /* "in" */ << 8); +- } +- +- /* +- * data transfer stage: buffer setup +- */ +- i = urb->num_mapped_sgs; +- if (len > 0 && i > 0) { +- sg = urb->sg; +- buf = sg_dma_address(sg); +- +- /* urb->transfer_buffer_length may be smaller than the +- * size of the scatterlist (or vice versa) +- */ +- this_sg_len = min_t(int, sg_dma_len(sg), len); +- } else { +- sg = NULL; +- buf = urb->transfer_dma; +- this_sg_len = len; +- } +- +- if (is_input) +- token |= (1 /* "in" */ << 8); +- /* else it's already initted to "out" pid (0 << 8) */ +- +- maxpacket = usb_maxpacket(urb->dev, urb->pipe); +- +- /* +- * buffer gets wrapped in one or more qtds; +- * last one may be "short" (including zero len) +- * and may serve as a control status ack +- */ +- for (;;) { +- int this_qtd_len; +- +- this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, +- maxpacket); +- this_sg_len -= this_qtd_len; +- len -= this_qtd_len; +- buf += this_qtd_len; +- +- /* +- * short reads advance to a "magic" dummy instead of the next +- * qtd ... that forces the queue to stop, for manual cleanup. +- * (this will usually be overridden later.) +- */ +- if (is_input) +- qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; +- +- /* qh makes control packets use qtd toggle; maybe switch it */ +- if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) +- token ^= QTD_TOGGLE; +- +- if (likely(this_sg_len <= 0)) { +- if (--i <= 0 || len <= 0) +- break; +- sg = sg_next(sg); +- buf = sg_dma_address(sg); +- this_sg_len = min_t(int, sg_dma_len(sg), len); +- } +- +- qtd_prev = qtd; +- qtd = fotg210_qtd_alloc(fotg210, flags); +- if (unlikely(!qtd)) +- goto cleanup; +- qtd->urb = urb; +- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); +- list_add_tail(&qtd->qtd_list, head); +- } +- +- /* +- * unless the caller requires manual cleanup after short reads, +- * have the alt_next mechanism keep the queue running after the +- * last data qtd (the only one, for control and most other cases). +- */ +- if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || +- usb_pipecontrol(urb->pipe))) +- qtd->hw_alt_next = FOTG210_LIST_END(fotg210); +- +- /* +- * control requests may need a terminating data "status" ack; +- * other OUT ones may need a terminating short packet +- * (zero length). +- */ +- if (likely(urb->transfer_buffer_length != 0)) { +- int one_more = 0; +- +- if (usb_pipecontrol(urb->pipe)) { +- one_more = 1; +- token ^= 0x0100; /* "in" <--> "out" */ +- token |= QTD_TOGGLE; /* force DATA1 */ +- } else if (usb_pipeout(urb->pipe) +- && (urb->transfer_flags & URB_ZERO_PACKET) +- && !(urb->transfer_buffer_length % maxpacket)) { +- one_more = 1; +- } +- if (one_more) { +- qtd_prev = qtd; +- qtd = fotg210_qtd_alloc(fotg210, flags); +- if (unlikely(!qtd)) +- goto cleanup; +- qtd->urb = urb; +- qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); +- list_add_tail(&qtd->qtd_list, head); +- +- /* never any data in such packets */ +- qtd_fill(fotg210, qtd, 0, 0, token, 0); +- } +- } +- +- /* by default, enable interrupt on urb completion */ +- if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) +- qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); +- return head; +- +-cleanup: +- qtd_list_free(fotg210, urb, head); +- return NULL; +-} +- +-/* Would be best to create all qh's from config descriptors, +- * when each interface/altsetting is established. Unlink +- * any previous qh and cancel its urbs first; endpoints are +- * implicitly reset then (data toggle too). +- * That'd mean updating how usbcore talks to HCDs. (2.7?) +- */ +- +- +-/* Each QH holds a qtd list; a QH is used for everything except iso. +- * +- * For interrupt urbs, the scheduler must set the microframe scheduling +- * mask(s) each time the QH gets scheduled. For highspeed, that's +- * just one microframe in the s-mask. For split interrupt transactions +- * there are additional complications: c-mask, maybe FSTNs. +- */ +-static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, +- gfp_t flags) +-{ +- struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); +- struct usb_host_endpoint *ep; +- u32 info1 = 0, info2 = 0; +- int is_input, type; +- int maxp = 0; +- int mult; +- struct usb_tt *tt = urb->dev->tt; +- struct fotg210_qh_hw *hw; +- +- if (!qh) +- return qh; +- +- /* +- * init endpoint/device data for this QH +- */ +- info1 |= usb_pipeendpoint(urb->pipe) << 8; +- info1 |= usb_pipedevice(urb->pipe) << 0; +- +- is_input = usb_pipein(urb->pipe); +- type = usb_pipetype(urb->pipe); +- ep = usb_pipe_endpoint(urb->dev, urb->pipe); +- maxp = usb_endpoint_maxp(&ep->desc); +- mult = usb_endpoint_maxp_mult(&ep->desc); +- +- /* 1024 byte maxpacket is a hardware ceiling. High bandwidth +- * acts like up to 3KB, but is built from smaller packets. +- */ +- if (maxp > 1024) { +- fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); +- goto done; +- } +- +- /* Compute interrupt scheduling parameters just once, and save. +- * - allowing for high bandwidth, how many nsec/uframe are used? +- * - split transactions need a second CSPLIT uframe; same question +- * - splits also need a schedule gap (for full/low speed I/O) +- * - qh has a polling interval +- * +- * For control/bulk requests, the HC or TT handles these. +- */ +- if (type == PIPE_INTERRUPT) { +- qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, +- is_input, 0, mult * maxp)); +- qh->start = NO_FRAME; +- +- if (urb->dev->speed == USB_SPEED_HIGH) { +- qh->c_usecs = 0; +- qh->gap_uf = 0; +- +- qh->period = urb->interval >> 3; +- if (qh->period == 0 && urb->interval != 1) { +- /* NOTE interval 2 or 4 uframes could work. +- * But interval 1 scheduling is simpler, and +- * includes high bandwidth. +- */ +- urb->interval = 1; +- } else if (qh->period > fotg210->periodic_size) { +- qh->period = fotg210->periodic_size; +- urb->interval = qh->period << 3; +- } +- } else { +- int think_time; +- +- /* gap is f(FS/LS transfer times) */ +- qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, +- is_input, 0, maxp) / (125 * 1000); +- +- /* FIXME this just approximates SPLIT/CSPLIT times */ +- if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ +- qh->c_usecs = qh->usecs + HS_USECS(0); +- qh->usecs = HS_USECS(1); +- } else { /* SPLIT+DATA, gap, CSPLIT */ +- qh->usecs += HS_USECS(1); +- qh->c_usecs = HS_USECS(0); +- } +- +- think_time = tt ? tt->think_time : 0; +- qh->tt_usecs = NS_TO_US(think_time + +- usb_calc_bus_time(urb->dev->speed, +- is_input, 0, maxp)); +- qh->period = urb->interval; +- if (qh->period > fotg210->periodic_size) { +- qh->period = fotg210->periodic_size; +- urb->interval = qh->period; +- } +- } +- } +- +- /* support for tt scheduling, and access to toggles */ +- qh->dev = urb->dev; +- +- /* using TT? */ +- switch (urb->dev->speed) { +- case USB_SPEED_LOW: +- info1 |= QH_LOW_SPEED; +- fallthrough; +- +- case USB_SPEED_FULL: +- /* EPS 0 means "full" */ +- if (type != PIPE_INTERRUPT) +- info1 |= (FOTG210_TUNE_RL_TT << 28); +- if (type == PIPE_CONTROL) { +- info1 |= QH_CONTROL_EP; /* for TT */ +- info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ +- } +- info1 |= maxp << 16; +- +- info2 |= (FOTG210_TUNE_MULT_TT << 30); +- +- /* Some Freescale processors have an erratum in which the +- * port number in the queue head was 0..N-1 instead of 1..N. +- */ +- if (fotg210_has_fsl_portno_bug(fotg210)) +- info2 |= (urb->dev->ttport-1) << 23; +- else +- info2 |= urb->dev->ttport << 23; +- +- /* set the address of the TT; for TDI's integrated +- * root hub tt, leave it zeroed. +- */ +- if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) +- info2 |= tt->hub->devnum << 16; +- +- /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ +- +- break; +- +- case USB_SPEED_HIGH: /* no TT involved */ +- info1 |= QH_HIGH_SPEED; +- if (type == PIPE_CONTROL) { +- info1 |= (FOTG210_TUNE_RL_HS << 28); +- info1 |= 64 << 16; /* usb2 fixed maxpacket */ +- info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ +- info2 |= (FOTG210_TUNE_MULT_HS << 30); +- } else if (type == PIPE_BULK) { +- info1 |= (FOTG210_TUNE_RL_HS << 28); +- /* The USB spec says that high speed bulk endpoints +- * always use 512 byte maxpacket. But some device +- * vendors decided to ignore that, and MSFT is happy +- * to help them do so. So now people expect to use +- * such nonconformant devices with Linux too; sigh. +- */ +- info1 |= maxp << 16; +- info2 |= (FOTG210_TUNE_MULT_HS << 30); +- } else { /* PIPE_INTERRUPT */ +- info1 |= maxp << 16; +- info2 |= mult << 30; +- } +- break; +- default: +- fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, +- urb->dev->speed); +-done: +- qh_destroy(fotg210, qh); +- return NULL; +- } +- +- /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ +- +- /* init as live, toggle clear, advance to dummy */ +- qh->qh_state = QH_STATE_IDLE; +- hw = qh->hw; +- hw->hw_info1 = cpu_to_hc32(fotg210, info1); +- hw->hw_info2 = cpu_to_hc32(fotg210, info2); +- qh->is_out = !is_input; +- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); +- qh_refresh(fotg210, qh); +- return qh; +-} +- +-static void enable_async(struct fotg210_hcd *fotg210) +-{ +- if (fotg210->async_count++) +- return; +- +- /* Stop waiting to turn off the async schedule */ +- fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); +- +- /* Don't start the schedule until ASS is 0 */ +- fotg210_poll_ASS(fotg210); +- turn_on_io_watchdog(fotg210); +-} +- +-static void disable_async(struct fotg210_hcd *fotg210) +-{ +- if (--fotg210->async_count) +- return; +- +- /* The async schedule and async_unlink list are supposed to be empty */ +- WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); +- +- /* Don't turn off the schedule until ASS is 1 */ +- fotg210_poll_ASS(fotg210); +-} +- +-/* move qh (and its qtds) onto async queue; maybe enable queue. */ +- +-static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); +- struct fotg210_qh *head; +- +- /* Don't link a QH if there's a Clear-TT-Buffer pending */ +- if (unlikely(qh->clearing_tt)) +- return; +- +- WARN_ON(qh->qh_state != QH_STATE_IDLE); +- +- /* clear halt and/or toggle; and maybe recover from silicon quirk */ +- qh_refresh(fotg210, qh); +- +- /* splice right after start */ +- head = fotg210->async; +- qh->qh_next = head->qh_next; +- qh->hw->hw_next = head->hw->hw_next; +- wmb(); +- +- head->qh_next.qh = qh; +- head->hw->hw_next = dma; +- +- qh->xacterrs = 0; +- qh->qh_state = QH_STATE_LINKED; +- /* qtd completions reported later by interrupt */ +- +- enable_async(fotg210); +-} +- +-/* For control/bulk/interrupt, return QH with these TDs appended. +- * Allocates and initializes the QH if necessary. +- * Returns null if it can't allocate a QH it needs to. +- * If the QH has TDs (urbs) already, that's great. +- */ +-static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, +- struct urb *urb, struct list_head *qtd_list, +- int epnum, void **ptr) +-{ +- struct fotg210_qh *qh = NULL; +- __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); +- +- qh = (struct fotg210_qh *) *ptr; +- if (unlikely(qh == NULL)) { +- /* can't sleep here, we have fotg210->lock... */ +- qh = qh_make(fotg210, urb, GFP_ATOMIC); +- *ptr = qh; +- } +- if (likely(qh != NULL)) { +- struct fotg210_qtd *qtd; +- +- if (unlikely(list_empty(qtd_list))) +- qtd = NULL; +- else +- qtd = list_entry(qtd_list->next, struct fotg210_qtd, +- qtd_list); +- +- /* control qh may need patching ... */ +- if (unlikely(epnum == 0)) { +- /* usb_reset_device() briefly reverts to address 0 */ +- if (usb_pipedevice(urb->pipe) == 0) +- qh->hw->hw_info1 &= ~qh_addr_mask; +- } +- +- /* just one way to queue requests: swap with the dummy qtd. +- * only hc or qh_refresh() ever modify the overlay. +- */ +- if (likely(qtd != NULL)) { +- struct fotg210_qtd *dummy; +- dma_addr_t dma; +- __hc32 token; +- +- /* to avoid racing the HC, use the dummy td instead of +- * the first td of our list (becomes new dummy). both +- * tds stay deactivated until we're done, when the +- * HC is allowed to fetch the old dummy (4.10.2). +- */ +- token = qtd->hw_token; +- qtd->hw_token = HALT_BIT(fotg210); +- +- dummy = qh->dummy; +- +- dma = dummy->qtd_dma; +- *dummy = *qtd; +- dummy->qtd_dma = dma; +- +- list_del(&qtd->qtd_list); +- list_add(&dummy->qtd_list, qtd_list); +- list_splice_tail(qtd_list, &qh->qtd_list); +- +- fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); +- qh->dummy = qtd; +- +- /* hc must see the new dummy at list end */ +- dma = qtd->qtd_dma; +- qtd = list_entry(qh->qtd_list.prev, +- struct fotg210_qtd, qtd_list); +- qtd->hw_next = QTD_NEXT(fotg210, dma); +- +- /* let the hc process these next qtds */ +- wmb(); +- dummy->hw_token = token; +- +- urb->hcpriv = qh; +- } +- } +- return qh; +-} +- +-static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, +- struct list_head *qtd_list, gfp_t mem_flags) +-{ +- int epnum; +- unsigned long flags; +- struct fotg210_qh *qh = NULL; +- int rc; +- +- epnum = urb->ep->desc.bEndpointAddress; +- +-#ifdef FOTG210_URB_TRACE +- { +- struct fotg210_qtd *qtd; +- +- qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); +- fotg210_dbg(fotg210, +- "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", +- __func__, urb->dev->devpath, urb, +- epnum & 0x0f, (epnum & USB_DIR_IN) +- ? "in" : "out", +- urb->transfer_buffer_length, +- qtd, urb->ep->hcpriv); +- } +-#endif +- +- spin_lock_irqsave(&fotg210->lock, flags); +- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { +- rc = -ESHUTDOWN; +- goto done; +- } +- rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); +- if (unlikely(rc)) +- goto done; +- +- qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); +- if (unlikely(qh == NULL)) { +- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +- rc = -ENOMEM; +- goto done; +- } +- +- /* Control/bulk operations through TTs don't need scheduling, +- * the HC and TT handle it when the TT has a buffer ready. +- */ +- if (likely(qh->qh_state == QH_STATE_IDLE)) +- qh_link_async(fotg210, qh); +-done: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- if (unlikely(qh == NULL)) +- qtd_list_free(fotg210, urb, qtd_list); +- return rc; +-} +- +-static void single_unlink_async(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh) +-{ +- struct fotg210_qh *prev; +- +- /* Add to the end of the list of QHs waiting for the next IAAD */ +- qh->qh_state = QH_STATE_UNLINK; +- if (fotg210->async_unlink) +- fotg210->async_unlink_last->unlink_next = qh; +- else +- fotg210->async_unlink = qh; +- fotg210->async_unlink_last = qh; +- +- /* Unlink it from the schedule */ +- prev = fotg210->async; +- while (prev->qh_next.qh != qh) +- prev = prev->qh_next.qh; +- +- prev->hw->hw_next = qh->hw->hw_next; +- prev->qh_next = qh->qh_next; +- if (fotg210->qh_scan_next == qh) +- fotg210->qh_scan_next = qh->qh_next.qh; +-} +- +-static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) +-{ +- /* +- * Do nothing if an IAA cycle is already running or +- * if one will be started shortly. +- */ +- if (fotg210->async_iaa || fotg210->async_unlinking) +- return; +- +- /* Do all the waiting QHs at once */ +- fotg210->async_iaa = fotg210->async_unlink; +- fotg210->async_unlink = NULL; +- +- /* If the controller isn't running, we don't have to wait for it */ +- if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { +- if (!nested) /* Avoid recursion */ +- end_unlink_async(fotg210); +- +- /* Otherwise start a new IAA cycle */ +- } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { +- /* Make sure the unlinks are all visible to the hardware */ +- wmb(); +- +- fotg210_writel(fotg210, fotg210->command | CMD_IAAD, +- &fotg210->regs->command); +- fotg210_readl(fotg210, &fotg210->regs->command); +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, +- true); +- } +-} +- +-/* the async qh for the qtds being unlinked are now gone from the HC */ +- +-static void end_unlink_async(struct fotg210_hcd *fotg210) +-{ +- struct fotg210_qh *qh; +- +- /* Process the idle QHs */ +-restart: +- fotg210->async_unlinking = true; +- while (fotg210->async_iaa) { +- qh = fotg210->async_iaa; +- fotg210->async_iaa = qh->unlink_next; +- qh->unlink_next = NULL; +- +- qh->qh_state = QH_STATE_IDLE; +- qh->qh_next.qh = NULL; +- +- qh_completions(fotg210, qh); +- if (!list_empty(&qh->qtd_list) && +- fotg210->rh_state == FOTG210_RH_RUNNING) +- qh_link_async(fotg210, qh); +- disable_async(fotg210); +- } +- fotg210->async_unlinking = false; +- +- /* Start a new IAA cycle if any QHs are waiting for it */ +- if (fotg210->async_unlink) { +- start_iaa_cycle(fotg210, true); +- if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) +- goto restart; +- } +-} +- +-static void unlink_empty_async(struct fotg210_hcd *fotg210) +-{ +- struct fotg210_qh *qh, *next; +- bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); +- bool check_unlinks_later = false; +- +- /* Unlink all the async QHs that have been empty for a timer cycle */ +- next = fotg210->async->qh_next.qh; +- while (next) { +- qh = next; +- next = qh->qh_next.qh; +- +- if (list_empty(&qh->qtd_list) && +- qh->qh_state == QH_STATE_LINKED) { +- if (!stopped && qh->unlink_cycle == +- fotg210->async_unlink_cycle) +- check_unlinks_later = true; +- else +- single_unlink_async(fotg210, qh); +- } +- } +- +- /* Start a new IAA cycle if any QHs are waiting for it */ +- if (fotg210->async_unlink) +- start_iaa_cycle(fotg210, false); +- +- /* QHs that haven't been empty for long enough will be handled later */ +- if (check_unlinks_later) { +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, +- true); +- ++fotg210->async_unlink_cycle; +- } +-} +- +-/* makes sure the async qh will become idle */ +-/* caller must own fotg210->lock */ +- +-static void start_unlink_async(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh) +-{ +- /* +- * If the QH isn't linked then there's nothing we can do +- * unless we were called during a giveback, in which case +- * qh_completions() has to deal with it. +- */ +- if (qh->qh_state != QH_STATE_LINKED) { +- if (qh->qh_state == QH_STATE_COMPLETING) +- qh->needs_rescan = 1; +- return; +- } +- +- single_unlink_async(fotg210, qh); +- start_iaa_cycle(fotg210, false); +-} +- +-static void scan_async(struct fotg210_hcd *fotg210) +-{ +- struct fotg210_qh *qh; +- bool check_unlinks_later = false; +- +- fotg210->qh_scan_next = fotg210->async->qh_next.qh; +- while (fotg210->qh_scan_next) { +- qh = fotg210->qh_scan_next; +- fotg210->qh_scan_next = qh->qh_next.qh; +-rescan: +- /* clean any finished work for this qh */ +- if (!list_empty(&qh->qtd_list)) { +- int temp; +- +- /* +- * Unlinks could happen here; completion reporting +- * drops the lock. That's why fotg210->qh_scan_next +- * always holds the next qh to scan; if the next qh +- * gets unlinked then fotg210->qh_scan_next is adjusted +- * in single_unlink_async(). +- */ +- temp = qh_completions(fotg210, qh); +- if (qh->needs_rescan) { +- start_unlink_async(fotg210, qh); +- } else if (list_empty(&qh->qtd_list) +- && qh->qh_state == QH_STATE_LINKED) { +- qh->unlink_cycle = fotg210->async_unlink_cycle; +- check_unlinks_later = true; +- } else if (temp != 0) +- goto rescan; +- } +- } +- +- /* +- * Unlink empty entries, reducing DMA usage as well +- * as HCD schedule-scanning costs. Delay for any qh +- * we just scanned, there's a not-unusual case that it +- * doesn't stay idle for long. +- */ +- if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && +- !(fotg210->enabled_hrtimer_events & +- BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { +- fotg210_enable_event(fotg210, +- FOTG210_HRTIMER_ASYNC_UNLINKS, true); +- ++fotg210->async_unlink_cycle; +- } +-} +-/* EHCI scheduled transaction support: interrupt, iso, split iso +- * These are called "periodic" transactions in the EHCI spec. +- * +- * Note that for interrupt transfers, the QH/QTD manipulation is shared +- * with the "asynchronous" transaction support (control/bulk transfers). +- * The only real difference is in how interrupt transfers are scheduled. +- * +- * For ISO, we make an "iso_stream" head to serve the same role as a QH. +- * It keeps track of every ITD (or SITD) that's linked, and holds enough +- * pre-calculated schedule data to make appending to the queue be quick. +- */ +-static int fotg210_get_frame(struct usb_hcd *hcd); +- +-/* periodic_next_shadow - return "next" pointer on shadow list +- * @periodic: host pointer to qh/itd +- * @tag: hardware tag for type of this record +- */ +-static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, +- union fotg210_shadow *periodic, __hc32 tag) +-{ +- switch (hc32_to_cpu(fotg210, tag)) { +- case Q_TYPE_QH: +- return &periodic->qh->qh_next; +- case Q_TYPE_FSTN: +- return &periodic->fstn->fstn_next; +- default: +- return &periodic->itd->itd_next; +- } +-} +- +-static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, +- union fotg210_shadow *periodic, __hc32 tag) +-{ +- switch (hc32_to_cpu(fotg210, tag)) { +- /* our fotg210_shadow.qh is actually software part */ +- case Q_TYPE_QH: +- return &periodic->qh->hw->hw_next; +- /* others are hw parts */ +- default: +- return periodic->hw_next; +- } +-} +- +-/* caller must hold fotg210->lock */ +-static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, +- void *ptr) +-{ +- union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; +- __hc32 *hw_p = &fotg210->periodic[frame]; +- union fotg210_shadow here = *prev_p; +- +- /* find predecessor of "ptr"; hw and shadow lists are in sync */ +- while (here.ptr && here.ptr != ptr) { +- prev_p = periodic_next_shadow(fotg210, prev_p, +- Q_NEXT_TYPE(fotg210, *hw_p)); +- hw_p = shadow_next_periodic(fotg210, &here, +- Q_NEXT_TYPE(fotg210, *hw_p)); +- here = *prev_p; +- } +- /* an interrupt entry (at list end) could have been shared */ +- if (!here.ptr) +- return; +- +- /* update shadow and hardware lists ... the old "next" pointers +- * from ptr may still be in use, the caller updates them. +- */ +- *prev_p = *periodic_next_shadow(fotg210, &here, +- Q_NEXT_TYPE(fotg210, *hw_p)); +- +- *hw_p = *shadow_next_periodic(fotg210, &here, +- Q_NEXT_TYPE(fotg210, *hw_p)); +-} +- +-/* how many of the uframe's 125 usecs are allocated? */ +-static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, +- unsigned frame, unsigned uframe) +-{ +- __hc32 *hw_p = &fotg210->periodic[frame]; +- union fotg210_shadow *q = &fotg210->pshadow[frame]; +- unsigned usecs = 0; +- struct fotg210_qh_hw *hw; +- +- while (q->ptr) { +- switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { +- case Q_TYPE_QH: +- hw = q->qh->hw; +- /* is it in the S-mask? */ +- if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) +- usecs += q->qh->usecs; +- /* ... or C-mask? */ +- if (hw->hw_info2 & cpu_to_hc32(fotg210, +- 1 << (8 + uframe))) +- usecs += q->qh->c_usecs; +- hw_p = &hw->hw_next; +- q = &q->qh->qh_next; +- break; +- /* case Q_TYPE_FSTN: */ +- default: +- /* for "save place" FSTNs, count the relevant INTR +- * bandwidth from the previous frame +- */ +- if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) +- fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); +- +- hw_p = &q->fstn->hw_next; +- q = &q->fstn->fstn_next; +- break; +- case Q_TYPE_ITD: +- if (q->itd->hw_transaction[uframe]) +- usecs += q->itd->stream->usecs; +- hw_p = &q->itd->hw_next; +- q = &q->itd->itd_next; +- break; +- } +- } +- if (usecs > fotg210->uframe_periodic_max) +- fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", +- frame * 8 + uframe, usecs); +- return usecs; +-} +- +-static int same_tt(struct usb_device *dev1, struct usb_device *dev2) +-{ +- if (!dev1->tt || !dev2->tt) +- return 0; +- if (dev1->tt != dev2->tt) +- return 0; +- if (dev1->tt->multi) +- return dev1->ttport == dev2->ttport; +- else +- return 1; +-} +- +-/* return true iff the device's transaction translator is available +- * for a periodic transfer starting at the specified frame, using +- * all the uframes in the mask. +- */ +-static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, +- struct usb_device *dev, unsigned frame, u32 uf_mask) +-{ +- if (period == 0) /* error */ +- return 0; +- +- /* note bandwidth wastage: split never follows csplit +- * (different dev or endpoint) until the next uframe. +- * calling convention doesn't make that distinction. +- */ +- for (; frame < fotg210->periodic_size; frame += period) { +- union fotg210_shadow here; +- __hc32 type; +- struct fotg210_qh_hw *hw; +- +- here = fotg210->pshadow[frame]; +- type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); +- while (here.ptr) { +- switch (hc32_to_cpu(fotg210, type)) { +- case Q_TYPE_ITD: +- type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); +- here = here.itd->itd_next; +- continue; +- case Q_TYPE_QH: +- hw = here.qh->hw; +- if (same_tt(dev, here.qh->dev)) { +- u32 mask; +- +- mask = hc32_to_cpu(fotg210, +- hw->hw_info2); +- /* "knows" no gap is needed */ +- mask |= mask >> 8; +- if (mask & uf_mask) +- break; +- } +- type = Q_NEXT_TYPE(fotg210, hw->hw_next); +- here = here.qh->qh_next; +- continue; +- /* case Q_TYPE_FSTN: */ +- default: +- fotg210_dbg(fotg210, +- "periodic frame %d bogus type %d\n", +- frame, type); +- } +- +- /* collision or error */ +- return 0; +- } +- } +- +- /* no collision */ +- return 1; +-} +- +-static void enable_periodic(struct fotg210_hcd *fotg210) +-{ +- if (fotg210->periodic_count++) +- return; +- +- /* Stop waiting to turn off the periodic schedule */ +- fotg210->enabled_hrtimer_events &= +- ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); +- +- /* Don't start the schedule until PSS is 0 */ +- fotg210_poll_PSS(fotg210); +- turn_on_io_watchdog(fotg210); +-} +- +-static void disable_periodic(struct fotg210_hcd *fotg210) +-{ +- if (--fotg210->periodic_count) +- return; +- +- /* Don't turn off the schedule until PSS is 1 */ +- fotg210_poll_PSS(fotg210); +-} +- +-/* periodic schedule slots have iso tds (normal or split) first, then a +- * sparse tree for active interrupt transfers. +- * +- * this just links in a qh; caller guarantees uframe masks are set right. +- * no FSTN support (yet; fotg210 0.96+) +- */ +-static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- unsigned i; +- unsigned period = qh->period; +- +- dev_dbg(&qh->dev->dev, +- "link qh%d-%04x/%p start %d [%d/%d us]\n", period, +- hc32_to_cpup(fotg210, &qh->hw->hw_info2) & +- (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, +- qh->c_usecs); +- +- /* high bandwidth, or otherwise every microframe */ +- if (period == 0) +- period = 1; +- +- for (i = qh->start; i < fotg210->periodic_size; i += period) { +- union fotg210_shadow *prev = &fotg210->pshadow[i]; +- __hc32 *hw_p = &fotg210->periodic[i]; +- union fotg210_shadow here = *prev; +- __hc32 type = 0; +- +- /* skip the iso nodes at list head */ +- while (here.ptr) { +- type = Q_NEXT_TYPE(fotg210, *hw_p); +- if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) +- break; +- prev = periodic_next_shadow(fotg210, prev, type); +- hw_p = shadow_next_periodic(fotg210, &here, type); +- here = *prev; +- } +- +- /* sorting each branch by period (slow-->fast) +- * enables sharing interior tree nodes +- */ +- while (here.ptr && qh != here.qh) { +- if (qh->period > here.qh->period) +- break; +- prev = &here.qh->qh_next; +- hw_p = &here.qh->hw->hw_next; +- here = *prev; +- } +- /* link in this qh, unless some earlier pass did that */ +- if (qh != here.qh) { +- qh->qh_next = here; +- if (here.qh) +- qh->hw->hw_next = *hw_p; +- wmb(); +- prev->qh = qh; +- *hw_p = QH_NEXT(fotg210, qh->qh_dma); +- } +- } +- qh->qh_state = QH_STATE_LINKED; +- qh->xacterrs = 0; +- +- /* update per-qh bandwidth for usbfs */ +- fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period +- ? ((qh->usecs + qh->c_usecs) / qh->period) +- : (qh->usecs * 8); +- +- list_add(&qh->intr_node, &fotg210->intr_qh_list); +- +- /* maybe enable periodic schedule processing */ +- ++fotg210->intr_count; +- enable_periodic(fotg210); +-} +- +-static void qh_unlink_periodic(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh) +-{ +- unsigned i; +- unsigned period; +- +- /* +- * If qh is for a low/full-speed device, simply unlinking it +- * could interfere with an ongoing split transaction. To unlink +- * it safely would require setting the QH_INACTIVATE bit and +- * waiting at least one frame, as described in EHCI 4.12.2.5. +- * +- * We won't bother with any of this. Instead, we assume that the +- * only reason for unlinking an interrupt QH while the current URB +- * is still active is to dequeue all the URBs (flush the whole +- * endpoint queue). +- * +- * If rebalancing the periodic schedule is ever implemented, this +- * approach will no longer be valid. +- */ +- +- /* high bandwidth, or otherwise part of every microframe */ +- period = qh->period; +- if (!period) +- period = 1; +- +- for (i = qh->start; i < fotg210->periodic_size; i += period) +- periodic_unlink(fotg210, i, qh); +- +- /* update per-qh bandwidth for usbfs */ +- fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period +- ? ((qh->usecs + qh->c_usecs) / qh->period) +- : (qh->usecs * 8); +- +- dev_dbg(&qh->dev->dev, +- "unlink qh%d-%04x/%p start %d [%d/%d us]\n", +- qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & +- (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, +- qh->c_usecs); +- +- /* qh->qh_next still "live" to HC */ +- qh->qh_state = QH_STATE_UNLINK; +- qh->qh_next.ptr = NULL; +- +- if (fotg210->qh_scan_next == qh) +- fotg210->qh_scan_next = list_entry(qh->intr_node.next, +- struct fotg210_qh, intr_node); +- list_del(&qh->intr_node); +-} +- +-static void start_unlink_intr(struct fotg210_hcd *fotg210, +- struct fotg210_qh *qh) +-{ +- /* If the QH isn't linked then there's nothing we can do +- * unless we were called during a giveback, in which case +- * qh_completions() has to deal with it. +- */ +- if (qh->qh_state != QH_STATE_LINKED) { +- if (qh->qh_state == QH_STATE_COMPLETING) +- qh->needs_rescan = 1; +- return; +- } +- +- qh_unlink_periodic(fotg210, qh); +- +- /* Make sure the unlinks are visible before starting the timer */ +- wmb(); +- +- /* +- * The EHCI spec doesn't say how long it takes the controller to +- * stop accessing an unlinked interrupt QH. The timer delay is +- * 9 uframes; presumably that will be long enough. +- */ +- qh->unlink_cycle = fotg210->intr_unlink_cycle; +- +- /* New entries go at the end of the intr_unlink list */ +- if (fotg210->intr_unlink) +- fotg210->intr_unlink_last->unlink_next = qh; +- else +- fotg210->intr_unlink = qh; +- fotg210->intr_unlink_last = qh; +- +- if (fotg210->intr_unlinking) +- ; /* Avoid recursive calls */ +- else if (fotg210->rh_state < FOTG210_RH_RUNNING) +- fotg210_handle_intr_unlinks(fotg210); +- else if (fotg210->intr_unlink == qh) { +- fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, +- true); +- ++fotg210->intr_unlink_cycle; +- } +-} +- +-static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- struct fotg210_qh_hw *hw = qh->hw; +- int rc; +- +- qh->qh_state = QH_STATE_IDLE; +- hw->hw_next = FOTG210_LIST_END(fotg210); +- +- qh_completions(fotg210, qh); +- +- /* reschedule QH iff another request is queued */ +- if (!list_empty(&qh->qtd_list) && +- fotg210->rh_state == FOTG210_RH_RUNNING) { +- rc = qh_schedule(fotg210, qh); +- +- /* An error here likely indicates handshake failure +- * or no space left in the schedule. Neither fault +- * should happen often ... +- * +- * FIXME kill the now-dysfunctional queued urbs +- */ +- if (rc != 0) +- fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", +- qh, rc); +- } +- +- /* maybe turn off periodic schedule */ +- --fotg210->intr_count; +- disable_periodic(fotg210); +-} +- +-static int check_period(struct fotg210_hcd *fotg210, unsigned frame, +- unsigned uframe, unsigned period, unsigned usecs) +-{ +- int claimed; +- +- /* complete split running into next frame? +- * given FSTN support, we could sometimes check... +- */ +- if (uframe >= 8) +- return 0; +- +- /* convert "usecs we need" to "max already claimed" */ +- usecs = fotg210->uframe_periodic_max - usecs; +- +- /* we "know" 2 and 4 uframe intervals were rejected; so +- * for period 0, check _every_ microframe in the schedule. +- */ +- if (unlikely(period == 0)) { +- do { +- for (uframe = 0; uframe < 7; uframe++) { +- claimed = periodic_usecs(fotg210, frame, +- uframe); +- if (claimed > usecs) +- return 0; +- } +- } while ((frame += 1) < fotg210->periodic_size); +- +- /* just check the specified uframe, at that period */ +- } else { +- do { +- claimed = periodic_usecs(fotg210, frame, uframe); +- if (claimed > usecs) +- return 0; +- } while ((frame += period) < fotg210->periodic_size); +- } +- +- /* success! */ +- return 1; +-} +- +-static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, +- unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) +-{ +- int retval = -ENOSPC; +- u8 mask = 0; +- +- if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ +- goto done; +- +- if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) +- goto done; +- if (!qh->c_usecs) { +- retval = 0; +- *c_maskp = 0; +- goto done; +- } +- +- /* Make sure this tt's buffer is also available for CSPLITs. +- * We pessimize a bit; probably the typical full speed case +- * doesn't need the second CSPLIT. +- * +- * NOTE: both SPLIT and CSPLIT could be checked in just +- * one smart pass... +- */ +- mask = 0x03 << (uframe + qh->gap_uf); +- *c_maskp = cpu_to_hc32(fotg210, mask << 8); +- +- mask |= 1 << uframe; +- if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { +- if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, +- qh->period, qh->c_usecs)) +- goto done; +- if (!check_period(fotg210, frame, uframe + qh->gap_uf, +- qh->period, qh->c_usecs)) +- goto done; +- retval = 0; +- } +-done: +- return retval; +-} +- +-/* "first fit" scheduling policy used the first time through, +- * or when the previous schedule slot can't be re-used. +- */ +-static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +-{ +- int status; +- unsigned uframe; +- __hc32 c_mask; +- unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ +- struct fotg210_qh_hw *hw = qh->hw; +- +- qh_refresh(fotg210, qh); +- hw->hw_next = FOTG210_LIST_END(fotg210); +- frame = qh->start; +- +- /* reuse the previous schedule slots, if we can */ +- if (frame < qh->period) { +- uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); +- status = check_intr_schedule(fotg210, frame, --uframe, +- qh, &c_mask); +- } else { +- uframe = 0; +- c_mask = 0; +- status = -ENOSPC; +- } +- +- /* else scan the schedule to find a group of slots such that all +- * uframes have enough periodic bandwidth available. +- */ +- if (status) { +- /* "normal" case, uframing flexible except with splits */ +- if (qh->period) { +- int i; +- +- for (i = qh->period; status && i > 0; --i) { +- frame = ++fotg210->random_frame % qh->period; +- for (uframe = 0; uframe < 8; uframe++) { +- status = check_intr_schedule(fotg210, +- frame, uframe, qh, +- &c_mask); +- if (status == 0) +- break; +- } +- } +- +- /* qh->period == 0 means every uframe */ +- } else { +- frame = 0; +- status = check_intr_schedule(fotg210, 0, 0, qh, +- &c_mask); +- } +- if (status) +- goto done; +- qh->start = frame; +- +- /* reset S-frame and (maybe) C-frame masks */ +- hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); +- hw->hw_info2 |= qh->period +- ? cpu_to_hc32(fotg210, 1 << uframe) +- : cpu_to_hc32(fotg210, QH_SMASK); +- hw->hw_info2 |= c_mask; +- } else +- fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); +- +- /* stuff into the periodic schedule */ +- qh_link_periodic(fotg210, qh); +-done: +- return status; +-} +- +-static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, +- struct list_head *qtd_list, gfp_t mem_flags) +-{ +- unsigned epnum; +- unsigned long flags; +- struct fotg210_qh *qh; +- int status; +- struct list_head empty; +- +- /* get endpoint and transfer/schedule data */ +- epnum = urb->ep->desc.bEndpointAddress; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- +- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { +- status = -ESHUTDOWN; +- goto done_not_linked; +- } +- status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); +- if (unlikely(status)) +- goto done_not_linked; +- +- /* get qh and force any scheduling errors */ +- INIT_LIST_HEAD(&empty); +- qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); +- if (qh == NULL) { +- status = -ENOMEM; +- goto done; +- } +- if (qh->qh_state == QH_STATE_IDLE) { +- status = qh_schedule(fotg210, qh); +- if (status) +- goto done; +- } +- +- /* then queue the urb's tds to the qh */ +- qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); +- BUG_ON(qh == NULL); +- +- /* ... update usbfs periodic stats */ +- fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; +- +-done: +- if (unlikely(status)) +- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +-done_not_linked: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- if (status) +- qtd_list_free(fotg210, urb, qtd_list); +- +- return status; +-} +- +-static void scan_intr(struct fotg210_hcd *fotg210) +-{ +- struct fotg210_qh *qh; +- +- list_for_each_entry_safe(qh, fotg210->qh_scan_next, +- &fotg210->intr_qh_list, intr_node) { +-rescan: +- /* clean any finished work for this qh */ +- if (!list_empty(&qh->qtd_list)) { +- int temp; +- +- /* +- * Unlinks could happen here; completion reporting +- * drops the lock. That's why fotg210->qh_scan_next +- * always holds the next qh to scan; if the next qh +- * gets unlinked then fotg210->qh_scan_next is adjusted +- * in qh_unlink_periodic(). +- */ +- temp = qh_completions(fotg210, qh); +- if (unlikely(qh->needs_rescan || +- (list_empty(&qh->qtd_list) && +- qh->qh_state == QH_STATE_LINKED))) +- start_unlink_intr(fotg210, qh); +- else if (temp != 0) +- goto rescan; +- } +- } +-} +- +-/* fotg210_iso_stream ops work with both ITD and SITD */ +- +-static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) +-{ +- struct fotg210_iso_stream *stream; +- +- stream = kzalloc(sizeof(*stream), mem_flags); +- if (likely(stream != NULL)) { +- INIT_LIST_HEAD(&stream->td_list); +- INIT_LIST_HEAD(&stream->free_list); +- stream->next_uframe = -1; +- } +- return stream; +-} +- +-static void iso_stream_init(struct fotg210_hcd *fotg210, +- struct fotg210_iso_stream *stream, struct usb_device *dev, +- int pipe, unsigned interval) +-{ +- u32 buf1; +- unsigned epnum, maxp; +- int is_input; +- long bandwidth; +- unsigned multi; +- struct usb_host_endpoint *ep; +- +- /* +- * this might be a "high bandwidth" highspeed endpoint, +- * as encoded in the ep descriptor's wMaxPacket field +- */ +- epnum = usb_pipeendpoint(pipe); +- is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; +- ep = usb_pipe_endpoint(dev, pipe); +- maxp = usb_endpoint_maxp(&ep->desc); +- if (is_input) +- buf1 = (1 << 11); +- else +- buf1 = 0; +- +- multi = usb_endpoint_maxp_mult(&ep->desc); +- buf1 |= maxp; +- maxp *= multi; +- +- stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); +- stream->buf1 = cpu_to_hc32(fotg210, buf1); +- stream->buf2 = cpu_to_hc32(fotg210, multi); +- +- /* usbfs wants to report the average usecs per frame tied up +- * when transfers on this endpoint are scheduled ... +- */ +- if (dev->speed == USB_SPEED_FULL) { +- interval <<= 3; +- stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, +- is_input, 1, maxp)); +- stream->usecs /= 8; +- } else { +- stream->highspeed = 1; +- stream->usecs = HS_USECS_ISO(maxp); +- } +- bandwidth = stream->usecs * 8; +- bandwidth /= interval; +- +- stream->bandwidth = bandwidth; +- stream->udev = dev; +- stream->bEndpointAddress = is_input | epnum; +- stream->interval = interval; +- stream->maxp = maxp; +-} +- +-static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, +- struct urb *urb) +-{ +- unsigned epnum; +- struct fotg210_iso_stream *stream; +- struct usb_host_endpoint *ep; +- unsigned long flags; +- +- epnum = usb_pipeendpoint(urb->pipe); +- if (usb_pipein(urb->pipe)) +- ep = urb->dev->ep_in[epnum]; +- else +- ep = urb->dev->ep_out[epnum]; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- stream = ep->hcpriv; +- +- if (unlikely(stream == NULL)) { +- stream = iso_stream_alloc(GFP_ATOMIC); +- if (likely(stream != NULL)) { +- ep->hcpriv = stream; +- stream->ep = ep; +- iso_stream_init(fotg210, stream, urb->dev, urb->pipe, +- urb->interval); +- } +- +- /* if dev->ep[epnum] is a QH, hw is set */ +- } else if (unlikely(stream->hw != NULL)) { +- fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", +- urb->dev->devpath, epnum, +- usb_pipein(urb->pipe) ? "in" : "out"); +- stream = NULL; +- } +- +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return stream; +-} +- +-/* fotg210_iso_sched ops can be ITD-only or SITD-only */ +- +-static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, +- gfp_t mem_flags) +-{ +- struct fotg210_iso_sched *iso_sched; +- +- iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); +- if (likely(iso_sched != NULL)) +- INIT_LIST_HEAD(&iso_sched->td_list); +- +- return iso_sched; +-} +- +-static inline void itd_sched_init(struct fotg210_hcd *fotg210, +- struct fotg210_iso_sched *iso_sched, +- struct fotg210_iso_stream *stream, struct urb *urb) +-{ +- unsigned i; +- dma_addr_t dma = urb->transfer_dma; +- +- /* how many uframes are needed for these transfers */ +- iso_sched->span = urb->number_of_packets * stream->interval; +- +- /* figure out per-uframe itd fields that we'll need later +- * when we fit new itds into the schedule. +- */ +- for (i = 0; i < urb->number_of_packets; i++) { +- struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; +- unsigned length; +- dma_addr_t buf; +- u32 trans; +- +- length = urb->iso_frame_desc[i].length; +- buf = dma + urb->iso_frame_desc[i].offset; +- +- trans = FOTG210_ISOC_ACTIVE; +- trans |= buf & 0x0fff; +- if (unlikely(((i + 1) == urb->number_of_packets)) +- && !(urb->transfer_flags & URB_NO_INTERRUPT)) +- trans |= FOTG210_ITD_IOC; +- trans |= length << 16; +- uframe->transaction = cpu_to_hc32(fotg210, trans); +- +- /* might need to cross a buffer page within a uframe */ +- uframe->bufp = (buf & ~(u64)0x0fff); +- buf += length; +- if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) +- uframe->cross = 1; +- } +-} +- +-static void iso_sched_free(struct fotg210_iso_stream *stream, +- struct fotg210_iso_sched *iso_sched) +-{ +- if (!iso_sched) +- return; +- /* caller must hold fotg210->lock!*/ +- list_splice(&iso_sched->td_list, &stream->free_list); +- kfree(iso_sched); +-} +- +-static int itd_urb_transaction(struct fotg210_iso_stream *stream, +- struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) +-{ +- struct fotg210_itd *itd; +- dma_addr_t itd_dma; +- int i; +- unsigned num_itds; +- struct fotg210_iso_sched *sched; +- unsigned long flags; +- +- sched = iso_sched_alloc(urb->number_of_packets, mem_flags); +- if (unlikely(sched == NULL)) +- return -ENOMEM; +- +- itd_sched_init(fotg210, sched, stream, urb); +- +- if (urb->interval < 8) +- num_itds = 1 + (sched->span + 7) / 8; +- else +- num_itds = urb->number_of_packets; +- +- /* allocate/init ITDs */ +- spin_lock_irqsave(&fotg210->lock, flags); +- for (i = 0; i < num_itds; i++) { +- +- /* +- * Use iTDs from the free list, but not iTDs that may +- * still be in use by the hardware. +- */ +- if (likely(!list_empty(&stream->free_list))) { +- itd = list_first_entry(&stream->free_list, +- struct fotg210_itd, itd_list); +- if (itd->frame == fotg210->now_frame) +- goto alloc_itd; +- list_del(&itd->itd_list); +- itd_dma = itd->itd_dma; +- } else { +-alloc_itd: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, +- &itd_dma); +- spin_lock_irqsave(&fotg210->lock, flags); +- if (!itd) { +- iso_sched_free(stream, sched); +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return -ENOMEM; +- } +- } +- +- memset(itd, 0, sizeof(*itd)); +- itd->itd_dma = itd_dma; +- list_add(&itd->itd_list, &sched->td_list); +- } +- spin_unlock_irqrestore(&fotg210->lock, flags); +- +- /* temporarily store schedule info in hcpriv */ +- urb->hcpriv = sched; +- urb->error_count = 0; +- return 0; +-} +- +-static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, +- u8 usecs, u32 period) +-{ +- uframe %= period; +- do { +- /* can't commit more than uframe_periodic_max usec */ +- if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) +- > (fotg210->uframe_periodic_max - usecs)) +- return 0; +- +- /* we know urb->interval is 2^N uframes */ +- uframe += period; +- } while (uframe < mod); +- return 1; +-} +- +-/* This scheduler plans almost as far into the future as it has actual +- * periodic schedule slots. (Affected by TUNE_FLS, which defaults to +- * "as small as possible" to be cache-friendlier.) That limits the size +- * transfers you can stream reliably; avoid more than 64 msec per urb. +- * Also avoid queue depths of less than fotg210's worst irq latency (affected +- * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, +- * and other factors); or more than about 230 msec total (for portability, +- * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! +- */ +- +-#define SCHEDULE_SLOP 80 /* microframes */ +- +-static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, +- struct fotg210_iso_stream *stream) +-{ +- u32 now, next, start, period, span; +- int status; +- unsigned mod = fotg210->periodic_size << 3; +- struct fotg210_iso_sched *sched = urb->hcpriv; +- +- period = urb->interval; +- span = sched->span; +- +- if (span > mod - SCHEDULE_SLOP) { +- fotg210_dbg(fotg210, "iso request %p too long\n", urb); +- status = -EFBIG; +- goto fail; +- } +- +- now = fotg210_read_frame_index(fotg210) & (mod - 1); +- +- /* Typical case: reuse current schedule, stream is still active. +- * Hopefully there are no gaps from the host falling behind +- * (irq delays etc), but if there are we'll take the next +- * slot in the schedule, implicitly assuming URB_ISO_ASAP. +- */ +- if (likely(!list_empty(&stream->td_list))) { +- u32 excess; +- +- /* For high speed devices, allow scheduling within the +- * isochronous scheduling threshold. For full speed devices +- * and Intel PCI-based controllers, don't (work around for +- * Intel ICH9 bug). +- */ +- if (!stream->highspeed && fotg210->fs_i_thresh) +- next = now + fotg210->i_thresh; +- else +- next = now; +- +- /* Fell behind (by up to twice the slop amount)? +- * We decide based on the time of the last currently-scheduled +- * slot, not the time of the next available slot. +- */ +- excess = (stream->next_uframe - period - next) & (mod - 1); +- if (excess >= mod - 2 * SCHEDULE_SLOP) +- start = next + excess - mod + period * +- DIV_ROUND_UP(mod - excess, period); +- else +- start = next + excess + period; +- if (start - now >= mod) { +- fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", +- urb, start - now - period, period, +- mod); +- status = -EFBIG; +- goto fail; +- } +- } +- +- /* need to schedule; when's the next (u)frame we could start? +- * this is bigger than fotg210->i_thresh allows; scheduling itself +- * isn't free, the slop should handle reasonably slow cpus. it +- * can also help high bandwidth if the dma and irq loads don't +- * jump until after the queue is primed. +- */ +- else { +- int done = 0; +- +- start = SCHEDULE_SLOP + (now & ~0x07); +- +- /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ +- +- /* find a uframe slot with enough bandwidth. +- * Early uframes are more precious because full-speed +- * iso IN transfers can't use late uframes, +- * and therefore they should be allocated last. +- */ +- next = start; +- start += period; +- do { +- start--; +- /* check schedule: enough space? */ +- if (itd_slot_ok(fotg210, mod, start, +- stream->usecs, period)) +- done = 1; +- } while (start > next && !done); +- +- /* no room in the schedule */ +- if (!done) { +- fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", +- urb, now, now + mod); +- status = -ENOSPC; +- goto fail; +- } +- } +- +- /* Tried to schedule too far into the future? */ +- if (unlikely(start - now + span - period >= +- mod - 2 * SCHEDULE_SLOP)) { +- fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", +- urb, start - now, span - period, +- mod - 2 * SCHEDULE_SLOP); +- status = -EFBIG; +- goto fail; +- } +- +- stream->next_uframe = start & (mod - 1); +- +- /* report high speed start in uframes; full speed, in frames */ +- urb->start_frame = stream->next_uframe; +- if (!stream->highspeed) +- urb->start_frame >>= 3; +- +- /* Make sure scan_isoc() sees these */ +- if (fotg210->isoc_count == 0) +- fotg210->next_frame = now >> 3; +- return 0; +- +-fail: +- iso_sched_free(stream, sched); +- urb->hcpriv = NULL; +- return status; +-} +- +-static inline void itd_init(struct fotg210_hcd *fotg210, +- struct fotg210_iso_stream *stream, struct fotg210_itd *itd) +-{ +- int i; +- +- /* it's been recently zeroed */ +- itd->hw_next = FOTG210_LIST_END(fotg210); +- itd->hw_bufp[0] = stream->buf0; +- itd->hw_bufp[1] = stream->buf1; +- itd->hw_bufp[2] = stream->buf2; +- +- for (i = 0; i < 8; i++) +- itd->index[i] = -1; +- +- /* All other fields are filled when scheduling */ +-} +- +-static inline void itd_patch(struct fotg210_hcd *fotg210, +- struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, +- unsigned index, u16 uframe) +-{ +- struct fotg210_iso_packet *uf = &iso_sched->packet[index]; +- unsigned pg = itd->pg; +- +- uframe &= 0x07; +- itd->index[uframe] = index; +- +- itd->hw_transaction[uframe] = uf->transaction; +- itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); +- itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); +- itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); +- +- /* iso_frame_desc[].offset must be strictly increasing */ +- if (unlikely(uf->cross)) { +- u64 bufp = uf->bufp + 4096; +- +- itd->pg = ++pg; +- itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); +- itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); +- } +-} +- +-static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, +- struct fotg210_itd *itd) +-{ +- union fotg210_shadow *prev = &fotg210->pshadow[frame]; +- __hc32 *hw_p = &fotg210->periodic[frame]; +- union fotg210_shadow here = *prev; +- __hc32 type = 0; +- +- /* skip any iso nodes which might belong to previous microframes */ +- while (here.ptr) { +- type = Q_NEXT_TYPE(fotg210, *hw_p); +- if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) +- break; +- prev = periodic_next_shadow(fotg210, prev, type); +- hw_p = shadow_next_periodic(fotg210, &here, type); +- here = *prev; +- } +- +- itd->itd_next = here; +- itd->hw_next = *hw_p; +- prev->itd = itd; +- itd->frame = frame; +- wmb(); +- *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); +-} +- +-/* fit urb's itds into the selected schedule slot; activate as needed */ +-static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, +- unsigned mod, struct fotg210_iso_stream *stream) +-{ +- int packet; +- unsigned next_uframe, uframe, frame; +- struct fotg210_iso_sched *iso_sched = urb->hcpriv; +- struct fotg210_itd *itd; +- +- next_uframe = stream->next_uframe & (mod - 1); +- +- if (unlikely(list_empty(&stream->td_list))) { +- fotg210_to_hcd(fotg210)->self.bandwidth_allocated +- += stream->bandwidth; +- fotg210_dbg(fotg210, +- "schedule devp %s ep%d%s-iso period %d start %d.%d\n", +- urb->dev->devpath, stream->bEndpointAddress & 0x0f, +- (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", +- urb->interval, +- next_uframe >> 3, next_uframe & 0x7); +- } +- +- /* fill iTDs uframe by uframe */ +- for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { +- if (itd == NULL) { +- /* ASSERT: we have all necessary itds */ +- +- /* ASSERT: no itds for this endpoint in this uframe */ +- +- itd = list_entry(iso_sched->td_list.next, +- struct fotg210_itd, itd_list); +- list_move_tail(&itd->itd_list, &stream->td_list); +- itd->stream = stream; +- itd->urb = urb; +- itd_init(fotg210, stream, itd); +- } +- +- uframe = next_uframe & 0x07; +- frame = next_uframe >> 3; +- +- itd_patch(fotg210, itd, iso_sched, packet, uframe); +- +- next_uframe += stream->interval; +- next_uframe &= mod - 1; +- packet++; +- +- /* link completed itds into the schedule */ +- if (((next_uframe >> 3) != frame) +- || packet == urb->number_of_packets) { +- itd_link(fotg210, frame & (fotg210->periodic_size - 1), +- itd); +- itd = NULL; +- } +- } +- stream->next_uframe = next_uframe; +- +- /* don't need that schedule data any more */ +- iso_sched_free(stream, iso_sched); +- urb->hcpriv = NULL; +- +- ++fotg210->isoc_count; +- enable_periodic(fotg210); +-} +- +-#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ +- FOTG210_ISOC_XACTERR) +- +-/* Process and recycle a completed ITD. Return true iff its urb completed, +- * and hence its completion callback probably added things to the hardware +- * schedule. +- * +- * Note that we carefully avoid recycling this descriptor until after any +- * completion callback runs, so that it won't be reused quickly. That is, +- * assuming (a) no more than two urbs per frame on this endpoint, and also +- * (b) only this endpoint's completions submit URBs. It seems some silicon +- * corrupts things if you reuse completed descriptors very quickly... +- */ +-static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) +-{ +- struct urb *urb = itd->urb; +- struct usb_iso_packet_descriptor *desc; +- u32 t; +- unsigned uframe; +- int urb_index = -1; +- struct fotg210_iso_stream *stream = itd->stream; +- struct usb_device *dev; +- bool retval = false; +- +- /* for each uframe with a packet */ +- for (uframe = 0; uframe < 8; uframe++) { +- if (likely(itd->index[uframe] == -1)) +- continue; +- urb_index = itd->index[uframe]; +- desc = &urb->iso_frame_desc[urb_index]; +- +- t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); +- itd->hw_transaction[uframe] = 0; +- +- /* report transfer status */ +- if (unlikely(t & ISO_ERRS)) { +- urb->error_count++; +- if (t & FOTG210_ISOC_BUF_ERR) +- desc->status = usb_pipein(urb->pipe) +- ? -ENOSR /* hc couldn't read */ +- : -ECOMM; /* hc couldn't write */ +- else if (t & FOTG210_ISOC_BABBLE) +- desc->status = -EOVERFLOW; +- else /* (t & FOTG210_ISOC_XACTERR) */ +- desc->status = -EPROTO; +- +- /* HC need not update length with this error */ +- if (!(t & FOTG210_ISOC_BABBLE)) { +- desc->actual_length = FOTG210_ITD_LENGTH(t); +- urb->actual_length += desc->actual_length; +- } +- } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { +- desc->status = 0; +- desc->actual_length = FOTG210_ITD_LENGTH(t); +- urb->actual_length += desc->actual_length; +- } else { +- /* URB was too late */ +- desc->status = -EXDEV; +- } +- } +- +- /* handle completion now? */ +- if (likely((urb_index + 1) != urb->number_of_packets)) +- goto done; +- +- /* ASSERT: it's really the last itd for this urb +- * list_for_each_entry (itd, &stream->td_list, itd_list) +- * BUG_ON (itd->urb == urb); +- */ +- +- /* give urb back to the driver; completion often (re)submits */ +- dev = urb->dev; +- fotg210_urb_done(fotg210, urb, 0); +- retval = true; +- urb = NULL; +- +- --fotg210->isoc_count; +- disable_periodic(fotg210); +- +- if (unlikely(list_is_singular(&stream->td_list))) { +- fotg210_to_hcd(fotg210)->self.bandwidth_allocated +- -= stream->bandwidth; +- fotg210_dbg(fotg210, +- "deschedule devp %s ep%d%s-iso\n", +- dev->devpath, stream->bEndpointAddress & 0x0f, +- (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); +- } +- +-done: +- itd->urb = NULL; +- +- /* Add to the end of the free list for later reuse */ +- list_move_tail(&itd->itd_list, &stream->free_list); +- +- /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ +- if (list_empty(&stream->td_list)) { +- list_splice_tail_init(&stream->free_list, +- &fotg210->cached_itd_list); +- start_free_itds(fotg210); +- } +- +- return retval; +-} +- +-static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, +- gfp_t mem_flags) +-{ +- int status = -EINVAL; +- unsigned long flags; +- struct fotg210_iso_stream *stream; +- +- /* Get iso_stream head */ +- stream = iso_stream_find(fotg210, urb); +- if (unlikely(stream == NULL)) { +- fotg210_dbg(fotg210, "can't get iso stream\n"); +- return -ENOMEM; +- } +- if (unlikely(urb->interval != stream->interval && +- fotg210_port_speed(fotg210, 0) == +- USB_PORT_STAT_HIGH_SPEED)) { +- fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", +- stream->interval, urb->interval); +- goto done; +- } +- +-#ifdef FOTG210_URB_TRACE +- fotg210_dbg(fotg210, +- "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", +- __func__, urb->dev->devpath, urb, +- usb_pipeendpoint(urb->pipe), +- usb_pipein(urb->pipe) ? "in" : "out", +- urb->transfer_buffer_length, +- urb->number_of_packets, urb->interval, +- stream); +-#endif +- +- /* allocate ITDs w/o locking anything */ +- status = itd_urb_transaction(stream, fotg210, urb, mem_flags); +- if (unlikely(status < 0)) { +- fotg210_dbg(fotg210, "can't init itds\n"); +- goto done; +- } +- +- /* schedule ... need to lock */ +- spin_lock_irqsave(&fotg210->lock, flags); +- if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { +- status = -ESHUTDOWN; +- goto done_not_linked; +- } +- status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); +- if (unlikely(status)) +- goto done_not_linked; +- status = iso_stream_schedule(fotg210, urb, stream); +- if (likely(status == 0)) +- itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); +- else +- usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); +-done_not_linked: +- spin_unlock_irqrestore(&fotg210->lock, flags); +-done: +- return status; +-} +- +-static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, +- unsigned now_frame, bool live) +-{ +- unsigned uf; +- bool modified; +- union fotg210_shadow q, *q_p; +- __hc32 type, *hw_p; +- +- /* scan each element in frame's queue for completions */ +- q_p = &fotg210->pshadow[frame]; +- hw_p = &fotg210->periodic[frame]; +- q.ptr = q_p->ptr; +- type = Q_NEXT_TYPE(fotg210, *hw_p); +- modified = false; +- +- while (q.ptr) { +- switch (hc32_to_cpu(fotg210, type)) { +- case Q_TYPE_ITD: +- /* If this ITD is still active, leave it for +- * later processing ... check the next entry. +- * No need to check for activity unless the +- * frame is current. +- */ +- if (frame == now_frame && live) { +- rmb(); +- for (uf = 0; uf < 8; uf++) { +- if (q.itd->hw_transaction[uf] & +- ITD_ACTIVE(fotg210)) +- break; +- } +- if (uf < 8) { +- q_p = &q.itd->itd_next; +- hw_p = &q.itd->hw_next; +- type = Q_NEXT_TYPE(fotg210, +- q.itd->hw_next); +- q = *q_p; +- break; +- } +- } +- +- /* Take finished ITDs out of the schedule +- * and process them: recycle, maybe report +- * URB completion. HC won't cache the +- * pointer for much longer, if at all. +- */ +- *q_p = q.itd->itd_next; +- *hw_p = q.itd->hw_next; +- type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); +- wmb(); +- modified = itd_complete(fotg210, q.itd); +- q = *q_p; +- break; +- default: +- fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", +- type, frame, q.ptr); +- fallthrough; +- case Q_TYPE_QH: +- case Q_TYPE_FSTN: +- /* End of the iTDs and siTDs */ +- q.ptr = NULL; +- break; +- } +- +- /* assume completion callbacks modify the queue */ +- if (unlikely(modified && fotg210->isoc_count > 0)) +- return -EINVAL; +- } +- return 0; +-} +- +-static void scan_isoc(struct fotg210_hcd *fotg210) +-{ +- unsigned uf, now_frame, frame, ret; +- unsigned fmask = fotg210->periodic_size - 1; +- bool live; +- +- /* +- * When running, scan from last scan point up to "now" +- * else clean up by scanning everything that's left. +- * Touches as few pages as possible: cache-friendly. +- */ +- if (fotg210->rh_state >= FOTG210_RH_RUNNING) { +- uf = fotg210_read_frame_index(fotg210); +- now_frame = (uf >> 3) & fmask; +- live = true; +- } else { +- now_frame = (fotg210->next_frame - 1) & fmask; +- live = false; +- } +- fotg210->now_frame = now_frame; +- +- frame = fotg210->next_frame; +- for (;;) { +- ret = 1; +- while (ret != 0) +- ret = scan_frame_queue(fotg210, frame, +- now_frame, live); +- +- /* Stop when we have reached the current frame */ +- if (frame == now_frame) +- break; +- frame = (frame + 1) & fmask; +- } +- fotg210->next_frame = now_frame; +-} +- +-/* Display / Set uframe_periodic_max +- */ +-static ssize_t uframe_periodic_max_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- struct fotg210_hcd *fotg210; +- int n; +- +- fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); +- n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); +- return n; +-} +- +- +-static ssize_t uframe_periodic_max_store(struct device *dev, +- struct device_attribute *attr, const char *buf, size_t count) +-{ +- struct fotg210_hcd *fotg210; +- unsigned uframe_periodic_max; +- unsigned frame, uframe; +- unsigned short allocated_max; +- unsigned long flags; +- ssize_t ret; +- +- fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); +- if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) +- return -EINVAL; +- +- if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { +- fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", +- uframe_periodic_max); +- return -EINVAL; +- } +- +- ret = -EINVAL; +- +- /* +- * lock, so that our checking does not race with possible periodic +- * bandwidth allocation through submitting new urbs. +- */ +- spin_lock_irqsave(&fotg210->lock, flags); +- +- /* +- * for request to decrease max periodic bandwidth, we have to check +- * every microframe in the schedule to see whether the decrease is +- * possible. +- */ +- if (uframe_periodic_max < fotg210->uframe_periodic_max) { +- allocated_max = 0; +- +- for (frame = 0; frame < fotg210->periodic_size; ++frame) +- for (uframe = 0; uframe < 7; ++uframe) +- allocated_max = max(allocated_max, +- periodic_usecs(fotg210, frame, +- uframe)); +- +- if (allocated_max > uframe_periodic_max) { +- fotg210_info(fotg210, +- "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", +- allocated_max, uframe_periodic_max); +- goto out_unlock; +- } +- } +- +- /* increasing is always ok */ +- +- fotg210_info(fotg210, +- "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", +- 100 * uframe_periodic_max/125, uframe_periodic_max); +- +- if (uframe_periodic_max != 100) +- fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); +- +- fotg210->uframe_periodic_max = uframe_periodic_max; +- ret = count; +- +-out_unlock: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return ret; +-} +- +-static DEVICE_ATTR_RW(uframe_periodic_max); +- +-static inline int create_sysfs_files(struct fotg210_hcd *fotg210) +-{ +- struct device *controller = fotg210_to_hcd(fotg210)->self.controller; +- +- return device_create_file(controller, &dev_attr_uframe_periodic_max); +-} +- +-static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) +-{ +- struct device *controller = fotg210_to_hcd(fotg210)->self.controller; +- +- device_remove_file(controller, &dev_attr_uframe_periodic_max); +-} +-/* On some systems, leaving remote wakeup enabled prevents system shutdown. +- * The firmware seems to think that powering off is a wakeup event! +- * This routine turns off remote wakeup and everything else, on all ports. +- */ +-static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) +-{ +- u32 __iomem *status_reg = &fotg210->regs->port_status; +- +- fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); +-} +- +-/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. +- * Must be called with interrupts enabled and the lock not held. +- */ +-static void fotg210_silence_controller(struct fotg210_hcd *fotg210) +-{ +- fotg210_halt(fotg210); +- +- spin_lock_irq(&fotg210->lock); +- fotg210->rh_state = FOTG210_RH_HALTED; +- fotg210_turn_off_all_ports(fotg210); +- spin_unlock_irq(&fotg210->lock); +-} +- +-/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). +- * This forcibly disables dma and IRQs, helping kexec and other cases +- * where the next system software may expect clean state. +- */ +-static void fotg210_shutdown(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- spin_lock_irq(&fotg210->lock); +- fotg210->shutdown = true; +- fotg210->rh_state = FOTG210_RH_STOPPING; +- fotg210->enabled_hrtimer_events = 0; +- spin_unlock_irq(&fotg210->lock); +- +- fotg210_silence_controller(fotg210); +- +- hrtimer_cancel(&fotg210->hrtimer); +-} +- +-/* fotg210_work is called from some interrupts, timers, and so on. +- * it calls driver completion functions, after dropping fotg210->lock. +- */ +-static void fotg210_work(struct fotg210_hcd *fotg210) +-{ +- /* another CPU may drop fotg210->lock during a schedule scan while +- * it reports urb completions. this flag guards against bogus +- * attempts at re-entrant schedule scanning. +- */ +- if (fotg210->scanning) { +- fotg210->need_rescan = true; +- return; +- } +- fotg210->scanning = true; +- +-rescan: +- fotg210->need_rescan = false; +- if (fotg210->async_count) +- scan_async(fotg210); +- if (fotg210->intr_count > 0) +- scan_intr(fotg210); +- if (fotg210->isoc_count > 0) +- scan_isoc(fotg210); +- if (fotg210->need_rescan) +- goto rescan; +- fotg210->scanning = false; +- +- /* the IO watchdog guards against hardware or driver bugs that +- * misplace IRQs, and should let us run completely without IRQs. +- * such lossage has been observed on both VT6202 and VT8235. +- */ +- turn_on_io_watchdog(fotg210); +-} +- +-/* Called when the fotg210_hcd module is removed. +- */ +-static void fotg210_stop(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- fotg210_dbg(fotg210, "stop\n"); +- +- /* no more interrupts ... */ +- +- spin_lock_irq(&fotg210->lock); +- fotg210->enabled_hrtimer_events = 0; +- spin_unlock_irq(&fotg210->lock); +- +- fotg210_quiesce(fotg210); +- fotg210_silence_controller(fotg210); +- fotg210_reset(fotg210); +- +- hrtimer_cancel(&fotg210->hrtimer); +- remove_sysfs_files(fotg210); +- remove_debug_files(fotg210); +- +- /* root hub is shut down separately (first, when possible) */ +- spin_lock_irq(&fotg210->lock); +- end_free_itds(fotg210); +- spin_unlock_irq(&fotg210->lock); +- fotg210_mem_cleanup(fotg210); +- +-#ifdef FOTG210_STATS +- fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", +- fotg210->stats.normal, fotg210->stats.error, +- fotg210->stats.iaa, fotg210->stats.lost_iaa); +- fotg210_dbg(fotg210, "complete %ld unlink %ld\n", +- fotg210->stats.complete, fotg210->stats.unlink); +-#endif +- +- dbg_status(fotg210, "fotg210_stop completed", +- fotg210_readl(fotg210, &fotg210->regs->status)); +-} +- +-/* one-time init, only for memory state */ +-static int hcd_fotg210_init(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- u32 temp; +- int retval; +- u32 hcc_params; +- struct fotg210_qh_hw *hw; +- +- spin_lock_init(&fotg210->lock); +- +- /* +- * keep io watchdog by default, those good HCDs could turn off it later +- */ +- fotg210->need_io_watchdog = 1; +- +- hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); +- fotg210->hrtimer.function = fotg210_hrtimer_func; +- fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; +- +- hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); +- +- /* +- * by default set standard 80% (== 100 usec/uframe) max periodic +- * bandwidth as required by USB 2.0 +- */ +- fotg210->uframe_periodic_max = 100; +- +- /* +- * hw default: 1K periodic list heads, one per frame. +- * periodic_size can shrink by USBCMD update if hcc_params allows. +- */ +- fotg210->periodic_size = DEFAULT_I_TDPS; +- INIT_LIST_HEAD(&fotg210->intr_qh_list); +- INIT_LIST_HEAD(&fotg210->cached_itd_list); +- +- if (HCC_PGM_FRAMELISTLEN(hcc_params)) { +- /* periodic schedule size can be smaller than default */ +- switch (FOTG210_TUNE_FLS) { +- case 0: +- fotg210->periodic_size = 1024; +- break; +- case 1: +- fotg210->periodic_size = 512; +- break; +- case 2: +- fotg210->periodic_size = 256; +- break; +- default: +- BUG(); +- } +- } +- retval = fotg210_mem_init(fotg210, GFP_KERNEL); +- if (retval < 0) +- return retval; +- +- /* controllers may cache some of the periodic schedule ... */ +- fotg210->i_thresh = 2; +- +- /* +- * dedicate a qh for the async ring head, since we couldn't unlink +- * a 'real' qh without stopping the async schedule [4.8]. use it +- * as the 'reclamation list head' too. +- * its dummy is used in hw_alt_next of many tds, to prevent the qh +- * from automatically advancing to the next td after short reads. +- */ +- fotg210->async->qh_next.qh = NULL; +- hw = fotg210->async->hw; +- hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); +- hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); +- hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); +- hw->hw_qtd_next = FOTG210_LIST_END(fotg210); +- fotg210->async->qh_state = QH_STATE_LINKED; +- hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); +- +- /* clear interrupt enables, set irq latency */ +- if (log2_irq_thresh < 0 || log2_irq_thresh > 6) +- log2_irq_thresh = 0; +- temp = 1 << (16 + log2_irq_thresh); +- if (HCC_CANPARK(hcc_params)) { +- /* HW default park == 3, on hardware that supports it (like +- * NVidia and ALI silicon), maximizes throughput on the async +- * schedule by avoiding QH fetches between transfers. +- * +- * With fast usb storage devices and NForce2, "park" seems to +- * make problems: throughput reduction (!), data errors... +- */ +- if (park) { +- park = min_t(unsigned, park, 3); +- temp |= CMD_PARK; +- temp |= park << 8; +- } +- fotg210_dbg(fotg210, "park %d\n", park); +- } +- if (HCC_PGM_FRAMELISTLEN(hcc_params)) { +- /* periodic schedule size can be smaller than default */ +- temp &= ~(3 << 2); +- temp |= (FOTG210_TUNE_FLS << 2); +- } +- fotg210->command = temp; +- +- /* Accept arbitrarily long scatter-gather lists */ +- if (!hcd->localmem_pool) +- hcd->self.sg_tablesize = ~0; +- return 0; +-} +- +-/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ +-static int fotg210_run(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- u32 temp; +- +- hcd->uses_new_polling = 1; +- +- /* EHCI spec section 4.1 */ +- +- fotg210_writel(fotg210, fotg210->periodic_dma, +- &fotg210->regs->frame_list); +- fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, +- &fotg210->regs->async_next); +- +- /* +- * hcc_params controls whether fotg210->regs->segment must (!!!) +- * be used; it constrains QH/ITD/SITD and QTD locations. +- * dma_pool consistent memory always uses segment zero. +- * streaming mappings for I/O buffers, like dma_map_single(), +- * can return segments above 4GB, if the device allows. +- * +- * NOTE: the dma mask is visible through dev->dma_mask, so +- * drivers can pass this info along ... like NETIF_F_HIGHDMA, +- * Scsi_Host.highmem_io, and so forth. It's readonly to all +- * host side drivers though. +- */ +- fotg210_readl(fotg210, &fotg210->caps->hcc_params); +- +- /* +- * Philips, Intel, and maybe others need CMD_RUN before the +- * root hub will detect new devices (why?); NEC doesn't +- */ +- fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); +- fotg210->command |= CMD_RUN; +- fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); +- dbg_cmd(fotg210, "init", fotg210->command); +- +- /* +- * Start, enabling full USB 2.0 functionality ... usb 1.1 devices +- * are explicitly handed to companion controller(s), so no TT is +- * involved with the root hub. (Except where one is integrated, +- * and there's no companion controller unless maybe for USB OTG.) +- * +- * Turning on the CF flag will transfer ownership of all ports +- * from the companions to the EHCI controller. If any of the +- * companions are in the middle of a port reset at the time, it +- * could cause trouble. Write-locking ehci_cf_port_reset_rwsem +- * guarantees that no resets are in progress. After we set CF, +- * a short delay lets the hardware catch up; new resets shouldn't +- * be started before the port switching actions could complete. +- */ +- down_write(&ehci_cf_port_reset_rwsem); +- fotg210->rh_state = FOTG210_RH_RUNNING; +- /* unblock posted writes */ +- fotg210_readl(fotg210, &fotg210->regs->command); +- usleep_range(5000, 10000); +- up_write(&ehci_cf_port_reset_rwsem); +- fotg210->last_periodic_enable = ktime_get_real(); +- +- temp = HC_VERSION(fotg210, +- fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); +- fotg210_info(fotg210, +- "USB %x.%x started, EHCI %x.%02x\n", +- ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), +- temp >> 8, temp & 0xff); +- +- fotg210_writel(fotg210, INTR_MASK, +- &fotg210->regs->intr_enable); /* Turn On Interrupts */ +- +- /* GRR this is run-once init(), being done every time the HC starts. +- * So long as they're part of class devices, we can't do it init() +- * since the class device isn't created that early. +- */ +- create_debug_files(fotg210); +- create_sysfs_files(fotg210); +- +- return 0; +-} +- +-static int fotg210_setup(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- int retval; +- +- fotg210->regs = (void __iomem *)fotg210->caps + +- HC_LENGTH(fotg210, +- fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); +- dbg_hcs_params(fotg210, "reset"); +- dbg_hcc_params(fotg210, "reset"); +- +- /* cache this readonly data; minimize chip reads */ +- fotg210->hcs_params = fotg210_readl(fotg210, +- &fotg210->caps->hcs_params); +- +- fotg210->sbrn = HCD_USB2; +- +- /* data structure init */ +- retval = hcd_fotg210_init(hcd); +- if (retval) +- return retval; +- +- retval = fotg210_halt(fotg210); +- if (retval) +- return retval; +- +- fotg210_reset(fotg210); +- +- return 0; +-} +- +-static irqreturn_t fotg210_irq(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- u32 status, masked_status, pcd_status = 0, cmd; +- int bh; +- +- spin_lock(&fotg210->lock); +- +- status = fotg210_readl(fotg210, &fotg210->regs->status); +- +- /* e.g. cardbus physical eject */ +- if (status == ~(u32) 0) { +- fotg210_dbg(fotg210, "device removed\n"); +- goto dead; +- } +- +- /* +- * We don't use STS_FLR, but some controllers don't like it to +- * remain on, so mask it out along with the other status bits. +- */ +- masked_status = status & (INTR_MASK | STS_FLR); +- +- /* Shared IRQ? */ +- if (!masked_status || +- unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { +- spin_unlock(&fotg210->lock); +- return IRQ_NONE; +- } +- +- /* clear (just) interrupts */ +- fotg210_writel(fotg210, masked_status, &fotg210->regs->status); +- cmd = fotg210_readl(fotg210, &fotg210->regs->command); +- bh = 0; +- +- /* unrequested/ignored: Frame List Rollover */ +- dbg_status(fotg210, "irq", status); +- +- /* INT, ERR, and IAA interrupt rates can be throttled */ +- +- /* normal [4.15.1.2] or error [4.15.1.1] completion */ +- if (likely((status & (STS_INT|STS_ERR)) != 0)) { +- if (likely((status & STS_ERR) == 0)) +- INCR(fotg210->stats.normal); +- else +- INCR(fotg210->stats.error); +- bh = 1; +- } +- +- /* complete the unlinking of some qh [4.15.2.3] */ +- if (status & STS_IAA) { +- +- /* Turn off the IAA watchdog */ +- fotg210->enabled_hrtimer_events &= +- ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); +- +- /* +- * Mild optimization: Allow another IAAD to reset the +- * hrtimer, if one occurs before the next expiration. +- * In theory we could always cancel the hrtimer, but +- * tests show that about half the time it will be reset +- * for some other event anyway. +- */ +- if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) +- ++fotg210->next_hrtimer_event; +- +- /* guard against (alleged) silicon errata */ +- if (cmd & CMD_IAAD) +- fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); +- if (fotg210->async_iaa) { +- INCR(fotg210->stats.iaa); +- end_unlink_async(fotg210); +- } else +- fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); +- } +- +- /* remote wakeup [4.3.1] */ +- if (status & STS_PCD) { +- int pstatus; +- u32 __iomem *status_reg = &fotg210->regs->port_status; +- +- /* kick root hub later */ +- pcd_status = status; +- +- /* resume root hub? */ +- if (fotg210->rh_state == FOTG210_RH_SUSPENDED) +- usb_hcd_resume_root_hub(hcd); +- +- pstatus = fotg210_readl(fotg210, status_reg); +- +- if (test_bit(0, &fotg210->suspended_ports) && +- ((pstatus & PORT_RESUME) || +- !(pstatus & PORT_SUSPEND)) && +- (pstatus & PORT_PE) && +- fotg210->reset_done[0] == 0) { +- +- /* start 20 msec resume signaling from this port, +- * and make hub_wq collect PORT_STAT_C_SUSPEND to +- * stop that signaling. Use 5 ms extra for safety, +- * like usb_port_resume() does. +- */ +- fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); +- set_bit(0, &fotg210->resuming_ports); +- fotg210_dbg(fotg210, "port 1 remote wakeup\n"); +- mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); +- } +- } +- +- /* PCI errors [4.15.2.4] */ +- if (unlikely((status & STS_FATAL) != 0)) { +- fotg210_err(fotg210, "fatal error\n"); +- dbg_cmd(fotg210, "fatal", cmd); +- dbg_status(fotg210, "fatal", status); +-dead: +- usb_hc_died(hcd); +- +- /* Don't let the controller do anything more */ +- fotg210->shutdown = true; +- fotg210->rh_state = FOTG210_RH_STOPPING; +- fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); +- fotg210_writel(fotg210, fotg210->command, +- &fotg210->regs->command); +- fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); +- fotg210_handle_controller_death(fotg210); +- +- /* Handle completions when the controller stops */ +- bh = 0; +- } +- +- if (bh) +- fotg210_work(fotg210); +- spin_unlock(&fotg210->lock); +- if (pcd_status) +- usb_hcd_poll_rh_status(hcd); +- return IRQ_HANDLED; +-} +- +-/* non-error returns are a promise to giveback() the urb later +- * we drop ownership so next owner (or urb unlink) can get it +- * +- * urb + dev is in hcd.self.controller.urb_list +- * we're queueing TDs onto software and hardware lists +- * +- * hcd-specific init for hcpriv hasn't been done yet +- * +- * NOTE: control, bulk, and interrupt share the same code to append TDs +- * to a (possibly active) QH, and the same QH scanning code. +- */ +-static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, +- gfp_t mem_flags) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- struct list_head qtd_list; +- +- INIT_LIST_HEAD(&qtd_list); +- +- switch (usb_pipetype(urb->pipe)) { +- case PIPE_CONTROL: +- /* qh_completions() code doesn't handle all the fault cases +- * in multi-TD control transfers. Even 1KB is rare anyway. +- */ +- if (urb->transfer_buffer_length > (16 * 1024)) +- return -EMSGSIZE; +- fallthrough; +- /* case PIPE_BULK: */ +- default: +- if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) +- return -ENOMEM; +- return submit_async(fotg210, urb, &qtd_list, mem_flags); +- +- case PIPE_INTERRUPT: +- if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) +- return -ENOMEM; +- return intr_submit(fotg210, urb, &qtd_list, mem_flags); +- +- case PIPE_ISOCHRONOUS: +- return itd_submit(fotg210, urb, mem_flags); +- } +-} +- +-/* remove from hardware lists +- * completions normally happen asynchronously +- */ +- +-static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- struct fotg210_qh *qh; +- unsigned long flags; +- int rc; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- rc = usb_hcd_check_unlink_urb(hcd, urb, status); +- if (rc) +- goto done; +- +- switch (usb_pipetype(urb->pipe)) { +- /* case PIPE_CONTROL: */ +- /* case PIPE_BULK:*/ +- default: +- qh = (struct fotg210_qh *) urb->hcpriv; +- if (!qh) +- break; +- switch (qh->qh_state) { +- case QH_STATE_LINKED: +- case QH_STATE_COMPLETING: +- start_unlink_async(fotg210, qh); +- break; +- case QH_STATE_UNLINK: +- case QH_STATE_UNLINK_WAIT: +- /* already started */ +- break; +- case QH_STATE_IDLE: +- /* QH might be waiting for a Clear-TT-Buffer */ +- qh_completions(fotg210, qh); +- break; +- } +- break; +- +- case PIPE_INTERRUPT: +- qh = (struct fotg210_qh *) urb->hcpriv; +- if (!qh) +- break; +- switch (qh->qh_state) { +- case QH_STATE_LINKED: +- case QH_STATE_COMPLETING: +- start_unlink_intr(fotg210, qh); +- break; +- case QH_STATE_IDLE: +- qh_completions(fotg210, qh); +- break; +- default: +- fotg210_dbg(fotg210, "bogus qh %p state %d\n", +- qh, qh->qh_state); +- goto done; +- } +- break; +- +- case PIPE_ISOCHRONOUS: +- /* itd... */ +- +- /* wait till next completion, do it then. */ +- /* completion irqs can wait up to 1024 msec, */ +- break; +- } +-done: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- return rc; +-} +- +-/* bulk qh holds the data toggle */ +- +-static void fotg210_endpoint_disable(struct usb_hcd *hcd, +- struct usb_host_endpoint *ep) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- unsigned long flags; +- struct fotg210_qh *qh, *tmp; +- +- /* ASSERT: any requests/urbs are being unlinked */ +- /* ASSERT: nobody can be submitting urbs for this any more */ +- +-rescan: +- spin_lock_irqsave(&fotg210->lock, flags); +- qh = ep->hcpriv; +- if (!qh) +- goto done; +- +- /* endpoints can be iso streams. for now, we don't +- * accelerate iso completions ... so spin a while. +- */ +- if (qh->hw == NULL) { +- struct fotg210_iso_stream *stream = ep->hcpriv; +- +- if (!list_empty(&stream->td_list)) +- goto idle_timeout; +- +- /* BUG_ON(!list_empty(&stream->free_list)); */ +- kfree(stream); +- goto done; +- } +- +- if (fotg210->rh_state < FOTG210_RH_RUNNING) +- qh->qh_state = QH_STATE_IDLE; +- switch (qh->qh_state) { +- case QH_STATE_LINKED: +- case QH_STATE_COMPLETING: +- for (tmp = fotg210->async->qh_next.qh; +- tmp && tmp != qh; +- tmp = tmp->qh_next.qh) +- continue; +- /* periodic qh self-unlinks on empty, and a COMPLETING qh +- * may already be unlinked. +- */ +- if (tmp) +- start_unlink_async(fotg210, qh); +- fallthrough; +- case QH_STATE_UNLINK: /* wait for hw to finish? */ +- case QH_STATE_UNLINK_WAIT: +-idle_timeout: +- spin_unlock_irqrestore(&fotg210->lock, flags); +- schedule_timeout_uninterruptible(1); +- goto rescan; +- case QH_STATE_IDLE: /* fully unlinked */ +- if (qh->clearing_tt) +- goto idle_timeout; +- if (list_empty(&qh->qtd_list)) { +- qh_destroy(fotg210, qh); +- break; +- } +- fallthrough; +- default: +- /* caller was supposed to have unlinked any requests; +- * that's not our job. just leak this memory. +- */ +- fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", +- qh, ep->desc.bEndpointAddress, qh->qh_state, +- list_empty(&qh->qtd_list) ? "" : "(has tds)"); +- break; +- } +-done: +- ep->hcpriv = NULL; +- spin_unlock_irqrestore(&fotg210->lock, flags); +-} +- +-static void fotg210_endpoint_reset(struct usb_hcd *hcd, +- struct usb_host_endpoint *ep) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- struct fotg210_qh *qh; +- int eptype = usb_endpoint_type(&ep->desc); +- int epnum = usb_endpoint_num(&ep->desc); +- int is_out = usb_endpoint_dir_out(&ep->desc); +- unsigned long flags; +- +- if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) +- return; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- qh = ep->hcpriv; +- +- /* For Bulk and Interrupt endpoints we maintain the toggle state +- * in the hardware; the toggle bits in udev aren't used at all. +- * When an endpoint is reset by usb_clear_halt() we must reset +- * the toggle bit in the QH. +- */ +- if (qh) { +- usb_settoggle(qh->dev, epnum, is_out, 0); +- if (!list_empty(&qh->qtd_list)) { +- WARN_ONCE(1, "clear_halt for a busy endpoint\n"); +- } else if (qh->qh_state == QH_STATE_LINKED || +- qh->qh_state == QH_STATE_COMPLETING) { +- +- /* The toggle value in the QH can't be updated +- * while the QH is active. Unlink it now; +- * re-linking will call qh_refresh(). +- */ +- if (eptype == USB_ENDPOINT_XFER_BULK) +- start_unlink_async(fotg210, qh); +- else +- start_unlink_intr(fotg210, qh); +- } +- } +- spin_unlock_irqrestore(&fotg210->lock, flags); +-} +- +-static int fotg210_get_frame(struct usb_hcd *hcd) +-{ +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- return (fotg210_read_frame_index(fotg210) >> 3) % +- fotg210->periodic_size; +-} +- +-/* The EHCI in ChipIdea HDRC cannot be a separate module or device, +- * because its registers (and irq) are shared between host/gadget/otg +- * functions and in order to facilitate role switching we cannot +- * give the fotg210 driver exclusive access to those. +- */ +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_LICENSE("GPL"); +- +-static const struct hc_driver fotg210_fotg210_hc_driver = { +- .description = hcd_name, +- .product_desc = "Faraday USB2.0 Host Controller", +- .hcd_priv_size = sizeof(struct fotg210_hcd), +- +- /* +- * generic hardware linkage +- */ +- .irq = fotg210_irq, +- .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, +- +- /* +- * basic lifecycle operations +- */ +- .reset = hcd_fotg210_init, +- .start = fotg210_run, +- .stop = fotg210_stop, +- .shutdown = fotg210_shutdown, +- +- /* +- * managing i/o requests and associated device resources +- */ +- .urb_enqueue = fotg210_urb_enqueue, +- .urb_dequeue = fotg210_urb_dequeue, +- .endpoint_disable = fotg210_endpoint_disable, +- .endpoint_reset = fotg210_endpoint_reset, +- +- /* +- * scheduling support +- */ +- .get_frame_number = fotg210_get_frame, +- +- /* +- * root hub support +- */ +- .hub_status_data = fotg210_hub_status_data, +- .hub_control = fotg210_hub_control, +- .bus_suspend = fotg210_bus_suspend, +- .bus_resume = fotg210_bus_resume, +- +- .relinquish_port = fotg210_relinquish_port, +- .port_handed_over = fotg210_port_handed_over, +- +- .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, +-}; +- +-static void fotg210_init(struct fotg210_hcd *fotg210) +-{ +- u32 value; +- +- iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, +- &fotg210->regs->gmir); +- +- value = ioread32(&fotg210->regs->otgcsr); +- value &= ~OTGCSR_A_BUS_DROP; +- value |= OTGCSR_A_BUS_REQ; +- iowrite32(value, &fotg210->regs->otgcsr); +-} +- +-/* +- * fotg210_hcd_probe - initialize faraday FOTG210 HCDs +- * +- * Allocates basic resources for this USB host controller, and +- * then invokes the start() method for the HCD associated with it +- * through the hotplug entry's driver_data. +- */ +-static int fotg210_hcd_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct usb_hcd *hcd; +- struct resource *res; +- int irq; +- int retval; +- struct fotg210_hcd *fotg210; +- +- if (usb_disabled()) +- return -ENODEV; +- +- pdev->dev.power.power_state = PMSG_ON; +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return irq; +- +- hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, +- dev_name(dev)); +- if (!hcd) { +- dev_err(dev, "failed to create hcd\n"); +- retval = -ENOMEM; +- goto fail_create_hcd; +- } +- +- hcd->has_tt = 1; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- hcd->regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(hcd->regs)) { +- retval = PTR_ERR(hcd->regs); +- goto failed_put_hcd; +- } +- +- hcd->rsrc_start = res->start; +- hcd->rsrc_len = resource_size(res); +- +- fotg210 = hcd_to_fotg210(hcd); +- +- fotg210->caps = hcd->regs; +- +- /* It's OK not to supply this clock */ +- fotg210->pclk = clk_get(dev, "PCLK"); +- if (!IS_ERR(fotg210->pclk)) { +- retval = clk_prepare_enable(fotg210->pclk); +- if (retval) { +- dev_err(dev, "failed to enable PCLK\n"); +- goto failed_put_hcd; +- } +- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { +- /* +- * Percolate deferrals, for anything else, +- * just live without the clocking. +- */ +- retval = PTR_ERR(fotg210->pclk); +- goto failed_dis_clk; +- } +- +- retval = fotg210_setup(hcd); +- if (retval) +- goto failed_dis_clk; +- +- fotg210_init(fotg210); +- +- retval = usb_add_hcd(hcd, irq, IRQF_SHARED); +- if (retval) { +- dev_err(dev, "failed to add hcd with err %d\n", retval); +- goto failed_dis_clk; +- } +- device_wakeup_enable(hcd->self.controller); +- platform_set_drvdata(pdev, hcd); +- +- return retval; +- +-failed_dis_clk: +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } +-failed_put_hcd: +- usb_put_hcd(hcd); +-fail_create_hcd: +- dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); +- return retval; +-} +- +-/* +- * fotg210_hcd_remove - shutdown processing for EHCI HCDs +- * @dev: USB Host Controller being removed +- * +- */ +-static int fotg210_hcd_remove(struct platform_device *pdev) +-{ +- struct usb_hcd *hcd = platform_get_drvdata(pdev); +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } +- +- usb_remove_hcd(hcd); +- usb_put_hcd(hcd); +- +- return 0; +-} +- +-#ifdef CONFIG_OF +-static const struct of_device_id fotg210_of_match[] = { +- { .compatible = "faraday,fotg210" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, fotg210_of_match); +-#endif +- +-static struct platform_driver fotg210_hcd_driver = { +- .driver = { +- .name = "fotg210-hcd", +- .of_match_table = of_match_ptr(fotg210_of_match), +- }, +- .probe = fotg210_hcd_probe, +- .remove = fotg210_hcd_remove, +-}; +- +-static int __init fotg210_hcd_init(void) +-{ +- int retval = 0; +- +- if (usb_disabled()) +- return -ENODEV; +- +- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || +- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) +- pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); +- +- pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", +- hcd_name, sizeof(struct fotg210_qh), +- sizeof(struct fotg210_qtd), +- sizeof(struct fotg210_itd)); +- +- fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); +- +- retval = platform_driver_register(&fotg210_hcd_driver); +- if (retval < 0) +- goto clean; +- return retval; +- +-clean: +- debugfs_remove(fotg210_debug_root); +- fotg210_debug_root = NULL; +- +- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +- return retval; +-} +-module_init(fotg210_hcd_init); +- +-static void __exit fotg210_hcd_cleanup(void) +-{ +- platform_driver_unregister(&fotg210_hcd_driver); +- debugfs_remove(fotg210_debug_root); +- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +-} +-module_exit(fotg210_hcd_cleanup); +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -0,0 +1,5727 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Faraday FOTG210 EHCI-like driver ++ * ++ * Copyright (c) 2013 Faraday Technology Corporation ++ * ++ * Author: Yuan-Hsin Chen ++ * Feng-Hsin Chiang ++ * Po-Yu Chuang ++ * ++ * Most of code borrowed from the Linux-3.7 EHCI driver ++ */ ++#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 ++ ++#define DRIVER_AUTHOR "Yuan-Hsin Chen" ++#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" ++static const char hcd_name[] = "fotg210_hcd"; ++ ++#undef FOTG210_URB_TRACE ++#define FOTG210_STATS ++ ++/* magic numbers that can affect system performance */ ++#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ ++#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ ++#define FOTG210_TUNE_RL_TT 0 ++#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ ++#define FOTG210_TUNE_MULT_TT 1 ++ ++/* Some drivers think it's safe to schedule isochronous transfers more than 256 ++ * ms into the future (partly as a result of an old bug in the scheduling ++ * code). In an attempt to avoid trouble, we will use a minimum scheduling ++ * length of 512 frames instead of 256. ++ */ ++#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ ++ ++/* Initial IRQ latency: faster than hw default */ ++static int log2_irq_thresh; /* 0 to 6 */ ++module_param(log2_irq_thresh, int, S_IRUGO); ++MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); ++ ++/* initial park setting: slower than hw default */ ++static unsigned park; ++module_param(park, uint, S_IRUGO); ++MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); ++ ++/* for link power management(LPM) feature */ ++static unsigned int hird; ++module_param(hird, int, S_IRUGO); ++MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); ++ ++#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) ++ ++#include "fotg210-hcd.h" ++ ++#define fotg210_dbg(fotg210, fmt, args...) \ ++ dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_err(fotg210, fmt, args...) \ ++ dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_info(fotg210, fmt, args...) \ ++ dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++#define fotg210_warn(fotg210, fmt, args...) \ ++ dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) ++ ++/* check the values in the HCSPARAMS register (host controller _Structural_ ++ * parameters) see EHCI spec, Table 2-4 for each value ++ */ ++static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) ++{ ++ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); ++ ++ fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, ++ HCS_N_PORTS(params)); ++} ++ ++/* check the values in the HCCPARAMS register (host controller _Capability_ ++ * parameters) see EHCI Spec, Table 2-5 for each value ++ */ ++static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) ++{ ++ u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, ++ params, ++ HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", ++ HCC_CANPARK(params) ? " park" : ""); ++} ++ ++static void __maybe_unused ++dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) ++{ ++ fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, ++ hc32_to_cpup(fotg210, &qtd->hw_next), ++ hc32_to_cpup(fotg210, &qtd->hw_alt_next), ++ hc32_to_cpup(fotg210, &qtd->hw_token), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[0])); ++ if (qtd->hw_buf[1]) ++ fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", ++ hc32_to_cpup(fotg210, &qtd->hw_buf[1]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[2]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[3]), ++ hc32_to_cpup(fotg210, &qtd->hw_buf[4])); ++} ++ ++static void __maybe_unused ++dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, ++ hw->hw_next, hw->hw_info1, hw->hw_info2, ++ hw->hw_current); ++ ++ dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); ++} ++ ++static void __maybe_unused ++dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) ++{ ++ fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, ++ itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), ++ itd->urb); ++ ++ fotg210_dbg(fotg210, ++ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", ++ hc32_to_cpu(fotg210, itd->hw_transaction[0]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[1]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[2]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[3]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[4]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[5]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[6]), ++ hc32_to_cpu(fotg210, itd->hw_transaction[7])); ++ ++ fotg210_dbg(fotg210, ++ " buf: %08x %08x %08x %08x %08x %08x %08x\n", ++ hc32_to_cpu(fotg210, itd->hw_bufp[0]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[1]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[2]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[3]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[4]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[5]), ++ hc32_to_cpu(fotg210, itd->hw_bufp[6])); ++ ++ fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", ++ itd->index[0], itd->index[1], itd->index[2], ++ itd->index[3], itd->index[4], itd->index[5], ++ itd->index[6], itd->index[7]); ++} ++ ++static int __maybe_unused ++dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) ++{ ++ return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", ++ label, label[0] ? " " : "", status, ++ (status & STS_ASS) ? " Async" : "", ++ (status & STS_PSS) ? " Periodic" : "", ++ (status & STS_RECL) ? " Recl" : "", ++ (status & STS_HALT) ? " Halt" : "", ++ (status & STS_IAA) ? " IAA" : "", ++ (status & STS_FATAL) ? " FATAL" : "", ++ (status & STS_FLR) ? " FLR" : "", ++ (status & STS_PCD) ? " PCD" : "", ++ (status & STS_ERR) ? " ERR" : "", ++ (status & STS_INT) ? " INT" : ""); ++} ++ ++static int __maybe_unused ++dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) ++{ ++ return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", ++ label, label[0] ? " " : "", enable, ++ (enable & STS_IAA) ? " IAA" : "", ++ (enable & STS_FATAL) ? " FATAL" : "", ++ (enable & STS_FLR) ? " FLR" : "", ++ (enable & STS_PCD) ? " PCD" : "", ++ (enable & STS_ERR) ? " ERR" : "", ++ (enable & STS_INT) ? " INT" : ""); ++} ++ ++static const char *const fls_strings[] = { "1024", "512", "256", "??" }; ++ ++static int dbg_command_buf(char *buf, unsigned len, const char *label, ++ u32 command) ++{ ++ return scnprintf(buf, len, ++ "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", ++ label, label[0] ? " " : "", command, ++ (command & CMD_PARK) ? " park" : "(park)", ++ CMD_PARK_CNT(command), ++ (command >> 16) & 0x3f, ++ (command & CMD_IAAD) ? " IAAD" : "", ++ (command & CMD_ASE) ? " Async" : "", ++ (command & CMD_PSE) ? " Periodic" : "", ++ fls_strings[(command >> 2) & 0x3], ++ (command & CMD_RESET) ? " Reset" : "", ++ (command & CMD_RUN) ? "RUN" : "HALT"); ++} ++ ++static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, ++ u32 status) ++{ ++ char *sig; ++ ++ /* signaling state */ ++ switch (status & (3 << 10)) { ++ case 0 << 10: ++ sig = "se0"; ++ break; ++ case 1 << 10: ++ sig = "k"; ++ break; /* low speed */ ++ case 2 << 10: ++ sig = "j"; ++ break; ++ default: ++ sig = "?"; ++ break; ++ } ++ ++ scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", ++ label, label[0] ? " " : "", port, status, ++ status >> 25, /*device address */ ++ sig, ++ (status & PORT_RESET) ? " RESET" : "", ++ (status & PORT_SUSPEND) ? " SUSPEND" : "", ++ (status & PORT_RESUME) ? " RESUME" : "", ++ (status & PORT_PEC) ? " PEC" : "", ++ (status & PORT_PE) ? " PE" : "", ++ (status & PORT_CSC) ? " CSC" : "", ++ (status & PORT_CONNECT) ? " CONNECT" : ""); ++ ++ return buf; ++} ++ ++/* functions have the "wrong" filename when they're output... */ ++#define dbg_status(fotg210, label, status) { \ ++ char _buf[80]; \ ++ dbg_status_buf(_buf, sizeof(_buf), label, status); \ ++ fotg210_dbg(fotg210, "%s\n", _buf); \ ++} ++ ++#define dbg_cmd(fotg210, label, command) { \ ++ char _buf[80]; \ ++ dbg_command_buf(_buf, sizeof(_buf), label, command); \ ++ fotg210_dbg(fotg210, "%s\n", _buf); \ ++} ++ ++#define dbg_port(fotg210, label, port, status) { \ ++ char _buf[80]; \ ++ fotg210_dbg(fotg210, "%s\n", \ ++ dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ ++} ++ ++/* troubleshooting help: expose state in debugfs */ ++static int debug_async_open(struct inode *, struct file *); ++static int debug_periodic_open(struct inode *, struct file *); ++static int debug_registers_open(struct inode *, struct file *); ++static int debug_async_open(struct inode *, struct file *); ++ ++static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); ++static int debug_close(struct inode *, struct file *); ++ ++static const struct file_operations debug_async_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_async_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++static const struct file_operations debug_periodic_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_periodic_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++static const struct file_operations debug_registers_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_registers_open, ++ .read = debug_output, ++ .release = debug_close, ++ .llseek = default_llseek, ++}; ++ ++static struct dentry *fotg210_debug_root; ++ ++struct debug_buffer { ++ ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ ++ struct usb_bus *bus; ++ struct mutex mutex; /* protect filling of buffer */ ++ size_t count; /* number of characters filled into buffer */ ++ char *output_buf; ++ size_t alloc_size; ++}; ++ ++static inline char speed_char(u32 scratch) ++{ ++ switch (scratch & (3 << 12)) { ++ case QH_FULL_SPEED: ++ return 'f'; ++ ++ case QH_LOW_SPEED: ++ return 'l'; ++ ++ case QH_HIGH_SPEED: ++ return 'h'; ++ ++ default: ++ return '?'; ++ } ++} ++ ++static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) ++{ ++ __u32 v = hc32_to_cpu(fotg210, token); ++ ++ if (v & QTD_STS_ACTIVE) ++ return '*'; ++ if (v & QTD_STS_HALT) ++ return '-'; ++ if (!IS_SHORT_READ(v)) ++ return ' '; ++ /* tries to advance through hw_alt_next */ ++ return '/'; ++} ++ ++static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, ++ char **nextp, unsigned *sizep) ++{ ++ u32 scratch; ++ u32 hw_curr; ++ struct fotg210_qtd *td; ++ unsigned temp; ++ unsigned size = *sizep; ++ char *next = *nextp; ++ char mark; ++ __le32 list_end = FOTG210_LIST_END(fotg210); ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ if (hw->hw_qtd_next == list_end) /* NEC does this */ ++ mark = '@'; ++ else ++ mark = token_mark(fotg210, hw->hw_token); ++ if (mark == '/') { /* qh_alt_next controls qh advance? */ ++ if ((hw->hw_alt_next & QTD_MASK(fotg210)) == ++ fotg210->async->hw->hw_alt_next) ++ mark = '#'; /* blocked */ ++ else if (hw->hw_alt_next == list_end) ++ mark = '.'; /* use hw_qtd_next */ ++ /* else alt_next points to some other qtd */ ++ } ++ scratch = hc32_to_cpup(fotg210, &hw->hw_info1); ++ hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0; ++ temp = scnprintf(next, size, ++ "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)", ++ qh, scratch & 0x007f, ++ speed_char(scratch), ++ (scratch >> 8) & 0x000f, ++ scratch, hc32_to_cpup(fotg210, &hw->hw_info2), ++ hc32_to_cpup(fotg210, &hw->hw_token), mark, ++ (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token) ++ ? "data1" : "data0", ++ (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f); ++ size -= temp; ++ next += temp; ++ ++ /* hc may be modifying the list as we read it ... */ ++ list_for_each_entry(td, &qh->qtd_list, qtd_list) { ++ scratch = hc32_to_cpup(fotg210, &td->hw_token); ++ mark = ' '; ++ if (hw_curr == td->qtd_dma) ++ mark = '*'; ++ else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma)) ++ mark = '+'; ++ else if (QTD_LENGTH(scratch)) { ++ if (td->hw_alt_next == fotg210->async->hw->hw_alt_next) ++ mark = '#'; ++ else if (td->hw_alt_next != list_end) ++ mark = '/'; ++ } ++ temp = snprintf(next, size, ++ "\n\t%p%c%s len=%d %08x urb %p", ++ td, mark, ({ char *tmp; ++ switch ((scratch>>8)&0x03) { ++ case 0: ++ tmp = "out"; ++ break; ++ case 1: ++ tmp = "in"; ++ break; ++ case 2: ++ tmp = "setup"; ++ break; ++ default: ++ tmp = "?"; ++ break; ++ } tmp; }), ++ (scratch >> 16) & 0x7fff, ++ scratch, ++ td->urb); ++ if (size < temp) ++ temp = size; ++ size -= temp; ++ next += temp; ++ if (temp == size) ++ goto done; ++ } ++ ++ temp = snprintf(next, size, "\n"); ++ if (size < temp) ++ temp = size; ++ ++ size -= temp; ++ next += temp; ++ ++done: ++ *sizep = size; ++ *nextp = next; ++} ++ ++static ssize_t fill_async_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ unsigned temp, size; ++ char *next; ++ struct fotg210_qh *qh; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ *next = 0; ++ ++ /* dumps a snapshot of the async schedule. ++ * usually empty except for long-term bulk reads, or head. ++ * one QH per line, and TDs we know about ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (qh = fotg210->async->qh_next.qh; size > 0 && qh; ++ qh = qh->qh_next.qh) ++ qh_lines(fotg210, qh, &next, &size); ++ if (fotg210->async_unlink && size > 0) { ++ temp = scnprintf(next, size, "\nunlink =\n"); ++ size -= temp; ++ next += temp; ++ ++ for (qh = fotg210->async_unlink; size > 0 && qh; ++ qh = qh->unlink_next) ++ qh_lines(fotg210, qh, &next, &size); ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return strlen(buf->output_buf); ++} ++ ++/* count tds, get ep direction */ ++static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, ++ struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) ++{ ++ u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); ++ struct fotg210_qtd *qtd; ++ char *type = ""; ++ unsigned temp = 0; ++ ++ /* count tds, get ep direction */ ++ list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { ++ temp++; ++ switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { ++ case 0: ++ type = "out"; ++ continue; ++ case 1: ++ type = "in"; ++ continue; ++ } ++ } ++ ++ return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", ++ speed_char(scratch), scratch & 0x007f, ++ (scratch >> 8) & 0x000f, type, qh->usecs, ++ qh->c_usecs, temp, (scratch >> 16) & 0x7ff); ++} ++ ++#define DBG_SCHED_LIMIT 64 ++static ssize_t fill_periodic_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ union fotg210_shadow p, *seen; ++ unsigned temp, size, seen_count; ++ char *next; ++ unsigned i; ++ __hc32 tag; ++ ++ seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); ++ if (!seen) ++ return 0; ++ ++ seen_count = 0; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size); ++ size -= temp; ++ next += temp; ++ ++ /* dump a snapshot of the periodic schedule. ++ * iso changes, interrupt usually doesn't. ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (i = 0; i < fotg210->periodic_size; i++) { ++ p = fotg210->pshadow[i]; ++ if (likely(!p.ptr)) ++ continue; ++ ++ tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); ++ ++ temp = scnprintf(next, size, "%4d: ", i); ++ size -= temp; ++ next += temp; ++ ++ do { ++ struct fotg210_qh_hw *hw; ++ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ case Q_TYPE_QH: ++ hw = p.qh->hw; ++ temp = scnprintf(next, size, " qh%d-%04x/%p", ++ p.qh->period, ++ hc32_to_cpup(fotg210, ++ &hw->hw_info2) ++ /* uframe masks */ ++ & (QH_CMASK | QH_SMASK), ++ p.qh); ++ size -= temp; ++ next += temp; ++ /* don't repeat what follows this qh */ ++ for (temp = 0; temp < seen_count; temp++) { ++ if (seen[temp].ptr != p.ptr) ++ continue; ++ if (p.qh->qh_next.ptr) { ++ temp = scnprintf(next, size, ++ " ..."); ++ size -= temp; ++ next += temp; ++ } ++ break; ++ } ++ /* show more info the first time around */ ++ if (temp == seen_count) { ++ temp = output_buf_tds_dir(next, ++ fotg210, hw, ++ p.qh, size); ++ ++ if (seen_count < DBG_SCHED_LIMIT) ++ seen[seen_count++].qh = p.qh; ++ } else ++ temp = 0; ++ tag = Q_NEXT_TYPE(fotg210, hw->hw_next); ++ p = p.qh->qh_next; ++ break; ++ case Q_TYPE_FSTN: ++ temp = scnprintf(next, size, ++ " fstn-%8x/%p", ++ p.fstn->hw_prev, p.fstn); ++ tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); ++ p = p.fstn->fstn_next; ++ break; ++ case Q_TYPE_ITD: ++ temp = scnprintf(next, size, ++ " itd/%p", p.itd); ++ tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); ++ p = p.itd->itd_next; ++ break; ++ } ++ size -= temp; ++ next += temp; ++ } while (p.ptr); ++ ++ temp = scnprintf(next, size, "\n"); ++ size -= temp; ++ next += temp; ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ kfree(seen); ++ ++ return buf->alloc_size - size; ++} ++#undef DBG_SCHED_LIMIT ++ ++static const char *rh_state_string(struct fotg210_hcd *fotg210) ++{ ++ switch (fotg210->rh_state) { ++ case FOTG210_RH_HALTED: ++ return "halted"; ++ case FOTG210_RH_SUSPENDED: ++ return "suspended"; ++ case FOTG210_RH_RUNNING: ++ return "running"; ++ case FOTG210_RH_STOPPING: ++ return "stopping"; ++ } ++ return "?"; ++} ++ ++static ssize_t fill_registers_buffer(struct debug_buffer *buf) ++{ ++ struct usb_hcd *hcd; ++ struct fotg210_hcd *fotg210; ++ unsigned long flags; ++ unsigned temp, size, i; ++ char *next, scratch[80]; ++ static const char fmt[] = "%*s\n"; ++ static const char label[] = ""; ++ ++ hcd = bus_to_hcd(buf->bus); ++ fotg210 = hcd_to_fotg210(hcd); ++ next = buf->output_buf; ++ size = buf->alloc_size; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ if (!HCD_HW_ACCESSIBLE(hcd)) { ++ size = scnprintf(next, size, ++ "bus %s, device %s\n" ++ "%s\n" ++ "SUSPENDED(no register access)\n", ++ hcd->self.controller->bus->name, ++ dev_name(hcd->self.controller), ++ hcd->product_desc); ++ goto done; ++ } ++ ++ /* Capability Registers */ ++ i = HC_VERSION(fotg210, fotg210_readl(fotg210, ++ &fotg210->caps->hc_capbase)); ++ temp = scnprintf(next, size, ++ "bus %s, device %s\n" ++ "%s\n" ++ "EHCI %x.%02x, rh state %s\n", ++ hcd->self.controller->bus->name, ++ dev_name(hcd->self.controller), ++ hcd->product_desc, ++ i >> 8, i & 0x0ff, rh_state_string(fotg210)); ++ size -= temp; ++ next += temp; ++ ++ /* FIXME interpret both types of params */ ++ i = fotg210_readl(fotg210, &fotg210->caps->hcs_params); ++ temp = scnprintf(next, size, "structural params 0x%08x\n", i); ++ size -= temp; ++ next += temp; ++ ++ i = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ temp = scnprintf(next, size, "capability params 0x%08x\n", i); ++ size -= temp; ++ next += temp; ++ ++ /* Operational Registers */ ++ temp = dbg_status_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->status)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = dbg_command_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->command)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = dbg_intr_buf(scratch, sizeof(scratch), label, ++ fotg210_readl(fotg210, &fotg210->regs->intr_enable)); ++ temp = scnprintf(next, size, fmt, temp, scratch); ++ size -= temp; ++ next += temp; ++ ++ temp = scnprintf(next, size, "uframe %04x\n", ++ fotg210_read_frame_index(fotg210)); ++ size -= temp; ++ next += temp; ++ ++ if (fotg210->async_unlink) { ++ temp = scnprintf(next, size, "async unlink qh %p\n", ++ fotg210->async_unlink); ++ size -= temp; ++ next += temp; ++ } ++ ++#ifdef FOTG210_STATS ++ temp = scnprintf(next, size, ++ "irq normal %ld err %ld iaa %ld(lost %ld)\n", ++ fotg210->stats.normal, fotg210->stats.error, ++ fotg210->stats.iaa, fotg210->stats.lost_iaa); ++ size -= temp; ++ next += temp; ++ ++ temp = scnprintf(next, size, "complete %ld unlink %ld\n", ++ fotg210->stats.complete, fotg210->stats.unlink); ++ size -= temp; ++ next += temp; ++#endif ++ ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return buf->alloc_size - size; ++} ++ ++static struct debug_buffer ++*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) ++{ ++ struct debug_buffer *buf; ++ ++ buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); ++ ++ if (buf) { ++ buf->bus = bus; ++ buf->fill_func = fill_func; ++ mutex_init(&buf->mutex); ++ buf->alloc_size = PAGE_SIZE; ++ } ++ ++ return buf; ++} ++ ++static int fill_buffer(struct debug_buffer *buf) ++{ ++ int ret = 0; ++ ++ if (!buf->output_buf) ++ buf->output_buf = vmalloc(buf->alloc_size); ++ ++ if (!buf->output_buf) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = buf->fill_func(buf); ++ ++ if (ret >= 0) { ++ buf->count = ret; ++ ret = 0; ++ } ++ ++out: ++ return ret; ++} ++ ++static ssize_t debug_output(struct file *file, char __user *user_buf, ++ size_t len, loff_t *offset) ++{ ++ struct debug_buffer *buf = file->private_data; ++ int ret = 0; ++ ++ mutex_lock(&buf->mutex); ++ if (buf->count == 0) { ++ ret = fill_buffer(buf); ++ if (ret != 0) { ++ mutex_unlock(&buf->mutex); ++ goto out; ++ } ++ } ++ mutex_unlock(&buf->mutex); ++ ++ ret = simple_read_from_buffer(user_buf, len, offset, ++ buf->output_buf, buf->count); ++ ++out: ++ return ret; ++ ++} ++ ++static int debug_close(struct inode *inode, struct file *file) ++{ ++ struct debug_buffer *buf = file->private_data; ++ ++ if (buf) { ++ vfree(buf->output_buf); ++ kfree(buf); ++ } ++ ++ return 0; ++} ++static int debug_async_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); ++ ++ return file->private_data ? 0 : -ENOMEM; ++} ++ ++static int debug_periodic_open(struct inode *inode, struct file *file) ++{ ++ struct debug_buffer *buf; ++ ++ buf = alloc_buffer(inode->i_private, fill_periodic_buffer); ++ if (!buf) ++ return -ENOMEM; ++ ++ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; ++ file->private_data = buf; ++ return 0; ++} ++ ++static int debug_registers_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = alloc_buffer(inode->i_private, ++ fill_registers_buffer); ++ ++ return file->private_data ? 0 : -ENOMEM; ++} ++ ++static inline void create_debug_files(struct fotg210_hcd *fotg210) ++{ ++ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; ++ struct dentry *root; ++ ++ root = debugfs_create_dir(bus->bus_name, fotg210_debug_root); ++ ++ debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops); ++ debugfs_create_file("periodic", S_IRUGO, root, bus, ++ &debug_periodic_fops); ++ debugfs_create_file("registers", S_IRUGO, root, bus, ++ &debug_registers_fops); ++} ++ ++static inline void remove_debug_files(struct fotg210_hcd *fotg210) ++{ ++ struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; ++ ++ debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root); ++} ++ ++/* handshake - spin reading hc until handshake completes or fails ++ * @ptr: address of hc register to be read ++ * @mask: bits to look at in result of read ++ * @done: value of those bits when handshake succeeds ++ * @usec: timeout in microseconds ++ * ++ * Returns negative errno, or zero on success ++ * ++ * Success happens when the "mask" bits have the specified value (hardware ++ * handshake done). There are two failure modes: "usec" have passed (major ++ * hardware flakeout), or the register reads as all-ones (hardware removed). ++ * ++ * That last failure should_only happen in cases like physical cardbus eject ++ * before driver shutdown. But it also seems to be caused by bugs in cardbus ++ * bridge shutdown: shutting down the bridge before the devices using it. ++ */ ++static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, ++ u32 mask, u32 done, int usec) ++{ ++ u32 result; ++ int ret; ++ ++ ret = readl_poll_timeout_atomic(ptr, result, ++ ((result & mask) == done || ++ result == U32_MAX), 1, usec); ++ if (result == U32_MAX) /* card removed */ ++ return -ENODEV; ++ ++ return ret; ++} ++ ++/* Force HC to halt state from unknown (EHCI spec section 2.3). ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static int fotg210_halt(struct fotg210_hcd *fotg210) ++{ ++ u32 temp; ++ ++ spin_lock_irq(&fotg210->lock); ++ ++ /* disable any irqs left enabled by previous code */ ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ ++ /* ++ * This routine gets called during probe before fotg210->command ++ * has been initialized, so we can't rely on its value. ++ */ ++ fotg210->command &= ~CMD_RUN; ++ temp = fotg210_readl(fotg210, &fotg210->regs->command); ++ temp &= ~(CMD_RUN | CMD_IAAD); ++ fotg210_writel(fotg210, temp, &fotg210->regs->command); ++ ++ spin_unlock_irq(&fotg210->lock); ++ synchronize_irq(fotg210_to_hcd(fotg210)->irq); ++ ++ return handshake(fotg210, &fotg210->regs->status, ++ STS_HALT, STS_HALT, 16 * 125); ++} ++ ++/* Reset a non-running (STS_HALT == 1) controller. ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static int fotg210_reset(struct fotg210_hcd *fotg210) ++{ ++ int retval; ++ u32 command = fotg210_readl(fotg210, &fotg210->regs->command); ++ ++ /* If the EHCI debug controller is active, special care must be ++ * taken before and after a host controller reset ++ */ ++ if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) ++ fotg210->debug = NULL; ++ ++ command |= CMD_RESET; ++ dbg_cmd(fotg210, "reset", command); ++ fotg210_writel(fotg210, command, &fotg210->regs->command); ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210->next_statechange = jiffies; ++ retval = handshake(fotg210, &fotg210->regs->command, ++ CMD_RESET, 0, 250 * 1000); ++ ++ if (retval) ++ return retval; ++ ++ if (fotg210->debug) ++ dbgp_external_startup(fotg210_to_hcd(fotg210)); ++ ++ fotg210->port_c_suspend = fotg210->suspended_ports = ++ fotg210->resuming_ports = 0; ++ return retval; ++} ++ ++/* Idle the controller (turn off the schedules). ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static void fotg210_quiesce(struct fotg210_hcd *fotg210) ++{ ++ u32 temp; ++ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ /* wait for any schedule enables/disables to take effect */ ++ temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); ++ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, ++ 16 * 125); ++ ++ /* then disable anything that's still active */ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->command &= ~(CMD_ASE | CMD_PSE); ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ spin_unlock_irq(&fotg210->lock); ++ ++ /* hardware can take 16 microframes to turn off ... */ ++ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, ++ 16 * 125); ++} ++ ++static void end_unlink_async(struct fotg210_hcd *fotg210); ++static void unlink_empty_async(struct fotg210_hcd *fotg210); ++static void fotg210_work(struct fotg210_hcd *fotg210); ++static void start_unlink_intr(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh); ++static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++/* Set a bit in the USBCMD register */ ++static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) ++{ ++ fotg210->command |= bit; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ ++ /* unblock posted write */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++} ++ ++/* Clear a bit in the USBCMD register */ ++static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) ++{ ++ fotg210->command &= ~bit; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ ++ /* unblock posted write */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++} ++ ++/* EHCI timer support... Now using hrtimers. ++ * ++ * Lots of different events are triggered from fotg210->hrtimer. Whenever ++ * the timer routine runs, it checks each possible event; events that are ++ * currently enabled and whose expiration time has passed get handled. ++ * The set of enabled events is stored as a collection of bitflags in ++ * fotg210->enabled_hrtimer_events, and they are numbered in order of ++ * increasing delay values (ranging between 1 ms and 100 ms). ++ * ++ * Rather than implementing a sorted list or tree of all pending events, ++ * we keep track only of the lowest-numbered pending event, in ++ * fotg210->next_hrtimer_event. Whenever fotg210->hrtimer gets restarted, its ++ * expiration time is set to the timeout value for this event. ++ * ++ * As a result, events might not get handled right away; the actual delay ++ * could be anywhere up to twice the requested delay. This doesn't ++ * matter, because none of the events are especially time-critical. The ++ * ones that matter most all have a delay of 1 ms, so they will be ++ * handled after 2 ms at most, which is okay. In addition to this, we ++ * allow for an expiration range of 1 ms. ++ */ ++ ++/* Delay lengths for the hrtimer event types. ++ * Keep this list sorted by delay length, in the same order as ++ * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. ++ */ ++static unsigned event_delays_ns[] = { ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */ ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */ ++ 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */ ++ 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */ ++ 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */ ++ 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ ++ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */ ++ 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ ++ 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */ ++ 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */ ++}; ++ ++/* Enable a pending hrtimer event */ ++static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, ++ bool resched) ++{ ++ ktime_t *timeout = &fotg210->hr_timeouts[event]; ++ ++ if (resched) ++ *timeout = ktime_add(ktime_get(), event_delays_ns[event]); ++ fotg210->enabled_hrtimer_events |= (1 << event); ++ ++ /* Track only the lowest-numbered pending event */ ++ if (event < fotg210->next_hrtimer_event) { ++ fotg210->next_hrtimer_event = event; ++ hrtimer_start_range_ns(&fotg210->hrtimer, *timeout, ++ NSEC_PER_MSEC, HRTIMER_MODE_ABS); ++ } ++} ++ ++ ++/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ ++static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) ++{ ++ unsigned actual, want; ++ ++ /* Don't enable anything if the controller isn't running (e.g., died) */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ want = (fotg210->command & CMD_ASE) ? STS_ASS : 0; ++ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS; ++ ++ if (want != actual) { ++ ++ /* Poll again later, but give up after about 20 ms */ ++ if (fotg210->ASS_poll_count++ < 20) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, ++ true); ++ return; ++ } ++ fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", ++ want, actual); ++ } ++ fotg210->ASS_poll_count = 0; ++ ++ /* The status is up-to-date; restart or stop the schedule as needed */ ++ if (want == 0) { /* Stopped */ ++ if (fotg210->async_count > 0) ++ fotg210_set_command_bit(fotg210, CMD_ASE); ++ ++ } else { /* Running */ ++ if (fotg210->async_count == 0) { ++ ++ /* Turn off the schedule after a while */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_DISABLE_ASYNC, ++ true); ++ } ++ } ++} ++ ++/* Turn off the async schedule after a brief delay */ ++static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) ++{ ++ fotg210_clear_command_bit(fotg210, CMD_ASE); ++} ++ ++ ++/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ ++static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) ++{ ++ unsigned actual, want; ++ ++ /* Don't do anything if the controller isn't running (e.g., died) */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ want = (fotg210->command & CMD_PSE) ? STS_PSS : 0; ++ actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS; ++ ++ if (want != actual) { ++ ++ /* Poll again later, but give up after about 20 ms */ ++ if (fotg210->PSS_poll_count++ < 20) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, ++ true); ++ return; ++ } ++ fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", ++ want, actual); ++ } ++ fotg210->PSS_poll_count = 0; ++ ++ /* The status is up-to-date; restart or stop the schedule as needed */ ++ if (want == 0) { /* Stopped */ ++ if (fotg210->periodic_count > 0) ++ fotg210_set_command_bit(fotg210, CMD_PSE); ++ ++ } else { /* Running */ ++ if (fotg210->periodic_count == 0) { ++ ++ /* Turn off the schedule after a while */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_DISABLE_PERIODIC, ++ true); ++ } ++ } ++} ++ ++/* Turn off the periodic schedule after a brief delay */ ++static void fotg210_disable_PSE(struct fotg210_hcd *fotg210) ++{ ++ fotg210_clear_command_bit(fotg210, CMD_PSE); ++} ++ ++ ++/* Poll the STS_HALT status bit; see when a dead controller stops */ ++static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) ++{ ++ if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) { ++ ++ /* Give up after a few milliseconds */ ++ if (fotg210->died_poll_count++ < 5) { ++ /* Try again later */ ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_POLL_DEAD, true); ++ return; ++ } ++ fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); ++ } ++ ++ /* Clean up the mess */ ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ fotg210_work(fotg210); ++ end_unlink_async(fotg210); ++ ++ /* Not in process context, so don't try to reset the controller */ ++} ++ ++ ++/* Handle unlinked interrupt QHs once they are gone from the hardware */ ++static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) ++{ ++ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); ++ ++ /* ++ * Process all the QHs on the intr_unlink list that were added ++ * before the current unlink cycle began. The list is in ++ * temporal order, so stop when we reach the first entry in the ++ * current cycle. But if the root hub isn't running then ++ * process all the QHs on the list. ++ */ ++ fotg210->intr_unlinking = true; ++ while (fotg210->intr_unlink) { ++ struct fotg210_qh *qh = fotg210->intr_unlink; ++ ++ if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) ++ break; ++ fotg210->intr_unlink = qh->unlink_next; ++ qh->unlink_next = NULL; ++ end_unlink_intr(fotg210, qh); ++ } ++ ++ /* Handle remaining entries later */ ++ if (fotg210->intr_unlink) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, ++ true); ++ ++fotg210->intr_unlink_cycle; ++ } ++ fotg210->intr_unlinking = false; ++} ++ ++ ++/* Start another free-iTDs/siTDs cycle */ ++static void start_free_itds(struct fotg210_hcd *fotg210) ++{ ++ if (!(fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_FREE_ITDS))) { ++ fotg210->last_itd_to_free = list_entry( ++ fotg210->cached_itd_list.prev, ++ struct fotg210_itd, itd_list); ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true); ++ } ++} ++ ++/* Wait for controller to stop using old iTDs and siTDs */ ++static void end_free_itds(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_itd *itd, *n; ++ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ fotg210->last_itd_to_free = NULL; ++ ++ list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) { ++ list_del(&itd->itd_list); ++ dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma); ++ if (itd == fotg210->last_itd_to_free) ++ break; ++ } ++ ++ if (!list_empty(&fotg210->cached_itd_list)) ++ start_free_itds(fotg210); ++} ++ ++ ++/* Handle lost (or very late) IAA interrupts */ ++static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING) ++ return; ++ ++ /* ++ * Lost IAA irqs wedge things badly; seen first with a vt8235. ++ * So we need this watchdog, but must protect it against both ++ * (a) SMP races against real IAA firing and retriggering, and ++ * (b) clean HC shutdown, when IAA watchdog was pending. ++ */ ++ if (fotg210->async_iaa) { ++ u32 cmd, status; ++ ++ /* If we get here, IAA is *REALLY* late. It's barely ++ * conceivable that the system is so busy that CMD_IAAD ++ * is still legitimately set, so let's be sure it's ++ * clear before we read STS_IAA. (The HC should clear ++ * CMD_IAAD when it sets STS_IAA.) ++ */ ++ cmd = fotg210_readl(fotg210, &fotg210->regs->command); ++ ++ /* ++ * If IAA is set here it either legitimately triggered ++ * after the watchdog timer expired (_way_ late, so we'll ++ * still count it as lost) ... or a silicon erratum: ++ * - VIA seems to set IAA without triggering the IRQ; ++ * - IAAD potentially cleared without setting IAA. ++ */ ++ status = fotg210_readl(fotg210, &fotg210->regs->status); ++ if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { ++ INCR(fotg210->stats.lost_iaa); ++ fotg210_writel(fotg210, STS_IAA, ++ &fotg210->regs->status); ++ } ++ ++ fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", ++ status, cmd); ++ end_unlink_async(fotg210); ++ } ++} ++ ++ ++/* Enable the I/O watchdog, if appropriate */ ++static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) ++{ ++ /* Not needed if the controller isn't running or it's already enabled */ ++ if (fotg210->rh_state != FOTG210_RH_RUNNING || ++ (fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_IO_WATCHDOG))) ++ return; ++ ++ /* ++ * Isochronous transfers always need the watchdog. ++ * For other sorts we use it only if the flag is set. ++ */ ++ if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && ++ fotg210->async_count + fotg210->intr_count > 0)) ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, ++ true); ++} ++ ++ ++/* Handler functions for the hrtimer event types. ++ * Keep this array in the same order as the event types indexed by ++ * enum fotg210_hrtimer_event in fotg210.h. ++ */ ++static void (*event_handlers[])(struct fotg210_hcd *) = { ++ fotg210_poll_ASS, /* FOTG210_HRTIMER_POLL_ASS */ ++ fotg210_poll_PSS, /* FOTG210_HRTIMER_POLL_PSS */ ++ fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */ ++ fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */ ++ end_free_itds, /* FOTG210_HRTIMER_FREE_ITDS */ ++ unlink_empty_async, /* FOTG210_HRTIMER_ASYNC_UNLINKS */ ++ fotg210_iaa_watchdog, /* FOTG210_HRTIMER_IAA_WATCHDOG */ ++ fotg210_disable_PSE, /* FOTG210_HRTIMER_DISABLE_PERIODIC */ ++ fotg210_disable_ASE, /* FOTG210_HRTIMER_DISABLE_ASYNC */ ++ fotg210_work, /* FOTG210_HRTIMER_IO_WATCHDOG */ ++}; ++ ++static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) ++{ ++ struct fotg210_hcd *fotg210 = ++ container_of(t, struct fotg210_hcd, hrtimer); ++ ktime_t now; ++ unsigned long events; ++ unsigned long flags; ++ unsigned e; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ events = fotg210->enabled_hrtimer_events; ++ fotg210->enabled_hrtimer_events = 0; ++ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; ++ ++ /* ++ * Check each pending event. If its time has expired, handle ++ * the event; otherwise re-enable it. ++ */ ++ now = ktime_get(); ++ for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) { ++ if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0) ++ event_handlers[e](fotg210); ++ else ++ fotg210_enable_event(fotg210, e, false); ++ } ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return HRTIMER_NORESTART; ++} ++ ++#define fotg210_bus_suspend NULL ++#define fotg210_bus_resume NULL ++ ++static int check_reset_complete(struct fotg210_hcd *fotg210, int index, ++ u32 __iomem *status_reg, int port_status) ++{ ++ if (!(port_status & PORT_CONNECT)) ++ return port_status; ++ ++ /* if reset finished and it's still not enabled -- handoff */ ++ if (!(port_status & PORT_PE)) ++ /* with integrated TT, there's nobody to hand it to! */ ++ fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", ++ index + 1); ++ else ++ fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", ++ index + 1); ++ ++ return port_status; ++} ++ ++ ++/* build "status change" packet (one or two bytes) from HC registers */ ++ ++static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp, status; ++ u32 mask; ++ int retval = 1; ++ unsigned long flags; ++ ++ /* init status to no-changes */ ++ buf[0] = 0; ++ ++ /* Inform the core about resumes-in-progress by returning ++ * a non-zero value even if there are no status changes. ++ */ ++ status = fotg210->resuming_ports; ++ ++ mask = PORT_CSC | PORT_PEC; ++ /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */ ++ ++ /* no hub change reports (bit 0) for now (power, ...) */ ++ ++ /* port N changes (bit N)? */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ temp = fotg210_readl(fotg210, &fotg210->regs->port_status); ++ ++ /* ++ * Return status information even for ports with OWNER set. ++ * Otherwise hub_wq wouldn't see the disconnect event when a ++ * high-speed device is switched over to the companion ++ * controller by the user. ++ */ ++ ++ if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || ++ (fotg210->reset_done[0] && ++ time_after_eq(jiffies, fotg210->reset_done[0]))) { ++ buf[0] |= 1 << 1; ++ status = STS_PCD; ++ } ++ /* FIXME autosuspend idle root hubs */ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return status ? retval : 0; ++} ++ ++static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, ++ struct usb_hub_descriptor *desc) ++{ ++ int ports = HCS_N_PORTS(fotg210->hcs_params); ++ u16 temp; ++ ++ desc->bDescriptorType = USB_DT_HUB; ++ desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ ++ desc->bHubContrCurrent = 0; ++ ++ desc->bNbrPorts = ports; ++ temp = 1 + (ports / 8); ++ desc->bDescLength = 7 + 2 * temp; ++ ++ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ ++ memset(&desc->u.hs.DeviceRemovable[0], 0, temp); ++ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); ++ ++ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ ++ temp |= HUB_CHAR_NO_LPSM; /* no power switching */ ++ desc->wHubCharacteristics = cpu_to_le16(temp); ++} ++ ++static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ++ u16 wIndex, char *buf, u16 wLength) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ int ports = HCS_N_PORTS(fotg210->hcs_params); ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ u32 temp, temp1, status; ++ unsigned long flags; ++ int retval = 0; ++ unsigned selector; ++ ++ /* ++ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. ++ * HCS_INDICATOR may say we can change LEDs to off/amber/green. ++ * (track current state ourselves) ... blink for diagnostics, ++ * power, "this is the one", etc. EHCI spec supports this. ++ */ ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ switch (typeReq) { ++ case ClearHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case ClearPortFeature: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ temp = fotg210_readl(fotg210, status_reg); ++ temp &= ~PORT_RWC_BITS; ++ ++ /* ++ * Even if OWNER is set, so the port is owned by the ++ * companion controller, hub_wq needs to be able to clear ++ * the port-change status bits (especially ++ * USB_PORT_STAT_C_CONNECTION). ++ */ ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ fotg210_writel(fotg210, temp & ~PORT_PE, status_reg); ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ fotg210_writel(fotg210, temp | PORT_PEC, status_reg); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ if (temp & PORT_RESET) ++ goto error; ++ if (!(temp & PORT_SUSPEND)) ++ break; ++ if ((temp & PORT_PE) == 0) ++ goto error; ++ ++ /* resume signaling for 20 msec */ ++ fotg210_writel(fotg210, temp | PORT_RESUME, status_reg); ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(USB_RESUME_TIMEOUT); ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ clear_bit(wIndex, &fotg210->port_c_suspend); ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ fotg210_writel(fotg210, temp | PORT_CSC, status_reg); ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ fotg210_writel(fotg210, temp | OTGISR_OVC, ++ &fotg210->regs->otgisr); ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* GetPortStatus clears reset */ ++ break; ++ default: ++ goto error; ++ } ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ break; ++ case GetHubDescriptor: ++ fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) ++ buf); ++ break; ++ case GetHubStatus: ++ /* no hub-wide feature/status flags */ ++ memset(buf, 0, 4); ++ /*cpu_to_le32s ((u32 *) buf); */ ++ break; ++ case GetPortStatus: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ status = 0; ++ temp = fotg210_readl(fotg210, status_reg); ++ ++ /* wPortChange bits */ ++ if (temp & PORT_CSC) ++ status |= USB_PORT_STAT_C_CONNECTION << 16; ++ if (temp & PORT_PEC) ++ status |= USB_PORT_STAT_C_ENABLE << 16; ++ ++ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); ++ if (temp1 & OTGISR_OVC) ++ status |= USB_PORT_STAT_C_OVERCURRENT << 16; ++ ++ /* whoever resumes must GetPortStatus to complete it!! */ ++ if (temp & PORT_RESUME) { ++ ++ /* Remote Wakeup received? */ ++ if (!fotg210->reset_done[wIndex]) { ++ /* resume signaling for 20 msec */ ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(20); ++ /* check the port again */ ++ mod_timer(&fotg210_to_hcd(fotg210)->rh_timer, ++ fotg210->reset_done[wIndex]); ++ } ++ ++ /* resume completed? */ ++ else if (time_after_eq(jiffies, ++ fotg210->reset_done[wIndex])) { ++ clear_bit(wIndex, &fotg210->suspended_ports); ++ set_bit(wIndex, &fotg210->port_c_suspend); ++ fotg210->reset_done[wIndex] = 0; ++ ++ /* stop resume signaling */ ++ temp = fotg210_readl(fotg210, status_reg); ++ fotg210_writel(fotg210, temp & ++ ~(PORT_RWC_BITS | PORT_RESUME), ++ status_reg); ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ retval = handshake(fotg210, status_reg, ++ PORT_RESUME, 0, 2000);/* 2ms */ ++ if (retval != 0) { ++ fotg210_err(fotg210, ++ "port %d resume error %d\n", ++ wIndex + 1, retval); ++ goto error; ++ } ++ temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); ++ } ++ } ++ ++ /* whoever resets must GetPortStatus to complete it!! */ ++ if ((temp & PORT_RESET) && time_after_eq(jiffies, ++ fotg210->reset_done[wIndex])) { ++ status |= USB_PORT_STAT_C_RESET << 16; ++ fotg210->reset_done[wIndex] = 0; ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ ++ /* force reset to complete */ ++ fotg210_writel(fotg210, ++ temp & ~(PORT_RWC_BITS | PORT_RESET), ++ status_reg); ++ /* REVISIT: some hardware needs 550+ usec to clear ++ * this bit; seems too long to spin routinely... ++ */ ++ retval = handshake(fotg210, status_reg, ++ PORT_RESET, 0, 1000); ++ if (retval != 0) { ++ fotg210_err(fotg210, "port %d reset error %d\n", ++ wIndex + 1, retval); ++ goto error; ++ } ++ ++ /* see what we found out */ ++ temp = check_reset_complete(fotg210, wIndex, status_reg, ++ fotg210_readl(fotg210, status_reg)); ++ ++ /* restart schedule */ ++ fotg210->command |= CMD_RUN; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ } ++ ++ if (!(temp & (PORT_RESUME|PORT_RESET))) { ++ fotg210->reset_done[wIndex] = 0; ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ } ++ ++ /* transfer dedicated ports to the companion hc */ ++ if ((temp & PORT_CONNECT) && ++ test_bit(wIndex, &fotg210->companion_ports)) { ++ temp &= ~PORT_RWC_BITS; ++ fotg210_writel(fotg210, temp, status_reg); ++ fotg210_dbg(fotg210, "port %d --> companion\n", ++ wIndex + 1); ++ temp = fotg210_readl(fotg210, status_reg); ++ } ++ ++ /* ++ * Even if OWNER is set, there's no harm letting hub_wq ++ * see the wPortStatus values (they should all be 0 except ++ * for PORT_POWER anyway). ++ */ ++ ++ if (temp & PORT_CONNECT) { ++ status |= USB_PORT_STAT_CONNECTION; ++ status |= fotg210_port_speed(fotg210, temp); ++ } ++ if (temp & PORT_PE) ++ status |= USB_PORT_STAT_ENABLE; ++ ++ /* maybe the port was unsuspended without our knowledge */ ++ if (temp & (PORT_SUSPEND|PORT_RESUME)) { ++ status |= USB_PORT_STAT_SUSPEND; ++ } else if (test_bit(wIndex, &fotg210->suspended_ports)) { ++ clear_bit(wIndex, &fotg210->suspended_ports); ++ clear_bit(wIndex, &fotg210->resuming_ports); ++ fotg210->reset_done[wIndex] = 0; ++ if (temp & PORT_PE) ++ set_bit(wIndex, &fotg210->port_c_suspend); ++ } ++ ++ temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr); ++ if (temp1 & OTGISR_OVC) ++ status |= USB_PORT_STAT_OVERCURRENT; ++ if (temp & PORT_RESET) ++ status |= USB_PORT_STAT_RESET; ++ if (test_bit(wIndex, &fotg210->port_c_suspend)) ++ status |= USB_PORT_STAT_C_SUSPEND << 16; ++ ++ if (status & ~0xffff) /* only if wPortChange is interesting */ ++ dbg_port(fotg210, "GetStatus", wIndex + 1, temp); ++ put_unaligned_le32(status, buf); ++ break; ++ case SetHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case SetPortFeature: ++ selector = wIndex >> 8; ++ wIndex &= 0xff; ++ ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ temp = fotg210_readl(fotg210, status_reg); ++ temp &= ~PORT_RWC_BITS; ++ switch (wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ if ((temp & PORT_PE) == 0 ++ || (temp & PORT_RESET) != 0) ++ goto error; ++ ++ /* After above check the port must be connected. ++ * Set appropriate bit thus could put phy into low power ++ * mode if we have hostpc feature ++ */ ++ fotg210_writel(fotg210, temp | PORT_SUSPEND, ++ status_reg); ++ set_bit(wIndex, &fotg210->suspended_ports); ++ break; ++ case USB_PORT_FEAT_RESET: ++ if (temp & PORT_RESUME) ++ goto error; ++ /* line status bits may report this as low speed, ++ * which can be fine if this root hub has a ++ * transaction translator built in. ++ */ ++ fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); ++ temp |= PORT_RESET; ++ temp &= ~PORT_PE; ++ ++ /* ++ * caller must wait, then call GetPortStatus ++ * usb 2.0 spec says 50 ms resets on root ++ */ ++ fotg210->reset_done[wIndex] = jiffies ++ + msecs_to_jiffies(50); ++ fotg210_writel(fotg210, temp, status_reg); ++ break; ++ ++ /* For downstream facing ports (these): one hub port is put ++ * into test mode according to USB2 11.24.2.13, then the hub ++ * must be reset (which for root hub now means rmmod+modprobe, ++ * or else system reboot). See EHCI 2.3.9 and 4.14 for info ++ * about the EHCI-specific stuff. ++ */ ++ case USB_PORT_FEAT_TEST: ++ if (!selector || selector > 5) ++ goto error; ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ fotg210_quiesce(fotg210); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ /* Put all enabled ports into suspend */ ++ temp = fotg210_readl(fotg210, status_reg) & ++ ~PORT_RWC_BITS; ++ if (temp & PORT_PE) ++ fotg210_writel(fotg210, temp | PORT_SUSPEND, ++ status_reg); ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ fotg210_halt(fotg210); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ temp = fotg210_readl(fotg210, status_reg); ++ temp |= selector << 16; ++ fotg210_writel(fotg210, temp, status_reg); ++ break; ++ ++ default: ++ goto error; ++ } ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ break; ++ ++ default: ++error: ++ /* "stall" on error */ ++ retval = -EPIPE; ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return retval; ++} ++ ++static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd, ++ int portnum) ++{ ++ return; ++} ++ ++static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, ++ int portnum) ++{ ++ return 0; ++} ++ ++/* There's basically three types of memory: ++ * - data used only by the HCD ... kmalloc is fine ++ * - async and periodic schedules, shared by HC and HCD ... these ++ * need to use dma_pool or dma_alloc_coherent ++ * - driver buffers, read/written by HC ... single shot DMA mapped ++ * ++ * There's also "register" data (e.g. PCI or SOC), which is memory mapped. ++ * No memory seen by this driver is pageable. ++ */ ++ ++/* Allocate the key transfer structures from the previously allocated pool */ ++static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, ++ struct fotg210_qtd *qtd, dma_addr_t dma) ++{ ++ memset(qtd, 0, sizeof(*qtd)); ++ qtd->qtd_dma = dma; ++ qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); ++ qtd->hw_next = FOTG210_LIST_END(fotg210); ++ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); ++ INIT_LIST_HEAD(&qtd->qtd_list); ++} ++ ++static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, ++ gfp_t flags) ++{ ++ struct fotg210_qtd *qtd; ++ dma_addr_t dma; ++ ++ qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); ++ if (qtd != NULL) ++ fotg210_qtd_init(fotg210, qtd, dma); ++ ++ return qtd; ++} ++ ++static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, ++ struct fotg210_qtd *qtd) ++{ ++ dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); ++} ++ ++ ++static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ /* clean qtds first, and know this is not linked */ ++ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { ++ fotg210_dbg(fotg210, "unused qh not empty!\n"); ++ BUG(); ++ } ++ if (qh->dummy) ++ fotg210_qtd_free(fotg210, qh->dummy); ++ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); ++ kfree(qh); ++} ++ ++static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, ++ gfp_t flags) ++{ ++ struct fotg210_qh *qh; ++ dma_addr_t dma; ++ ++ qh = kzalloc(sizeof(*qh), GFP_ATOMIC); ++ if (!qh) ++ goto done; ++ qh->hw = (struct fotg210_qh_hw *) ++ dma_pool_zalloc(fotg210->qh_pool, flags, &dma); ++ if (!qh->hw) ++ goto fail; ++ qh->qh_dma = dma; ++ INIT_LIST_HEAD(&qh->qtd_list); ++ ++ /* dummy td enables safe urb queuing */ ++ qh->dummy = fotg210_qtd_alloc(fotg210, flags); ++ if (qh->dummy == NULL) { ++ fotg210_dbg(fotg210, "no dummy td\n"); ++ goto fail1; ++ } ++done: ++ return qh; ++fail1: ++ dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma); ++fail: ++ kfree(qh); ++ return NULL; ++} ++ ++/* The queue heads and transfer descriptors are managed from pools tied ++ * to each of the "per device" structures. ++ * This is the initialisation and cleanup code. ++ */ ++ ++static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->async) ++ qh_destroy(fotg210, fotg210->async); ++ fotg210->async = NULL; ++ ++ if (fotg210->dummy) ++ qh_destroy(fotg210, fotg210->dummy); ++ fotg210->dummy = NULL; ++ ++ /* DMA consistent memory and pools */ ++ dma_pool_destroy(fotg210->qtd_pool); ++ fotg210->qtd_pool = NULL; ++ ++ dma_pool_destroy(fotg210->qh_pool); ++ fotg210->qh_pool = NULL; ++ ++ dma_pool_destroy(fotg210->itd_pool); ++ fotg210->itd_pool = NULL; ++ ++ if (fotg210->periodic) ++ dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, ++ fotg210->periodic_size * sizeof(u32), ++ fotg210->periodic, fotg210->periodic_dma); ++ fotg210->periodic = NULL; ++ ++ /* shadow periodic table */ ++ kfree(fotg210->pshadow); ++ fotg210->pshadow = NULL; ++} ++ ++/* remember to add cleanup code (above) if you add anything here */ ++static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) ++{ ++ int i; ++ ++ /* QTDs for control/bulk/intr transfers */ ++ fotg210->qtd_pool = dma_pool_create("fotg210_qtd", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_qtd), ++ 32 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->qtd_pool) ++ goto fail; ++ ++ /* QHs for control/bulk/intr transfers */ ++ fotg210->qh_pool = dma_pool_create("fotg210_qh", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_qh_hw), ++ 32 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->qh_pool) ++ goto fail; ++ ++ fotg210->async = fotg210_qh_alloc(fotg210, flags); ++ if (!fotg210->async) ++ goto fail; ++ ++ /* ITD for high speed ISO transfers */ ++ fotg210->itd_pool = dma_pool_create("fotg210_itd", ++ fotg210_to_hcd(fotg210)->self.controller, ++ sizeof(struct fotg210_itd), ++ 64 /* byte alignment (for hw parts) */, ++ 4096 /* can't cross 4K */); ++ if (!fotg210->itd_pool) ++ goto fail; ++ ++ /* Hardware periodic table */ ++ fotg210->periodic = ++ dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, ++ fotg210->periodic_size * sizeof(__le32), ++ &fotg210->periodic_dma, 0); ++ if (fotg210->periodic == NULL) ++ goto fail; ++ ++ for (i = 0; i < fotg210->periodic_size; i++) ++ fotg210->periodic[i] = FOTG210_LIST_END(fotg210); ++ ++ /* software shadow of hardware table */ ++ fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), ++ flags); ++ if (fotg210->pshadow != NULL) ++ return 0; ++ ++fail: ++ fotg210_dbg(fotg210, "couldn't init memory\n"); ++ fotg210_mem_cleanup(fotg210); ++ return -ENOMEM; ++} ++/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. ++ * ++ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" ++ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned ++ * buffers needed for the larger number). We use one QH per endpoint, queue ++ * multiple urbs (all three types) per endpoint. URBs may need several qtds. ++ * ++ * ISO traffic uses "ISO TD" (itd) records, and (along with ++ * interrupts) needs careful scheduling. Performance improvements can be ++ * an ongoing challenge. That's in "ehci-sched.c". ++ * ++ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, ++ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using ++ * (b) special fields in qh entries or (c) split iso entries. TTs will ++ * buffer low/full speed data so the host collects it at high speed. ++ */ ++ ++/* fill a qtd, returning how much of the buffer we were able to queue up */ ++static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, ++ dma_addr_t buf, size_t len, int token, int maxpacket) ++{ ++ int i, count; ++ u64 addr = buf; ++ ++ /* one buffer entry per 4K ... first might be short or unaligned */ ++ qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); ++ qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32)); ++ count = 0x1000 - (buf & 0x0fff); /* rest of that page */ ++ if (likely(len < count)) /* ... iff needed */ ++ count = len; ++ else { ++ buf += 0x1000; ++ buf &= ~0x0fff; ++ ++ /* per-qtd limit: from 16K to 20K (best alignment) */ ++ for (i = 1; count < len && i < 5; i++) { ++ addr = buf; ++ qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr); ++ qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210, ++ (u32)(addr >> 32)); ++ buf += 0x1000; ++ if ((count + 0x1000) < len) ++ count += 0x1000; ++ else ++ count = len; ++ } ++ ++ /* short packets may only terminate transfers */ ++ if (count != len) ++ count -= (count % maxpacket); ++ } ++ qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token); ++ qtd->length = count; ++ ++ return count; ++} ++ ++static inline void qh_update(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh, struct fotg210_qtd *qtd) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ /* writes to an active overlay are unsafe */ ++ BUG_ON(qh->qh_state != QH_STATE_IDLE); ++ ++ hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ hw->hw_alt_next = FOTG210_LIST_END(fotg210); ++ ++ /* Except for control endpoints, we make hardware maintain data ++ * toggle (like OHCI) ... here (re)initialize the toggle in the QH, ++ * and set the pseudo-toggle in udev. Only usb_clear_halt() will ++ * ever clear it. ++ */ ++ if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { ++ unsigned is_out, epnum; ++ ++ is_out = qh->is_out; ++ epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; ++ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { ++ hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE); ++ usb_settoggle(qh->dev, epnum, is_out, 1); ++ } ++ } ++ ++ hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING); ++} ++ ++/* if it weren't for a common silicon quirk (writing the dummy into the qh ++ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault ++ * recovery (including urb dequeue) would need software changes to a QH... ++ */ ++static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qtd *qtd; ++ ++ if (list_empty(&qh->qtd_list)) ++ qtd = qh->dummy; ++ else { ++ qtd = list_entry(qh->qtd_list.next, ++ struct fotg210_qtd, qtd_list); ++ /* ++ * first qtd may already be partially processed. ++ * If we come here during unlink, the QH overlay region ++ * might have reference to the just unlinked qtd. The ++ * qtd is updated in qh_completions(). Update the QH ++ * overlay here. ++ */ ++ if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) { ++ qh->hw->hw_qtd_next = qtd->hw_next; ++ qtd = NULL; ++ } ++ } ++ ++ if (qtd) ++ qh_update(fotg210, qh, qtd); ++} ++ ++static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh = ep->hcpriv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh->clearing_tt = 0; ++ if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) ++ && fotg210->rh_state == FOTG210_RH_RUNNING) ++ qh_link_async(fotg210, qh); ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh, struct urb *urb, u32 token) ++{ ++ ++ /* If an async split transaction gets an error or is unlinked, ++ * the TT buffer may be left in an indeterminate state. We ++ * have to clear the TT buffer. ++ * ++ * Note: this routine is never called for Isochronous transfers. ++ */ ++ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { ++ struct usb_device *tt = urb->dev->tt->hub; ++ ++ dev_dbg(&tt->dev, ++ "clear tt buffer port %d, a%d ep%d t%08x\n", ++ urb->dev->ttport, urb->dev->devnum, ++ usb_pipeendpoint(urb->pipe), token); ++ ++ if (urb->dev->tt->hub != ++ fotg210_to_hcd(fotg210)->self.root_hub) { ++ if (usb_hub_clear_tt_buffer(urb) == 0) ++ qh->clearing_tt = 1; ++ } ++ } ++} ++ ++static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, ++ size_t length, u32 token) ++{ ++ int status = -EINPROGRESS; ++ ++ /* count IN/OUT bytes, not SETUP (even short packets) */ ++ if (likely(QTD_PID(token) != 2)) ++ urb->actual_length += length - QTD_LENGTH(token); ++ ++ /* don't modify error codes */ ++ if (unlikely(urb->unlinked)) ++ return status; ++ ++ /* force cleanup after short read; not always an error */ ++ if (unlikely(IS_SHORT_READ(token))) ++ status = -EREMOTEIO; ++ ++ /* serious "can't proceed" faults reported by the hardware */ ++ if (token & QTD_STS_HALT) { ++ if (token & QTD_STS_BABBLE) { ++ /* FIXME "must" disable babbling device's port too */ ++ status = -EOVERFLOW; ++ /* CERR nonzero + halt --> stall */ ++ } else if (QTD_CERR(token)) { ++ status = -EPIPE; ++ ++ /* In theory, more than one of the following bits can be set ++ * since they are sticky and the transaction is retried. ++ * Which to test first is rather arbitrary. ++ */ ++ } else if (token & QTD_STS_MMF) { ++ /* fs/ls interrupt xfer missed the complete-split */ ++ status = -EPROTO; ++ } else if (token & QTD_STS_DBE) { ++ status = (QTD_PID(token) == 1) /* IN ? */ ++ ? -ENOSR /* hc couldn't read data */ ++ : -ECOMM; /* hc couldn't write data */ ++ } else if (token & QTD_STS_XACT) { ++ /* timeout, bad CRC, wrong PID, etc */ ++ fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", ++ urb->dev->devpath, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out"); ++ status = -EPROTO; ++ } else { /* unknown */ ++ status = -EPROTO; ++ } ++ ++ fotg210_dbg(fotg210, ++ "dev%d ep%d%s qtd token %08x --> status %d\n", ++ usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ token, status); ++ } ++ ++ return status; ++} ++ ++static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, ++ int status) ++__releases(fotg210->lock) ++__acquires(fotg210->lock) ++{ ++ if (likely(urb->hcpriv != NULL)) { ++ struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; ++ ++ /* S-mask in a QH means it's an interrupt urb */ ++ if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { ++ ++ /* ... update hc-wide periodic stats (for usbfs) */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--; ++ } ++ } ++ ++ if (unlikely(urb->unlinked)) { ++ INCR(fotg210->stats.unlink); ++ } else { ++ /* report non-error and short read status as zero */ ++ if (status == -EINPROGRESS || status == -EREMOTEIO) ++ status = 0; ++ INCR(fotg210->stats.complete); ++ } ++ ++#ifdef FOTG210_URB_TRACE ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s status %d len %d/%d\n", ++ __func__, urb->dev->devpath, urb, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ status, ++ urb->actual_length, urb->transfer_buffer_length); ++#endif ++ ++ /* complete() can reenter this HCD */ ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++ spin_unlock(&fotg210->lock); ++ usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status); ++ spin_lock(&fotg210->lock); ++} ++ ++static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); ++ ++/* Process and free completed qtds for a qh, returning URBs to drivers. ++ * Chases up to qh->hw_current. Returns number of completions called, ++ * indicating how much "real" work we did. ++ */ ++static unsigned qh_completions(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ struct fotg210_qtd *last, *end = qh->dummy; ++ struct fotg210_qtd *qtd, *tmp; ++ int last_status; ++ int stopped; ++ unsigned count = 0; ++ u8 state; ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ if (unlikely(list_empty(&qh->qtd_list))) ++ return count; ++ ++ /* completions (or tasks on other cpus) must never clobber HALT ++ * till we've gone through and cleaned everything up, even when ++ * they add urbs to this qh's queue or mark them for unlinking. ++ * ++ * NOTE: unlinking expects to be done in queue order. ++ * ++ * It's a bug for qh->qh_state to be anything other than ++ * QH_STATE_IDLE, unless our caller is scan_async() or ++ * scan_intr(). ++ */ ++ state = qh->qh_state; ++ qh->qh_state = QH_STATE_COMPLETING; ++ stopped = (state == QH_STATE_IDLE); ++ ++rescan: ++ last = NULL; ++ last_status = -EINPROGRESS; ++ qh->needs_rescan = 0; ++ ++ /* remove de-activated QTDs from front of queue. ++ * after faults (including short reads), cleanup this urb ++ * then let the queue advance. ++ * if queue is stopped, handles unlinks. ++ */ ++ list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { ++ struct urb *urb; ++ u32 token = 0; ++ ++ urb = qtd->urb; ++ ++ /* clean up any state from previous QTD ...*/ ++ if (last) { ++ if (likely(last->urb != urb)) { ++ fotg210_urb_done(fotg210, last->urb, ++ last_status); ++ count++; ++ last_status = -EINPROGRESS; ++ } ++ fotg210_qtd_free(fotg210, last); ++ last = NULL; ++ } ++ ++ /* ignore urbs submitted during completions we reported */ ++ if (qtd == end) ++ break; ++ ++ /* hardware copies qtd out of qh overlay */ ++ rmb(); ++ token = hc32_to_cpu(fotg210, qtd->hw_token); ++ ++ /* always clean up qtds the hc de-activated */ ++retry_xacterr: ++ if ((token & QTD_STS_ACTIVE) == 0) { ++ ++ /* Report Data Buffer Error: non-fatal but useful */ ++ if (token & QTD_STS_DBE) ++ fotg210_dbg(fotg210, ++ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", ++ urb, usb_endpoint_num(&urb->ep->desc), ++ usb_endpoint_dir_in(&urb->ep->desc) ++ ? "in" : "out", ++ urb->transfer_buffer_length, qtd, qh); ++ ++ /* on STALL, error, and short reads this urb must ++ * complete and all its qtds must be recycled. ++ */ ++ if ((token & QTD_STS_HALT) != 0) { ++ ++ /* retry transaction errors until we ++ * reach the software xacterr limit ++ */ ++ if ((token & QTD_STS_XACT) && ++ QTD_CERR(token) == 0 && ++ ++qh->xacterrs < QH_XACTERR_MAX && ++ !urb->unlinked) { ++ fotg210_dbg(fotg210, ++ "detected XactErr len %zu/%zu retry %d\n", ++ qtd->length - QTD_LENGTH(token), ++ qtd->length, ++ qh->xacterrs); ++ ++ /* reset the token in the qtd and the ++ * qh overlay (which still contains ++ * the qtd) so that we pick up from ++ * where we left off ++ */ ++ token &= ~QTD_STS_HALT; ++ token |= QTD_STS_ACTIVE | ++ (FOTG210_TUNE_CERR << 10); ++ qtd->hw_token = cpu_to_hc32(fotg210, ++ token); ++ wmb(); ++ hw->hw_token = cpu_to_hc32(fotg210, ++ token); ++ goto retry_xacterr; ++ } ++ stopped = 1; ++ ++ /* magic dummy for some short reads; qh won't advance. ++ * that silicon quirk can kick in with this dummy too. ++ * ++ * other short reads won't stop the queue, including ++ * control transfers (status stage handles that) or ++ * most other single-qtd reads ... the queue stops if ++ * URB_SHORT_NOT_OK was set so the driver submitting ++ * the urbs could clean it up. ++ */ ++ } else if (IS_SHORT_READ(token) && ++ !(qtd->hw_alt_next & ++ FOTG210_LIST_END(fotg210))) { ++ stopped = 1; ++ } ++ ++ /* stop scanning when we reach qtds the hc is using */ ++ } else if (likely(!stopped ++ && fotg210->rh_state >= FOTG210_RH_RUNNING)) { ++ break; ++ ++ /* scan the whole queue for unlinks whenever it stops */ ++ } else { ++ stopped = 1; ++ ++ /* cancel everything if we halt, suspend, etc */ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ last_status = -ESHUTDOWN; ++ ++ /* this qtd is active; skip it unless a previous qtd ++ * for its urb faulted, or its urb was canceled. ++ */ ++ else if (last_status == -EINPROGRESS && !urb->unlinked) ++ continue; ++ ++ /* qh unlinked; token in overlay may be most current */ ++ if (state == QH_STATE_IDLE && ++ cpu_to_hc32(fotg210, qtd->qtd_dma) ++ == hw->hw_current) { ++ token = hc32_to_cpu(fotg210, hw->hw_token); ++ ++ /* An unlink may leave an incomplete ++ * async transaction in the TT buffer. ++ * We have to clear it. ++ */ ++ fotg210_clear_tt_buffer(fotg210, qh, urb, ++ token); ++ } ++ } ++ ++ /* unless we already know the urb's status, collect qtd status ++ * and update count of bytes transferred. in common short read ++ * cases with only one data qtd (including control transfers), ++ * queue processing won't halt. but with two or more qtds (for ++ * example, with a 32 KB transfer), when the first qtd gets a ++ * short read the second must be removed by hand. ++ */ ++ if (last_status == -EINPROGRESS) { ++ last_status = qtd_copy_status(fotg210, urb, ++ qtd->length, token); ++ if (last_status == -EREMOTEIO && ++ (qtd->hw_alt_next & ++ FOTG210_LIST_END(fotg210))) ++ last_status = -EINPROGRESS; ++ ++ /* As part of low/full-speed endpoint-halt processing ++ * we must clear the TT buffer (11.17.5). ++ */ ++ if (unlikely(last_status != -EINPROGRESS && ++ last_status != -EREMOTEIO)) { ++ /* The TT's in some hubs malfunction when they ++ * receive this request following a STALL (they ++ * stop sending isochronous packets). Since a ++ * STALL can't leave the TT buffer in a busy ++ * state (if you believe Figures 11-48 - 11-51 ++ * in the USB 2.0 spec), we won't clear the TT ++ * buffer in this case. Strictly speaking this ++ * is a violation of the spec. ++ */ ++ if (last_status != -EPIPE) ++ fotg210_clear_tt_buffer(fotg210, qh, ++ urb, token); ++ } ++ } ++ ++ /* if we're removing something not at the queue head, ++ * patch the hardware queue pointer. ++ */ ++ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { ++ last = list_entry(qtd->qtd_list.prev, ++ struct fotg210_qtd, qtd_list); ++ last->hw_next = qtd->hw_next; ++ } ++ ++ /* remove qtd; it's recycled after possible urb completion */ ++ list_del(&qtd->qtd_list); ++ last = qtd; ++ ++ /* reinit the xacterr counter for the next qtd */ ++ qh->xacterrs = 0; ++ } ++ ++ /* last urb's completion might still need calling */ ++ if (likely(last != NULL)) { ++ fotg210_urb_done(fotg210, last->urb, last_status); ++ count++; ++ fotg210_qtd_free(fotg210, last); ++ } ++ ++ /* Do we need to rescan for URBs dequeued during a giveback? */ ++ if (unlikely(qh->needs_rescan)) { ++ /* If the QH is already unlinked, do the rescan now. */ ++ if (state == QH_STATE_IDLE) ++ goto rescan; ++ ++ /* Otherwise we have to wait until the QH is fully unlinked. ++ * Our caller will start an unlink if qh->needs_rescan is ++ * set. But if an unlink has already started, nothing needs ++ * to be done. ++ */ ++ if (state != QH_STATE_LINKED) ++ qh->needs_rescan = 0; ++ } ++ ++ /* restore original state; caller must unlink or relink */ ++ qh->qh_state = state; ++ ++ /* be sure the hardware's done with the qh before refreshing ++ * it after fault cleanup, or recovering from silicon wrongly ++ * overlaying the dummy qtd (which reduces DMA chatter). ++ */ ++ if (stopped != 0 || hw->hw_qtd_next == FOTG210_LIST_END(fotg210)) { ++ switch (state) { ++ case QH_STATE_IDLE: ++ qh_refresh(fotg210, qh); ++ break; ++ case QH_STATE_LINKED: ++ /* We won't refresh a QH that's linked (after the HC ++ * stopped the queue). That avoids a race: ++ * - HC reads first part of QH; ++ * - CPU updates that first part and the token; ++ * - HC reads rest of that QH, including token ++ * Result: HC gets an inconsistent image, and then ++ * DMAs to/from the wrong memory (corrupting it). ++ * ++ * That should be rare for interrupt transfers, ++ * except maybe high bandwidth ... ++ */ ++ ++ /* Tell the caller to start an unlink */ ++ qh->needs_rescan = 1; ++ break; ++ /* otherwise, unlink already started */ ++ } ++ } ++ ++ return count; ++} ++ ++/* reverse of qh_urb_transaction: free a list of TDs. ++ * used for cleanup after errors, before HC sees an URB's TDs. ++ */ ++static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *head) ++{ ++ struct fotg210_qtd *qtd, *temp; ++ ++ list_for_each_entry_safe(qtd, temp, head, qtd_list) { ++ list_del(&qtd->qtd_list); ++ fotg210_qtd_free(fotg210, qtd); ++ } ++} ++ ++/* create a list of filled qtds for this URB; won't link into qh. ++ */ ++static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, ++ struct urb *urb, struct list_head *head, gfp_t flags) ++{ ++ struct fotg210_qtd *qtd, *qtd_prev; ++ dma_addr_t buf; ++ int len, this_sg_len, maxpacket; ++ int is_input; ++ u32 token; ++ int i; ++ struct scatterlist *sg; ++ ++ /* ++ * URBs map to sequences of QTDs: one logical transaction ++ */ ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ return NULL; ++ list_add_tail(&qtd->qtd_list, head); ++ qtd->urb = urb; ++ ++ token = QTD_STS_ACTIVE; ++ token |= (FOTG210_TUNE_CERR << 10); ++ /* for split transactions, SplitXState initialized to zero */ ++ ++ len = urb->transfer_buffer_length; ++ is_input = usb_pipein(urb->pipe); ++ if (usb_pipecontrol(urb->pipe)) { ++ /* SETUP pid */ ++ qtd_fill(fotg210, qtd, urb->setup_dma, ++ sizeof(struct usb_ctrlrequest), ++ token | (2 /* "setup" */ << 8), 8); ++ ++ /* ... and always at least one more pid */ ++ token ^= QTD_TOGGLE; ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ /* for zero length DATA stages, STATUS is always IN */ ++ if (len == 0) ++ token |= (1 /* "in" */ << 8); ++ } ++ ++ /* ++ * data transfer stage: buffer setup ++ */ ++ i = urb->num_mapped_sgs; ++ if (len > 0 && i > 0) { ++ sg = urb->sg; ++ buf = sg_dma_address(sg); ++ ++ /* urb->transfer_buffer_length may be smaller than the ++ * size of the scatterlist (or vice versa) ++ */ ++ this_sg_len = min_t(int, sg_dma_len(sg), len); ++ } else { ++ sg = NULL; ++ buf = urb->transfer_dma; ++ this_sg_len = len; ++ } ++ ++ if (is_input) ++ token |= (1 /* "in" */ << 8); ++ /* else it's already initted to "out" pid (0 << 8) */ ++ ++ maxpacket = usb_maxpacket(urb->dev, urb->pipe); ++ ++ /* ++ * buffer gets wrapped in one or more qtds; ++ * last one may be "short" (including zero len) ++ * and may serve as a control status ack ++ */ ++ for (;;) { ++ int this_qtd_len; ++ ++ this_qtd_len = qtd_fill(fotg210, qtd, buf, this_sg_len, token, ++ maxpacket); ++ this_sg_len -= this_qtd_len; ++ len -= this_qtd_len; ++ buf += this_qtd_len; ++ ++ /* ++ * short reads advance to a "magic" dummy instead of the next ++ * qtd ... that forces the queue to stop, for manual cleanup. ++ * (this will usually be overridden later.) ++ */ ++ if (is_input) ++ qtd->hw_alt_next = fotg210->async->hw->hw_alt_next; ++ ++ /* qh makes control packets use qtd toggle; maybe switch it */ ++ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) ++ token ^= QTD_TOGGLE; ++ ++ if (likely(this_sg_len <= 0)) { ++ if (--i <= 0 || len <= 0) ++ break; ++ sg = sg_next(sg); ++ buf = sg_dma_address(sg); ++ this_sg_len = min_t(int, sg_dma_len(sg), len); ++ } ++ ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ } ++ ++ /* ++ * unless the caller requires manual cleanup after short reads, ++ * have the alt_next mechanism keep the queue running after the ++ * last data qtd (the only one, for control and most other cases). ++ */ ++ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || ++ usb_pipecontrol(urb->pipe))) ++ qtd->hw_alt_next = FOTG210_LIST_END(fotg210); ++ ++ /* ++ * control requests may need a terminating data "status" ack; ++ * other OUT ones may need a terminating short packet ++ * (zero length). ++ */ ++ if (likely(urb->transfer_buffer_length != 0)) { ++ int one_more = 0; ++ ++ if (usb_pipecontrol(urb->pipe)) { ++ one_more = 1; ++ token ^= 0x0100; /* "in" <--> "out" */ ++ token |= QTD_TOGGLE; /* force DATA1 */ ++ } else if (usb_pipeout(urb->pipe) ++ && (urb->transfer_flags & URB_ZERO_PACKET) ++ && !(urb->transfer_buffer_length % maxpacket)) { ++ one_more = 1; ++ } ++ if (one_more) { ++ qtd_prev = qtd; ++ qtd = fotg210_qtd_alloc(fotg210, flags); ++ if (unlikely(!qtd)) ++ goto cleanup; ++ qtd->urb = urb; ++ qtd_prev->hw_next = QTD_NEXT(fotg210, qtd->qtd_dma); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ /* never any data in such packets */ ++ qtd_fill(fotg210, qtd, 0, 0, token, 0); ++ } ++ } ++ ++ /* by default, enable interrupt on urb completion */ ++ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) ++ qtd->hw_token |= cpu_to_hc32(fotg210, QTD_IOC); ++ return head; ++ ++cleanup: ++ qtd_list_free(fotg210, urb, head); ++ return NULL; ++} ++ ++/* Would be best to create all qh's from config descriptors, ++ * when each interface/altsetting is established. Unlink ++ * any previous qh and cancel its urbs first; endpoints are ++ * implicitly reset then (data toggle too). ++ * That'd mean updating how usbcore talks to HCDs. (2.7?) ++ */ ++ ++ ++/* Each QH holds a qtd list; a QH is used for everything except iso. ++ * ++ * For interrupt urbs, the scheduler must set the microframe scheduling ++ * mask(s) each time the QH gets scheduled. For highspeed, that's ++ * just one microframe in the s-mask. For split interrupt transactions ++ * there are additional complications: c-mask, maybe FSTNs. ++ */ ++static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, ++ gfp_t flags) ++{ ++ struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); ++ struct usb_host_endpoint *ep; ++ u32 info1 = 0, info2 = 0; ++ int is_input, type; ++ int maxp = 0; ++ int mult; ++ struct usb_tt *tt = urb->dev->tt; ++ struct fotg210_qh_hw *hw; ++ ++ if (!qh) ++ return qh; ++ ++ /* ++ * init endpoint/device data for this QH ++ */ ++ info1 |= usb_pipeendpoint(urb->pipe) << 8; ++ info1 |= usb_pipedevice(urb->pipe) << 0; ++ ++ is_input = usb_pipein(urb->pipe); ++ type = usb_pipetype(urb->pipe); ++ ep = usb_pipe_endpoint(urb->dev, urb->pipe); ++ maxp = usb_endpoint_maxp(&ep->desc); ++ mult = usb_endpoint_maxp_mult(&ep->desc); ++ ++ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth ++ * acts like up to 3KB, but is built from smaller packets. ++ */ ++ if (maxp > 1024) { ++ fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); ++ goto done; ++ } ++ ++ /* Compute interrupt scheduling parameters just once, and save. ++ * - allowing for high bandwidth, how many nsec/uframe are used? ++ * - split transactions need a second CSPLIT uframe; same question ++ * - splits also need a schedule gap (for full/low speed I/O) ++ * - qh has a polling interval ++ * ++ * For control/bulk requests, the HC or TT handles these. ++ */ ++ if (type == PIPE_INTERRUPT) { ++ qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, ++ is_input, 0, mult * maxp)); ++ qh->start = NO_FRAME; ++ ++ if (urb->dev->speed == USB_SPEED_HIGH) { ++ qh->c_usecs = 0; ++ qh->gap_uf = 0; ++ ++ qh->period = urb->interval >> 3; ++ if (qh->period == 0 && urb->interval != 1) { ++ /* NOTE interval 2 or 4 uframes could work. ++ * But interval 1 scheduling is simpler, and ++ * includes high bandwidth. ++ */ ++ urb->interval = 1; ++ } else if (qh->period > fotg210->periodic_size) { ++ qh->period = fotg210->periodic_size; ++ urb->interval = qh->period << 3; ++ } ++ } else { ++ int think_time; ++ ++ /* gap is f(FS/LS transfer times) */ ++ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, ++ is_input, 0, maxp) / (125 * 1000); ++ ++ /* FIXME this just approximates SPLIT/CSPLIT times */ ++ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ ++ qh->c_usecs = qh->usecs + HS_USECS(0); ++ qh->usecs = HS_USECS(1); ++ } else { /* SPLIT+DATA, gap, CSPLIT */ ++ qh->usecs += HS_USECS(1); ++ qh->c_usecs = HS_USECS(0); ++ } ++ ++ think_time = tt ? tt->think_time : 0; ++ qh->tt_usecs = NS_TO_US(think_time + ++ usb_calc_bus_time(urb->dev->speed, ++ is_input, 0, maxp)); ++ qh->period = urb->interval; ++ if (qh->period > fotg210->periodic_size) { ++ qh->period = fotg210->periodic_size; ++ urb->interval = qh->period; ++ } ++ } ++ } ++ ++ /* support for tt scheduling, and access to toggles */ ++ qh->dev = urb->dev; ++ ++ /* using TT? */ ++ switch (urb->dev->speed) { ++ case USB_SPEED_LOW: ++ info1 |= QH_LOW_SPEED; ++ fallthrough; ++ ++ case USB_SPEED_FULL: ++ /* EPS 0 means "full" */ ++ if (type != PIPE_INTERRUPT) ++ info1 |= (FOTG210_TUNE_RL_TT << 28); ++ if (type == PIPE_CONTROL) { ++ info1 |= QH_CONTROL_EP; /* for TT */ ++ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ ++ } ++ info1 |= maxp << 16; ++ ++ info2 |= (FOTG210_TUNE_MULT_TT << 30); ++ ++ /* Some Freescale processors have an erratum in which the ++ * port number in the queue head was 0..N-1 instead of 1..N. ++ */ ++ if (fotg210_has_fsl_portno_bug(fotg210)) ++ info2 |= (urb->dev->ttport-1) << 23; ++ else ++ info2 |= urb->dev->ttport << 23; ++ ++ /* set the address of the TT; for TDI's integrated ++ * root hub tt, leave it zeroed. ++ */ ++ if (tt && tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) ++ info2 |= tt->hub->devnum << 16; ++ ++ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ ++ ++ break; ++ ++ case USB_SPEED_HIGH: /* no TT involved */ ++ info1 |= QH_HIGH_SPEED; ++ if (type == PIPE_CONTROL) { ++ info1 |= (FOTG210_TUNE_RL_HS << 28); ++ info1 |= 64 << 16; /* usb2 fixed maxpacket */ ++ info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ ++ info2 |= (FOTG210_TUNE_MULT_HS << 30); ++ } else if (type == PIPE_BULK) { ++ info1 |= (FOTG210_TUNE_RL_HS << 28); ++ /* The USB spec says that high speed bulk endpoints ++ * always use 512 byte maxpacket. But some device ++ * vendors decided to ignore that, and MSFT is happy ++ * to help them do so. So now people expect to use ++ * such nonconformant devices with Linux too; sigh. ++ */ ++ info1 |= maxp << 16; ++ info2 |= (FOTG210_TUNE_MULT_HS << 30); ++ } else { /* PIPE_INTERRUPT */ ++ info1 |= maxp << 16; ++ info2 |= mult << 30; ++ } ++ break; ++ default: ++ fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, ++ urb->dev->speed); ++done: ++ qh_destroy(fotg210, qh); ++ return NULL; ++ } ++ ++ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ ++ ++ /* init as live, toggle clear, advance to dummy */ ++ qh->qh_state = QH_STATE_IDLE; ++ hw = qh->hw; ++ hw->hw_info1 = cpu_to_hc32(fotg210, info1); ++ hw->hw_info2 = cpu_to_hc32(fotg210, info2); ++ qh->is_out = !is_input; ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); ++ qh_refresh(fotg210, qh); ++ return qh; ++} ++ ++static void enable_async(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->async_count++) ++ return; ++ ++ /* Stop waiting to turn off the async schedule */ ++ fotg210->enabled_hrtimer_events &= ~BIT(FOTG210_HRTIMER_DISABLE_ASYNC); ++ ++ /* Don't start the schedule until ASS is 0 */ ++ fotg210_poll_ASS(fotg210); ++ turn_on_io_watchdog(fotg210); ++} ++ ++static void disable_async(struct fotg210_hcd *fotg210) ++{ ++ if (--fotg210->async_count) ++ return; ++ ++ /* The async schedule and async_unlink list are supposed to be empty */ ++ WARN_ON(fotg210->async->qh_next.qh || fotg210->async_unlink); ++ ++ /* Don't turn off the schedule until ASS is 1 */ ++ fotg210_poll_ASS(fotg210); ++} ++ ++/* move qh (and its qtds) onto async queue; maybe enable queue. */ ++ ++static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); ++ struct fotg210_qh *head; ++ ++ /* Don't link a QH if there's a Clear-TT-Buffer pending */ ++ if (unlikely(qh->clearing_tt)) ++ return; ++ ++ WARN_ON(qh->qh_state != QH_STATE_IDLE); ++ ++ /* clear halt and/or toggle; and maybe recover from silicon quirk */ ++ qh_refresh(fotg210, qh); ++ ++ /* splice right after start */ ++ head = fotg210->async; ++ qh->qh_next = head->qh_next; ++ qh->hw->hw_next = head->hw->hw_next; ++ wmb(); ++ ++ head->qh_next.qh = qh; ++ head->hw->hw_next = dma; ++ ++ qh->xacterrs = 0; ++ qh->qh_state = QH_STATE_LINKED; ++ /* qtd completions reported later by interrupt */ ++ ++ enable_async(fotg210); ++} ++ ++/* For control/bulk/interrupt, return QH with these TDs appended. ++ * Allocates and initializes the QH if necessary. ++ * Returns null if it can't allocate a QH it needs to. ++ * If the QH has TDs (urbs) already, that's great. ++ */ ++static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, ++ struct urb *urb, struct list_head *qtd_list, ++ int epnum, void **ptr) ++{ ++ struct fotg210_qh *qh = NULL; ++ __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); ++ ++ qh = (struct fotg210_qh *) *ptr; ++ if (unlikely(qh == NULL)) { ++ /* can't sleep here, we have fotg210->lock... */ ++ qh = qh_make(fotg210, urb, GFP_ATOMIC); ++ *ptr = qh; ++ } ++ if (likely(qh != NULL)) { ++ struct fotg210_qtd *qtd; ++ ++ if (unlikely(list_empty(qtd_list))) ++ qtd = NULL; ++ else ++ qtd = list_entry(qtd_list->next, struct fotg210_qtd, ++ qtd_list); ++ ++ /* control qh may need patching ... */ ++ if (unlikely(epnum == 0)) { ++ /* usb_reset_device() briefly reverts to address 0 */ ++ if (usb_pipedevice(urb->pipe) == 0) ++ qh->hw->hw_info1 &= ~qh_addr_mask; ++ } ++ ++ /* just one way to queue requests: swap with the dummy qtd. ++ * only hc or qh_refresh() ever modify the overlay. ++ */ ++ if (likely(qtd != NULL)) { ++ struct fotg210_qtd *dummy; ++ dma_addr_t dma; ++ __hc32 token; ++ ++ /* to avoid racing the HC, use the dummy td instead of ++ * the first td of our list (becomes new dummy). both ++ * tds stay deactivated until we're done, when the ++ * HC is allowed to fetch the old dummy (4.10.2). ++ */ ++ token = qtd->hw_token; ++ qtd->hw_token = HALT_BIT(fotg210); ++ ++ dummy = qh->dummy; ++ ++ dma = dummy->qtd_dma; ++ *dummy = *qtd; ++ dummy->qtd_dma = dma; ++ ++ list_del(&qtd->qtd_list); ++ list_add(&dummy->qtd_list, qtd_list); ++ list_splice_tail(qtd_list, &qh->qtd_list); ++ ++ fotg210_qtd_init(fotg210, qtd, qtd->qtd_dma); ++ qh->dummy = qtd; ++ ++ /* hc must see the new dummy at list end */ ++ dma = qtd->qtd_dma; ++ qtd = list_entry(qh->qtd_list.prev, ++ struct fotg210_qtd, qtd_list); ++ qtd->hw_next = QTD_NEXT(fotg210, dma); ++ ++ /* let the hc process these next qtds */ ++ wmb(); ++ dummy->hw_token = token; ++ ++ urb->hcpriv = qh; ++ } ++ } ++ return qh; ++} ++ ++static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *qtd_list, gfp_t mem_flags) ++{ ++ int epnum; ++ unsigned long flags; ++ struct fotg210_qh *qh = NULL; ++ int rc; ++ ++ epnum = urb->ep->desc.bEndpointAddress; ++ ++#ifdef FOTG210_URB_TRACE ++ { ++ struct fotg210_qtd *qtd; ++ ++ qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", ++ __func__, urb->dev->devpath, urb, ++ epnum & 0x0f, (epnum & USB_DIR_IN) ++ ? "in" : "out", ++ urb->transfer_buffer_length, ++ qtd, urb->ep->hcpriv); ++ } ++#endif ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ rc = -ESHUTDOWN; ++ goto done; ++ } ++ rc = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(rc)) ++ goto done; ++ ++ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); ++ if (unlikely(qh == NULL)) { ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ /* Control/bulk operations through TTs don't need scheduling, ++ * the HC and TT handle it when the TT has a buffer ready. ++ */ ++ if (likely(qh->qh_state == QH_STATE_IDLE)) ++ qh_link_async(fotg210, qh); ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ if (unlikely(qh == NULL)) ++ qtd_list_free(fotg210, urb, qtd_list); ++ return rc; ++} ++ ++static void single_unlink_async(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ struct fotg210_qh *prev; ++ ++ /* Add to the end of the list of QHs waiting for the next IAAD */ ++ qh->qh_state = QH_STATE_UNLINK; ++ if (fotg210->async_unlink) ++ fotg210->async_unlink_last->unlink_next = qh; ++ else ++ fotg210->async_unlink = qh; ++ fotg210->async_unlink_last = qh; ++ ++ /* Unlink it from the schedule */ ++ prev = fotg210->async; ++ while (prev->qh_next.qh != qh) ++ prev = prev->qh_next.qh; ++ ++ prev->hw->hw_next = qh->hw->hw_next; ++ prev->qh_next = qh->qh_next; ++ if (fotg210->qh_scan_next == qh) ++ fotg210->qh_scan_next = qh->qh_next.qh; ++} ++ ++static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) ++{ ++ /* ++ * Do nothing if an IAA cycle is already running or ++ * if one will be started shortly. ++ */ ++ if (fotg210->async_iaa || fotg210->async_unlinking) ++ return; ++ ++ /* Do all the waiting QHs at once */ ++ fotg210->async_iaa = fotg210->async_unlink; ++ fotg210->async_unlink = NULL; ++ ++ /* If the controller isn't running, we don't have to wait for it */ ++ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) { ++ if (!nested) /* Avoid recursion */ ++ end_unlink_async(fotg210); ++ ++ /* Otherwise start a new IAA cycle */ ++ } else if (likely(fotg210->rh_state == FOTG210_RH_RUNNING)) { ++ /* Make sure the unlinks are all visible to the hardware */ ++ wmb(); ++ ++ fotg210_writel(fotg210, fotg210->command | CMD_IAAD, ++ &fotg210->regs->command); ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, ++ true); ++ } ++} ++ ++/* the async qh for the qtds being unlinked are now gone from the HC */ ++ ++static void end_unlink_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ ++ /* Process the idle QHs */ ++restart: ++ fotg210->async_unlinking = true; ++ while (fotg210->async_iaa) { ++ qh = fotg210->async_iaa; ++ fotg210->async_iaa = qh->unlink_next; ++ qh->unlink_next = NULL; ++ ++ qh->qh_state = QH_STATE_IDLE; ++ qh->qh_next.qh = NULL; ++ ++ qh_completions(fotg210, qh); ++ if (!list_empty(&qh->qtd_list) && ++ fotg210->rh_state == FOTG210_RH_RUNNING) ++ qh_link_async(fotg210, qh); ++ disable_async(fotg210); ++ } ++ fotg210->async_unlinking = false; ++ ++ /* Start a new IAA cycle if any QHs are waiting for it */ ++ if (fotg210->async_unlink) { ++ start_iaa_cycle(fotg210, true); ++ if (unlikely(fotg210->rh_state < FOTG210_RH_RUNNING)) ++ goto restart; ++ } ++} ++ ++static void unlink_empty_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh, *next; ++ bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); ++ bool check_unlinks_later = false; ++ ++ /* Unlink all the async QHs that have been empty for a timer cycle */ ++ next = fotg210->async->qh_next.qh; ++ while (next) { ++ qh = next; ++ next = qh->qh_next.qh; ++ ++ if (list_empty(&qh->qtd_list) && ++ qh->qh_state == QH_STATE_LINKED) { ++ if (!stopped && qh->unlink_cycle == ++ fotg210->async_unlink_cycle) ++ check_unlinks_later = true; ++ else ++ single_unlink_async(fotg210, qh); ++ } ++ } ++ ++ /* Start a new IAA cycle if any QHs are waiting for it */ ++ if (fotg210->async_unlink) ++ start_iaa_cycle(fotg210, false); ++ ++ /* QHs that haven't been empty for long enough will be handled later */ ++ if (check_unlinks_later) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, ++ true); ++ ++fotg210->async_unlink_cycle; ++ } ++} ++ ++/* makes sure the async qh will become idle */ ++/* caller must own fotg210->lock */ ++ ++static void start_unlink_async(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ /* ++ * If the QH isn't linked then there's nothing we can do ++ * unless we were called during a giveback, in which case ++ * qh_completions() has to deal with it. ++ */ ++ if (qh->qh_state != QH_STATE_LINKED) { ++ if (qh->qh_state == QH_STATE_COMPLETING) ++ qh->needs_rescan = 1; ++ return; ++ } ++ ++ single_unlink_async(fotg210, qh); ++ start_iaa_cycle(fotg210, false); ++} ++ ++static void scan_async(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ bool check_unlinks_later = false; ++ ++ fotg210->qh_scan_next = fotg210->async->qh_next.qh; ++ while (fotg210->qh_scan_next) { ++ qh = fotg210->qh_scan_next; ++ fotg210->qh_scan_next = qh->qh_next.qh; ++rescan: ++ /* clean any finished work for this qh */ ++ if (!list_empty(&qh->qtd_list)) { ++ int temp; ++ ++ /* ++ * Unlinks could happen here; completion reporting ++ * drops the lock. That's why fotg210->qh_scan_next ++ * always holds the next qh to scan; if the next qh ++ * gets unlinked then fotg210->qh_scan_next is adjusted ++ * in single_unlink_async(). ++ */ ++ temp = qh_completions(fotg210, qh); ++ if (qh->needs_rescan) { ++ start_unlink_async(fotg210, qh); ++ } else if (list_empty(&qh->qtd_list) ++ && qh->qh_state == QH_STATE_LINKED) { ++ qh->unlink_cycle = fotg210->async_unlink_cycle; ++ check_unlinks_later = true; ++ } else if (temp != 0) ++ goto rescan; ++ } ++ } ++ ++ /* ++ * Unlink empty entries, reducing DMA usage as well ++ * as HCD schedule-scanning costs. Delay for any qh ++ * we just scanned, there's a not-unusual case that it ++ * doesn't stay idle for long. ++ */ ++ if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && ++ !(fotg210->enabled_hrtimer_events & ++ BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { ++ fotg210_enable_event(fotg210, ++ FOTG210_HRTIMER_ASYNC_UNLINKS, true); ++ ++fotg210->async_unlink_cycle; ++ } ++} ++/* EHCI scheduled transaction support: interrupt, iso, split iso ++ * These are called "periodic" transactions in the EHCI spec. ++ * ++ * Note that for interrupt transfers, the QH/QTD manipulation is shared ++ * with the "asynchronous" transaction support (control/bulk transfers). ++ * The only real difference is in how interrupt transfers are scheduled. ++ * ++ * For ISO, we make an "iso_stream" head to serve the same role as a QH. ++ * It keeps track of every ITD (or SITD) that's linked, and holds enough ++ * pre-calculated schedule data to make appending to the queue be quick. ++ */ ++static int fotg210_get_frame(struct usb_hcd *hcd); ++ ++/* periodic_next_shadow - return "next" pointer on shadow list ++ * @periodic: host pointer to qh/itd ++ * @tag: hardware tag for type of this record ++ */ ++static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, ++ union fotg210_shadow *periodic, __hc32 tag) ++{ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ case Q_TYPE_QH: ++ return &periodic->qh->qh_next; ++ case Q_TYPE_FSTN: ++ return &periodic->fstn->fstn_next; ++ default: ++ return &periodic->itd->itd_next; ++ } ++} ++ ++static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, ++ union fotg210_shadow *periodic, __hc32 tag) ++{ ++ switch (hc32_to_cpu(fotg210, tag)) { ++ /* our fotg210_shadow.qh is actually software part */ ++ case Q_TYPE_QH: ++ return &periodic->qh->hw->hw_next; ++ /* others are hw parts */ ++ default: ++ return periodic->hw_next; ++ } ++} ++ ++/* caller must hold fotg210->lock */ ++static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, ++ void *ptr) ++{ ++ union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow here = *prev_p; ++ ++ /* find predecessor of "ptr"; hw and shadow lists are in sync */ ++ while (here.ptr && here.ptr != ptr) { ++ prev_p = periodic_next_shadow(fotg210, prev_p, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ hw_p = shadow_next_periodic(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ here = *prev_p; ++ } ++ /* an interrupt entry (at list end) could have been shared */ ++ if (!here.ptr) ++ return; ++ ++ /* update shadow and hardware lists ... the old "next" pointers ++ * from ptr may still be in use, the caller updates them. ++ */ ++ *prev_p = *periodic_next_shadow(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++ ++ *hw_p = *shadow_next_periodic(fotg210, &here, ++ Q_NEXT_TYPE(fotg210, *hw_p)); ++} ++ ++/* how many of the uframe's 125 usecs are allocated? */ ++static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, ++ unsigned frame, unsigned uframe) ++{ ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow *q = &fotg210->pshadow[frame]; ++ unsigned usecs = 0; ++ struct fotg210_qh_hw *hw; ++ ++ while (q->ptr) { ++ switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { ++ case Q_TYPE_QH: ++ hw = q->qh->hw; ++ /* is it in the S-mask? */ ++ if (hw->hw_info2 & cpu_to_hc32(fotg210, 1 << uframe)) ++ usecs += q->qh->usecs; ++ /* ... or C-mask? */ ++ if (hw->hw_info2 & cpu_to_hc32(fotg210, ++ 1 << (8 + uframe))) ++ usecs += q->qh->c_usecs; ++ hw_p = &hw->hw_next; ++ q = &q->qh->qh_next; ++ break; ++ /* case Q_TYPE_FSTN: */ ++ default: ++ /* for "save place" FSTNs, count the relevant INTR ++ * bandwidth from the previous frame ++ */ ++ if (q->fstn->hw_prev != FOTG210_LIST_END(fotg210)) ++ fotg210_dbg(fotg210, "ignoring FSTN cost ...\n"); ++ ++ hw_p = &q->fstn->hw_next; ++ q = &q->fstn->fstn_next; ++ break; ++ case Q_TYPE_ITD: ++ if (q->itd->hw_transaction[uframe]) ++ usecs += q->itd->stream->usecs; ++ hw_p = &q->itd->hw_next; ++ q = &q->itd->itd_next; ++ break; ++ } ++ } ++ if (usecs > fotg210->uframe_periodic_max) ++ fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", ++ frame * 8 + uframe, usecs); ++ return usecs; ++} ++ ++static int same_tt(struct usb_device *dev1, struct usb_device *dev2) ++{ ++ if (!dev1->tt || !dev2->tt) ++ return 0; ++ if (dev1->tt != dev2->tt) ++ return 0; ++ if (dev1->tt->multi) ++ return dev1->ttport == dev2->ttport; ++ else ++ return 1; ++} ++ ++/* return true iff the device's transaction translator is available ++ * for a periodic transfer starting at the specified frame, using ++ * all the uframes in the mask. ++ */ ++static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, ++ struct usb_device *dev, unsigned frame, u32 uf_mask) ++{ ++ if (period == 0) /* error */ ++ return 0; ++ ++ /* note bandwidth wastage: split never follows csplit ++ * (different dev or endpoint) until the next uframe. ++ * calling convention doesn't make that distinction. ++ */ ++ for (; frame < fotg210->periodic_size; frame += period) { ++ union fotg210_shadow here; ++ __hc32 type; ++ struct fotg210_qh_hw *hw; ++ ++ here = fotg210->pshadow[frame]; ++ type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); ++ while (here.ptr) { ++ switch (hc32_to_cpu(fotg210, type)) { ++ case Q_TYPE_ITD: ++ type = Q_NEXT_TYPE(fotg210, here.itd->hw_next); ++ here = here.itd->itd_next; ++ continue; ++ case Q_TYPE_QH: ++ hw = here.qh->hw; ++ if (same_tt(dev, here.qh->dev)) { ++ u32 mask; ++ ++ mask = hc32_to_cpu(fotg210, ++ hw->hw_info2); ++ /* "knows" no gap is needed */ ++ mask |= mask >> 8; ++ if (mask & uf_mask) ++ break; ++ } ++ type = Q_NEXT_TYPE(fotg210, hw->hw_next); ++ here = here.qh->qh_next; ++ continue; ++ /* case Q_TYPE_FSTN: */ ++ default: ++ fotg210_dbg(fotg210, ++ "periodic frame %d bogus type %d\n", ++ frame, type); ++ } ++ ++ /* collision or error */ ++ return 0; ++ } ++ } ++ ++ /* no collision */ ++ return 1; ++} ++ ++static void enable_periodic(struct fotg210_hcd *fotg210) ++{ ++ if (fotg210->periodic_count++) ++ return; ++ ++ /* Stop waiting to turn off the periodic schedule */ ++ fotg210->enabled_hrtimer_events &= ++ ~BIT(FOTG210_HRTIMER_DISABLE_PERIODIC); ++ ++ /* Don't start the schedule until PSS is 0 */ ++ fotg210_poll_PSS(fotg210); ++ turn_on_io_watchdog(fotg210); ++} ++ ++static void disable_periodic(struct fotg210_hcd *fotg210) ++{ ++ if (--fotg210->periodic_count) ++ return; ++ ++ /* Don't turn off the schedule until PSS is 1 */ ++ fotg210_poll_PSS(fotg210); ++} ++ ++/* periodic schedule slots have iso tds (normal or split) first, then a ++ * sparse tree for active interrupt transfers. ++ * ++ * this just links in a qh; caller guarantees uframe masks are set right. ++ * no FSTN support (yet; fotg210 0.96+) ++ */ ++static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ unsigned i; ++ unsigned period = qh->period; ++ ++ dev_dbg(&qh->dev->dev, ++ "link qh%d-%04x/%p start %d [%d/%d us]\n", period, ++ hc32_to_cpup(fotg210, &qh->hw->hw_info2) & ++ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, ++ qh->c_usecs); ++ ++ /* high bandwidth, or otherwise every microframe */ ++ if (period == 0) ++ period = 1; ++ ++ for (i = qh->start; i < fotg210->periodic_size; i += period) { ++ union fotg210_shadow *prev = &fotg210->pshadow[i]; ++ __hc32 *hw_p = &fotg210->periodic[i]; ++ union fotg210_shadow here = *prev; ++ __hc32 type = 0; ++ ++ /* skip the iso nodes at list head */ ++ while (here.ptr) { ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) ++ break; ++ prev = periodic_next_shadow(fotg210, prev, type); ++ hw_p = shadow_next_periodic(fotg210, &here, type); ++ here = *prev; ++ } ++ ++ /* sorting each branch by period (slow-->fast) ++ * enables sharing interior tree nodes ++ */ ++ while (here.ptr && qh != here.qh) { ++ if (qh->period > here.qh->period) ++ break; ++ prev = &here.qh->qh_next; ++ hw_p = &here.qh->hw->hw_next; ++ here = *prev; ++ } ++ /* link in this qh, unless some earlier pass did that */ ++ if (qh != here.qh) { ++ qh->qh_next = here; ++ if (here.qh) ++ qh->hw->hw_next = *hw_p; ++ wmb(); ++ prev->qh = qh; ++ *hw_p = QH_NEXT(fotg210, qh->qh_dma); ++ } ++ } ++ qh->qh_state = QH_STATE_LINKED; ++ qh->xacterrs = 0; ++ ++ /* update per-qh bandwidth for usbfs */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated += qh->period ++ ? ((qh->usecs + qh->c_usecs) / qh->period) ++ : (qh->usecs * 8); ++ ++ list_add(&qh->intr_node, &fotg210->intr_qh_list); ++ ++ /* maybe enable periodic schedule processing */ ++ ++fotg210->intr_count; ++ enable_periodic(fotg210); ++} ++ ++static void qh_unlink_periodic(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ unsigned i; ++ unsigned period; ++ ++ /* ++ * If qh is for a low/full-speed device, simply unlinking it ++ * could interfere with an ongoing split transaction. To unlink ++ * it safely would require setting the QH_INACTIVATE bit and ++ * waiting at least one frame, as described in EHCI 4.12.2.5. ++ * ++ * We won't bother with any of this. Instead, we assume that the ++ * only reason for unlinking an interrupt QH while the current URB ++ * is still active is to dequeue all the URBs (flush the whole ++ * endpoint queue). ++ * ++ * If rebalancing the periodic schedule is ever implemented, this ++ * approach will no longer be valid. ++ */ ++ ++ /* high bandwidth, or otherwise part of every microframe */ ++ period = qh->period; ++ if (!period) ++ period = 1; ++ ++ for (i = qh->start; i < fotg210->periodic_size; i += period) ++ periodic_unlink(fotg210, i, qh); ++ ++ /* update per-qh bandwidth for usbfs */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= qh->period ++ ? ((qh->usecs + qh->c_usecs) / qh->period) ++ : (qh->usecs * 8); ++ ++ dev_dbg(&qh->dev->dev, ++ "unlink qh%d-%04x/%p start %d [%d/%d us]\n", ++ qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & ++ (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, ++ qh->c_usecs); ++ ++ /* qh->qh_next still "live" to HC */ ++ qh->qh_state = QH_STATE_UNLINK; ++ qh->qh_next.ptr = NULL; ++ ++ if (fotg210->qh_scan_next == qh) ++ fotg210->qh_scan_next = list_entry(qh->intr_node.next, ++ struct fotg210_qh, intr_node); ++ list_del(&qh->intr_node); ++} ++ ++static void start_unlink_intr(struct fotg210_hcd *fotg210, ++ struct fotg210_qh *qh) ++{ ++ /* If the QH isn't linked then there's nothing we can do ++ * unless we were called during a giveback, in which case ++ * qh_completions() has to deal with it. ++ */ ++ if (qh->qh_state != QH_STATE_LINKED) { ++ if (qh->qh_state == QH_STATE_COMPLETING) ++ qh->needs_rescan = 1; ++ return; ++ } ++ ++ qh_unlink_periodic(fotg210, qh); ++ ++ /* Make sure the unlinks are visible before starting the timer */ ++ wmb(); ++ ++ /* ++ * The EHCI spec doesn't say how long it takes the controller to ++ * stop accessing an unlinked interrupt QH. The timer delay is ++ * 9 uframes; presumably that will be long enough. ++ */ ++ qh->unlink_cycle = fotg210->intr_unlink_cycle; ++ ++ /* New entries go at the end of the intr_unlink list */ ++ if (fotg210->intr_unlink) ++ fotg210->intr_unlink_last->unlink_next = qh; ++ else ++ fotg210->intr_unlink = qh; ++ fotg210->intr_unlink_last = qh; ++ ++ if (fotg210->intr_unlinking) ++ ; /* Avoid recursive calls */ ++ else if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ fotg210_handle_intr_unlinks(fotg210); ++ else if (fotg210->intr_unlink == qh) { ++ fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, ++ true); ++ ++fotg210->intr_unlink_cycle; ++ } ++} ++ ++static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ struct fotg210_qh_hw *hw = qh->hw; ++ int rc; ++ ++ qh->qh_state = QH_STATE_IDLE; ++ hw->hw_next = FOTG210_LIST_END(fotg210); ++ ++ qh_completions(fotg210, qh); ++ ++ /* reschedule QH iff another request is queued */ ++ if (!list_empty(&qh->qtd_list) && ++ fotg210->rh_state == FOTG210_RH_RUNNING) { ++ rc = qh_schedule(fotg210, qh); ++ ++ /* An error here likely indicates handshake failure ++ * or no space left in the schedule. Neither fault ++ * should happen often ... ++ * ++ * FIXME kill the now-dysfunctional queued urbs ++ */ ++ if (rc != 0) ++ fotg210_err(fotg210, "can't reschedule qh %p, err %d\n", ++ qh, rc); ++ } ++ ++ /* maybe turn off periodic schedule */ ++ --fotg210->intr_count; ++ disable_periodic(fotg210); ++} ++ ++static int check_period(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned uframe, unsigned period, unsigned usecs) ++{ ++ int claimed; ++ ++ /* complete split running into next frame? ++ * given FSTN support, we could sometimes check... ++ */ ++ if (uframe >= 8) ++ return 0; ++ ++ /* convert "usecs we need" to "max already claimed" */ ++ usecs = fotg210->uframe_periodic_max - usecs; ++ ++ /* we "know" 2 and 4 uframe intervals were rejected; so ++ * for period 0, check _every_ microframe in the schedule. ++ */ ++ if (unlikely(period == 0)) { ++ do { ++ for (uframe = 0; uframe < 7; uframe++) { ++ claimed = periodic_usecs(fotg210, frame, ++ uframe); ++ if (claimed > usecs) ++ return 0; ++ } ++ } while ((frame += 1) < fotg210->periodic_size); ++ ++ /* just check the specified uframe, at that period */ ++ } else { ++ do { ++ claimed = periodic_usecs(fotg210, frame, uframe); ++ if (claimed > usecs) ++ return 0; ++ } while ((frame += period) < fotg210->periodic_size); ++ } ++ ++ /* success! */ ++ return 1; ++} ++ ++static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) ++{ ++ int retval = -ENOSPC; ++ u8 mask = 0; ++ ++ if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ ++ goto done; ++ ++ if (!check_period(fotg210, frame, uframe, qh->period, qh->usecs)) ++ goto done; ++ if (!qh->c_usecs) { ++ retval = 0; ++ *c_maskp = 0; ++ goto done; ++ } ++ ++ /* Make sure this tt's buffer is also available for CSPLITs. ++ * We pessimize a bit; probably the typical full speed case ++ * doesn't need the second CSPLIT. ++ * ++ * NOTE: both SPLIT and CSPLIT could be checked in just ++ * one smart pass... ++ */ ++ mask = 0x03 << (uframe + qh->gap_uf); ++ *c_maskp = cpu_to_hc32(fotg210, mask << 8); ++ ++ mask |= 1 << uframe; ++ if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { ++ if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, ++ qh->period, qh->c_usecs)) ++ goto done; ++ if (!check_period(fotg210, frame, uframe + qh->gap_uf, ++ qh->period, qh->c_usecs)) ++ goto done; ++ retval = 0; ++ } ++done: ++ return retval; ++} ++ ++/* "first fit" scheduling policy used the first time through, ++ * or when the previous schedule slot can't be re-used. ++ */ ++static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) ++{ ++ int status; ++ unsigned uframe; ++ __hc32 c_mask; ++ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ ++ struct fotg210_qh_hw *hw = qh->hw; ++ ++ qh_refresh(fotg210, qh); ++ hw->hw_next = FOTG210_LIST_END(fotg210); ++ frame = qh->start; ++ ++ /* reuse the previous schedule slots, if we can */ ++ if (frame < qh->period) { ++ uframe = ffs(hc32_to_cpup(fotg210, &hw->hw_info2) & QH_SMASK); ++ status = check_intr_schedule(fotg210, frame, --uframe, ++ qh, &c_mask); ++ } else { ++ uframe = 0; ++ c_mask = 0; ++ status = -ENOSPC; ++ } ++ ++ /* else scan the schedule to find a group of slots such that all ++ * uframes have enough periodic bandwidth available. ++ */ ++ if (status) { ++ /* "normal" case, uframing flexible except with splits */ ++ if (qh->period) { ++ int i; ++ ++ for (i = qh->period; status && i > 0; --i) { ++ frame = ++fotg210->random_frame % qh->period; ++ for (uframe = 0; uframe < 8; uframe++) { ++ status = check_intr_schedule(fotg210, ++ frame, uframe, qh, ++ &c_mask); ++ if (status == 0) ++ break; ++ } ++ } ++ ++ /* qh->period == 0 means every uframe */ ++ } else { ++ frame = 0; ++ status = check_intr_schedule(fotg210, 0, 0, qh, ++ &c_mask); ++ } ++ if (status) ++ goto done; ++ qh->start = frame; ++ ++ /* reset S-frame and (maybe) C-frame masks */ ++ hw->hw_info2 &= cpu_to_hc32(fotg210, ~(QH_CMASK | QH_SMASK)); ++ hw->hw_info2 |= qh->period ++ ? cpu_to_hc32(fotg210, 1 << uframe) ++ : cpu_to_hc32(fotg210, QH_SMASK); ++ hw->hw_info2 |= c_mask; ++ } else ++ fotg210_dbg(fotg210, "reused qh %p schedule\n", qh); ++ ++ /* stuff into the periodic schedule */ ++ qh_link_periodic(fotg210, qh); ++done: ++ return status; ++} ++ ++static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct list_head *qtd_list, gfp_t mem_flags) ++{ ++ unsigned epnum; ++ unsigned long flags; ++ struct fotg210_qh *qh; ++ int status; ++ struct list_head empty; ++ ++ /* get endpoint and transfer/schedule data */ ++ epnum = urb->ep->desc.bEndpointAddress; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ status = -ESHUTDOWN; ++ goto done_not_linked; ++ } ++ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(status)) ++ goto done_not_linked; ++ ++ /* get qh and force any scheduling errors */ ++ INIT_LIST_HEAD(&empty); ++ qh = qh_append_tds(fotg210, urb, &empty, epnum, &urb->ep->hcpriv); ++ if (qh == NULL) { ++ status = -ENOMEM; ++ goto done; ++ } ++ if (qh->qh_state == QH_STATE_IDLE) { ++ status = qh_schedule(fotg210, qh); ++ if (status) ++ goto done; ++ } ++ ++ /* then queue the urb's tds to the qh */ ++ qh = qh_append_tds(fotg210, urb, qtd_list, epnum, &urb->ep->hcpriv); ++ BUG_ON(qh == NULL); ++ ++ /* ... update usbfs periodic stats */ ++ fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs++; ++ ++done: ++ if (unlikely(status)) ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++done_not_linked: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ if (status) ++ qtd_list_free(fotg210, urb, qtd_list); ++ ++ return status; ++} ++ ++static void scan_intr(struct fotg210_hcd *fotg210) ++{ ++ struct fotg210_qh *qh; ++ ++ list_for_each_entry_safe(qh, fotg210->qh_scan_next, ++ &fotg210->intr_qh_list, intr_node) { ++rescan: ++ /* clean any finished work for this qh */ ++ if (!list_empty(&qh->qtd_list)) { ++ int temp; ++ ++ /* ++ * Unlinks could happen here; completion reporting ++ * drops the lock. That's why fotg210->qh_scan_next ++ * always holds the next qh to scan; if the next qh ++ * gets unlinked then fotg210->qh_scan_next is adjusted ++ * in qh_unlink_periodic(). ++ */ ++ temp = qh_completions(fotg210, qh); ++ if (unlikely(qh->needs_rescan || ++ (list_empty(&qh->qtd_list) && ++ qh->qh_state == QH_STATE_LINKED))) ++ start_unlink_intr(fotg210, qh); ++ else if (temp != 0) ++ goto rescan; ++ } ++ } ++} ++ ++/* fotg210_iso_stream ops work with both ITD and SITD */ ++ ++static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) ++{ ++ struct fotg210_iso_stream *stream; ++ ++ stream = kzalloc(sizeof(*stream), mem_flags); ++ if (likely(stream != NULL)) { ++ INIT_LIST_HEAD(&stream->td_list); ++ INIT_LIST_HEAD(&stream->free_list); ++ stream->next_uframe = -1; ++ } ++ return stream; ++} ++ ++static void iso_stream_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_stream *stream, struct usb_device *dev, ++ int pipe, unsigned interval) ++{ ++ u32 buf1; ++ unsigned epnum, maxp; ++ int is_input; ++ long bandwidth; ++ unsigned multi; ++ struct usb_host_endpoint *ep; ++ ++ /* ++ * this might be a "high bandwidth" highspeed endpoint, ++ * as encoded in the ep descriptor's wMaxPacket field ++ */ ++ epnum = usb_pipeendpoint(pipe); ++ is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; ++ ep = usb_pipe_endpoint(dev, pipe); ++ maxp = usb_endpoint_maxp(&ep->desc); ++ if (is_input) ++ buf1 = (1 << 11); ++ else ++ buf1 = 0; ++ ++ multi = usb_endpoint_maxp_mult(&ep->desc); ++ buf1 |= maxp; ++ maxp *= multi; ++ ++ stream->buf0 = cpu_to_hc32(fotg210, (epnum << 8) | dev->devnum); ++ stream->buf1 = cpu_to_hc32(fotg210, buf1); ++ stream->buf2 = cpu_to_hc32(fotg210, multi); ++ ++ /* usbfs wants to report the average usecs per frame tied up ++ * when transfers on this endpoint are scheduled ... ++ */ ++ if (dev->speed == USB_SPEED_FULL) { ++ interval <<= 3; ++ stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, ++ is_input, 1, maxp)); ++ stream->usecs /= 8; ++ } else { ++ stream->highspeed = 1; ++ stream->usecs = HS_USECS_ISO(maxp); ++ } ++ bandwidth = stream->usecs * 8; ++ bandwidth /= interval; ++ ++ stream->bandwidth = bandwidth; ++ stream->udev = dev; ++ stream->bEndpointAddress = is_input | epnum; ++ stream->interval = interval; ++ stream->maxp = maxp; ++} ++ ++static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, ++ struct urb *urb) ++{ ++ unsigned epnum; ++ struct fotg210_iso_stream *stream; ++ struct usb_host_endpoint *ep; ++ unsigned long flags; ++ ++ epnum = usb_pipeendpoint(urb->pipe); ++ if (usb_pipein(urb->pipe)) ++ ep = urb->dev->ep_in[epnum]; ++ else ++ ep = urb->dev->ep_out[epnum]; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ stream = ep->hcpriv; ++ ++ if (unlikely(stream == NULL)) { ++ stream = iso_stream_alloc(GFP_ATOMIC); ++ if (likely(stream != NULL)) { ++ ep->hcpriv = stream; ++ stream->ep = ep; ++ iso_stream_init(fotg210, stream, urb->dev, urb->pipe, ++ urb->interval); ++ } ++ ++ /* if dev->ep[epnum] is a QH, hw is set */ ++ } else if (unlikely(stream->hw != NULL)) { ++ fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", ++ urb->dev->devpath, epnum, ++ usb_pipein(urb->pipe) ? "in" : "out"); ++ stream = NULL; ++ } ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return stream; ++} ++ ++/* fotg210_iso_sched ops can be ITD-only or SITD-only */ ++ ++static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, ++ gfp_t mem_flags) ++{ ++ struct fotg210_iso_sched *iso_sched; ++ ++ iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); ++ if (likely(iso_sched != NULL)) ++ INIT_LIST_HEAD(&iso_sched->td_list); ++ ++ return iso_sched; ++} ++ ++static inline void itd_sched_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_sched *iso_sched, ++ struct fotg210_iso_stream *stream, struct urb *urb) ++{ ++ unsigned i; ++ dma_addr_t dma = urb->transfer_dma; ++ ++ /* how many uframes are needed for these transfers */ ++ iso_sched->span = urb->number_of_packets * stream->interval; ++ ++ /* figure out per-uframe itd fields that we'll need later ++ * when we fit new itds into the schedule. ++ */ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; ++ unsigned length; ++ dma_addr_t buf; ++ u32 trans; ++ ++ length = urb->iso_frame_desc[i].length; ++ buf = dma + urb->iso_frame_desc[i].offset; ++ ++ trans = FOTG210_ISOC_ACTIVE; ++ trans |= buf & 0x0fff; ++ if (unlikely(((i + 1) == urb->number_of_packets)) ++ && !(urb->transfer_flags & URB_NO_INTERRUPT)) ++ trans |= FOTG210_ITD_IOC; ++ trans |= length << 16; ++ uframe->transaction = cpu_to_hc32(fotg210, trans); ++ ++ /* might need to cross a buffer page within a uframe */ ++ uframe->bufp = (buf & ~(u64)0x0fff); ++ buf += length; ++ if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) ++ uframe->cross = 1; ++ } ++} ++ ++static void iso_sched_free(struct fotg210_iso_stream *stream, ++ struct fotg210_iso_sched *iso_sched) ++{ ++ if (!iso_sched) ++ return; ++ /* caller must hold fotg210->lock!*/ ++ list_splice(&iso_sched->td_list, &stream->free_list); ++ kfree(iso_sched); ++} ++ ++static int itd_urb_transaction(struct fotg210_iso_stream *stream, ++ struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) ++{ ++ struct fotg210_itd *itd; ++ dma_addr_t itd_dma; ++ int i; ++ unsigned num_itds; ++ struct fotg210_iso_sched *sched; ++ unsigned long flags; ++ ++ sched = iso_sched_alloc(urb->number_of_packets, mem_flags); ++ if (unlikely(sched == NULL)) ++ return -ENOMEM; ++ ++ itd_sched_init(fotg210, sched, stream, urb); ++ ++ if (urb->interval < 8) ++ num_itds = 1 + (sched->span + 7) / 8; ++ else ++ num_itds = urb->number_of_packets; ++ ++ /* allocate/init ITDs */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ for (i = 0; i < num_itds; i++) { ++ ++ /* ++ * Use iTDs from the free list, but not iTDs that may ++ * still be in use by the hardware. ++ */ ++ if (likely(!list_empty(&stream->free_list))) { ++ itd = list_first_entry(&stream->free_list, ++ struct fotg210_itd, itd_list); ++ if (itd->frame == fotg210->now_frame) ++ goto alloc_itd; ++ list_del(&itd->itd_list); ++ itd_dma = itd->itd_dma; ++ } else { ++alloc_itd: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, ++ &itd_dma); ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (!itd) { ++ iso_sched_free(stream, sched); ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return -ENOMEM; ++ } ++ } ++ ++ memset(itd, 0, sizeof(*itd)); ++ itd->itd_dma = itd_dma; ++ list_add(&itd->itd_list, &sched->td_list); ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ /* temporarily store schedule info in hcpriv */ ++ urb->hcpriv = sched; ++ urb->error_count = 0; ++ return 0; ++} ++ ++static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, ++ u8 usecs, u32 period) ++{ ++ uframe %= period; ++ do { ++ /* can't commit more than uframe_periodic_max usec */ ++ if (periodic_usecs(fotg210, uframe >> 3, uframe & 0x7) ++ > (fotg210->uframe_periodic_max - usecs)) ++ return 0; ++ ++ /* we know urb->interval is 2^N uframes */ ++ uframe += period; ++ } while (uframe < mod); ++ return 1; ++} ++ ++/* This scheduler plans almost as far into the future as it has actual ++ * periodic schedule slots. (Affected by TUNE_FLS, which defaults to ++ * "as small as possible" to be cache-friendlier.) That limits the size ++ * transfers you can stream reliably; avoid more than 64 msec per urb. ++ * Also avoid queue depths of less than fotg210's worst irq latency (affected ++ * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, ++ * and other factors); or more than about 230 msec total (for portability, ++ * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! ++ */ ++ ++#define SCHEDULE_SLOP 80 /* microframes */ ++ ++static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, ++ struct fotg210_iso_stream *stream) ++{ ++ u32 now, next, start, period, span; ++ int status; ++ unsigned mod = fotg210->periodic_size << 3; ++ struct fotg210_iso_sched *sched = urb->hcpriv; ++ ++ period = urb->interval; ++ span = sched->span; ++ ++ if (span > mod - SCHEDULE_SLOP) { ++ fotg210_dbg(fotg210, "iso request %p too long\n", urb); ++ status = -EFBIG; ++ goto fail; ++ } ++ ++ now = fotg210_read_frame_index(fotg210) & (mod - 1); ++ ++ /* Typical case: reuse current schedule, stream is still active. ++ * Hopefully there are no gaps from the host falling behind ++ * (irq delays etc), but if there are we'll take the next ++ * slot in the schedule, implicitly assuming URB_ISO_ASAP. ++ */ ++ if (likely(!list_empty(&stream->td_list))) { ++ u32 excess; ++ ++ /* For high speed devices, allow scheduling within the ++ * isochronous scheduling threshold. For full speed devices ++ * and Intel PCI-based controllers, don't (work around for ++ * Intel ICH9 bug). ++ */ ++ if (!stream->highspeed && fotg210->fs_i_thresh) ++ next = now + fotg210->i_thresh; ++ else ++ next = now; ++ ++ /* Fell behind (by up to twice the slop amount)? ++ * We decide based on the time of the last currently-scheduled ++ * slot, not the time of the next available slot. ++ */ ++ excess = (stream->next_uframe - period - next) & (mod - 1); ++ if (excess >= mod - 2 * SCHEDULE_SLOP) ++ start = next + excess - mod + period * ++ DIV_ROUND_UP(mod - excess, period); ++ else ++ start = next + excess + period; ++ if (start - now >= mod) { ++ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now - period, period, ++ mod); ++ status = -EFBIG; ++ goto fail; ++ } ++ } ++ ++ /* need to schedule; when's the next (u)frame we could start? ++ * this is bigger than fotg210->i_thresh allows; scheduling itself ++ * isn't free, the slop should handle reasonably slow cpus. it ++ * can also help high bandwidth if the dma and irq loads don't ++ * jump until after the queue is primed. ++ */ ++ else { ++ int done = 0; ++ ++ start = SCHEDULE_SLOP + (now & ~0x07); ++ ++ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ ++ ++ /* find a uframe slot with enough bandwidth. ++ * Early uframes are more precious because full-speed ++ * iso IN transfers can't use late uframes, ++ * and therefore they should be allocated last. ++ */ ++ next = start; ++ start += period; ++ do { ++ start--; ++ /* check schedule: enough space? */ ++ if (itd_slot_ok(fotg210, mod, start, ++ stream->usecs, period)) ++ done = 1; ++ } while (start > next && !done); ++ ++ /* no room in the schedule */ ++ if (!done) { ++ fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", ++ urb, now, now + mod); ++ status = -ENOSPC; ++ goto fail; ++ } ++ } ++ ++ /* Tried to schedule too far into the future? */ ++ if (unlikely(start - now + span - period >= ++ mod - 2 * SCHEDULE_SLOP)) { ++ fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now, span - period, ++ mod - 2 * SCHEDULE_SLOP); ++ status = -EFBIG; ++ goto fail; ++ } ++ ++ stream->next_uframe = start & (mod - 1); ++ ++ /* report high speed start in uframes; full speed, in frames */ ++ urb->start_frame = stream->next_uframe; ++ if (!stream->highspeed) ++ urb->start_frame >>= 3; ++ ++ /* Make sure scan_isoc() sees these */ ++ if (fotg210->isoc_count == 0) ++ fotg210->next_frame = now >> 3; ++ return 0; ++ ++fail: ++ iso_sched_free(stream, sched); ++ urb->hcpriv = NULL; ++ return status; ++} ++ ++static inline void itd_init(struct fotg210_hcd *fotg210, ++ struct fotg210_iso_stream *stream, struct fotg210_itd *itd) ++{ ++ int i; ++ ++ /* it's been recently zeroed */ ++ itd->hw_next = FOTG210_LIST_END(fotg210); ++ itd->hw_bufp[0] = stream->buf0; ++ itd->hw_bufp[1] = stream->buf1; ++ itd->hw_bufp[2] = stream->buf2; ++ ++ for (i = 0; i < 8; i++) ++ itd->index[i] = -1; ++ ++ /* All other fields are filled when scheduling */ ++} ++ ++static inline void itd_patch(struct fotg210_hcd *fotg210, ++ struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, ++ unsigned index, u16 uframe) ++{ ++ struct fotg210_iso_packet *uf = &iso_sched->packet[index]; ++ unsigned pg = itd->pg; ++ ++ uframe &= 0x07; ++ itd->index[uframe] = index; ++ ++ itd->hw_transaction[uframe] = uf->transaction; ++ itd->hw_transaction[uframe] |= cpu_to_hc32(fotg210, pg << 12); ++ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, uf->bufp & ~(u32)0); ++ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(uf->bufp >> 32)); ++ ++ /* iso_frame_desc[].offset must be strictly increasing */ ++ if (unlikely(uf->cross)) { ++ u64 bufp = uf->bufp + 4096; ++ ++ itd->pg = ++pg; ++ itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); ++ itd->hw_bufp_hi[pg] |= cpu_to_hc32(fotg210, (u32)(bufp >> 32)); ++ } ++} ++ ++static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, ++ struct fotg210_itd *itd) ++{ ++ union fotg210_shadow *prev = &fotg210->pshadow[frame]; ++ __hc32 *hw_p = &fotg210->periodic[frame]; ++ union fotg210_shadow here = *prev; ++ __hc32 type = 0; ++ ++ /* skip any iso nodes which might belong to previous microframes */ ++ while (here.ptr) { ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ if (type == cpu_to_hc32(fotg210, Q_TYPE_QH)) ++ break; ++ prev = periodic_next_shadow(fotg210, prev, type); ++ hw_p = shadow_next_periodic(fotg210, &here, type); ++ here = *prev; ++ } ++ ++ itd->itd_next = here; ++ itd->hw_next = *hw_p; ++ prev->itd = itd; ++ itd->frame = frame; ++ wmb(); ++ *hw_p = cpu_to_hc32(fotg210, itd->itd_dma | Q_TYPE_ITD); ++} ++ ++/* fit urb's itds into the selected schedule slot; activate as needed */ ++static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, ++ unsigned mod, struct fotg210_iso_stream *stream) ++{ ++ int packet; ++ unsigned next_uframe, uframe, frame; ++ struct fotg210_iso_sched *iso_sched = urb->hcpriv; ++ struct fotg210_itd *itd; ++ ++ next_uframe = stream->next_uframe & (mod - 1); ++ ++ if (unlikely(list_empty(&stream->td_list))) { ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated ++ += stream->bandwidth; ++ fotg210_dbg(fotg210, ++ "schedule devp %s ep%d%s-iso period %d start %d.%d\n", ++ urb->dev->devpath, stream->bEndpointAddress & 0x0f, ++ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", ++ urb->interval, ++ next_uframe >> 3, next_uframe & 0x7); ++ } ++ ++ /* fill iTDs uframe by uframe */ ++ for (packet = 0, itd = NULL; packet < urb->number_of_packets;) { ++ if (itd == NULL) { ++ /* ASSERT: we have all necessary itds */ ++ ++ /* ASSERT: no itds for this endpoint in this uframe */ ++ ++ itd = list_entry(iso_sched->td_list.next, ++ struct fotg210_itd, itd_list); ++ list_move_tail(&itd->itd_list, &stream->td_list); ++ itd->stream = stream; ++ itd->urb = urb; ++ itd_init(fotg210, stream, itd); ++ } ++ ++ uframe = next_uframe & 0x07; ++ frame = next_uframe >> 3; ++ ++ itd_patch(fotg210, itd, iso_sched, packet, uframe); ++ ++ next_uframe += stream->interval; ++ next_uframe &= mod - 1; ++ packet++; ++ ++ /* link completed itds into the schedule */ ++ if (((next_uframe >> 3) != frame) ++ || packet == urb->number_of_packets) { ++ itd_link(fotg210, frame & (fotg210->periodic_size - 1), ++ itd); ++ itd = NULL; ++ } ++ } ++ stream->next_uframe = next_uframe; ++ ++ /* don't need that schedule data any more */ ++ iso_sched_free(stream, iso_sched); ++ urb->hcpriv = NULL; ++ ++ ++fotg210->isoc_count; ++ enable_periodic(fotg210); ++} ++ ++#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ ++ FOTG210_ISOC_XACTERR) ++ ++/* Process and recycle a completed ITD. Return true iff its urb completed, ++ * and hence its completion callback probably added things to the hardware ++ * schedule. ++ * ++ * Note that we carefully avoid recycling this descriptor until after any ++ * completion callback runs, so that it won't be reused quickly. That is, ++ * assuming (a) no more than two urbs per frame on this endpoint, and also ++ * (b) only this endpoint's completions submit URBs. It seems some silicon ++ * corrupts things if you reuse completed descriptors very quickly... ++ */ ++static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) ++{ ++ struct urb *urb = itd->urb; ++ struct usb_iso_packet_descriptor *desc; ++ u32 t; ++ unsigned uframe; ++ int urb_index = -1; ++ struct fotg210_iso_stream *stream = itd->stream; ++ struct usb_device *dev; ++ bool retval = false; ++ ++ /* for each uframe with a packet */ ++ for (uframe = 0; uframe < 8; uframe++) { ++ if (likely(itd->index[uframe] == -1)) ++ continue; ++ urb_index = itd->index[uframe]; ++ desc = &urb->iso_frame_desc[urb_index]; ++ ++ t = hc32_to_cpup(fotg210, &itd->hw_transaction[uframe]); ++ itd->hw_transaction[uframe] = 0; ++ ++ /* report transfer status */ ++ if (unlikely(t & ISO_ERRS)) { ++ urb->error_count++; ++ if (t & FOTG210_ISOC_BUF_ERR) ++ desc->status = usb_pipein(urb->pipe) ++ ? -ENOSR /* hc couldn't read */ ++ : -ECOMM; /* hc couldn't write */ ++ else if (t & FOTG210_ISOC_BABBLE) ++ desc->status = -EOVERFLOW; ++ else /* (t & FOTG210_ISOC_XACTERR) */ ++ desc->status = -EPROTO; ++ ++ /* HC need not update length with this error */ ++ if (!(t & FOTG210_ISOC_BABBLE)) { ++ desc->actual_length = FOTG210_ITD_LENGTH(t); ++ urb->actual_length += desc->actual_length; ++ } ++ } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { ++ desc->status = 0; ++ desc->actual_length = FOTG210_ITD_LENGTH(t); ++ urb->actual_length += desc->actual_length; ++ } else { ++ /* URB was too late */ ++ desc->status = -EXDEV; ++ } ++ } ++ ++ /* handle completion now? */ ++ if (likely((urb_index + 1) != urb->number_of_packets)) ++ goto done; ++ ++ /* ASSERT: it's really the last itd for this urb ++ * list_for_each_entry (itd, &stream->td_list, itd_list) ++ * BUG_ON (itd->urb == urb); ++ */ ++ ++ /* give urb back to the driver; completion often (re)submits */ ++ dev = urb->dev; ++ fotg210_urb_done(fotg210, urb, 0); ++ retval = true; ++ urb = NULL; ++ ++ --fotg210->isoc_count; ++ disable_periodic(fotg210); ++ ++ if (unlikely(list_is_singular(&stream->td_list))) { ++ fotg210_to_hcd(fotg210)->self.bandwidth_allocated ++ -= stream->bandwidth; ++ fotg210_dbg(fotg210, ++ "deschedule devp %s ep%d%s-iso\n", ++ dev->devpath, stream->bEndpointAddress & 0x0f, ++ (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); ++ } ++ ++done: ++ itd->urb = NULL; ++ ++ /* Add to the end of the free list for later reuse */ ++ list_move_tail(&itd->itd_list, &stream->free_list); ++ ++ /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ ++ if (list_empty(&stream->td_list)) { ++ list_splice_tail_init(&stream->free_list, ++ &fotg210->cached_itd_list); ++ start_free_itds(fotg210); ++ } ++ ++ return retval; ++} ++ ++static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, ++ gfp_t mem_flags) ++{ ++ int status = -EINVAL; ++ unsigned long flags; ++ struct fotg210_iso_stream *stream; ++ ++ /* Get iso_stream head */ ++ stream = iso_stream_find(fotg210, urb); ++ if (unlikely(stream == NULL)) { ++ fotg210_dbg(fotg210, "can't get iso stream\n"); ++ return -ENOMEM; ++ } ++ if (unlikely(urb->interval != stream->interval && ++ fotg210_port_speed(fotg210, 0) == ++ USB_PORT_STAT_HIGH_SPEED)) { ++ fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", ++ stream->interval, urb->interval); ++ goto done; ++ } ++ ++#ifdef FOTG210_URB_TRACE ++ fotg210_dbg(fotg210, ++ "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", ++ __func__, urb->dev->devpath, urb, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "in" : "out", ++ urb->transfer_buffer_length, ++ urb->number_of_packets, urb->interval, ++ stream); ++#endif ++ ++ /* allocate ITDs w/o locking anything */ ++ status = itd_urb_transaction(stream, fotg210, urb, mem_flags); ++ if (unlikely(status < 0)) { ++ fotg210_dbg(fotg210, "can't init itds\n"); ++ goto done; ++ } ++ ++ /* schedule ... need to lock */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ if (unlikely(!HCD_HW_ACCESSIBLE(fotg210_to_hcd(fotg210)))) { ++ status = -ESHUTDOWN; ++ goto done_not_linked; ++ } ++ status = usb_hcd_link_urb_to_ep(fotg210_to_hcd(fotg210), urb); ++ if (unlikely(status)) ++ goto done_not_linked; ++ status = iso_stream_schedule(fotg210, urb, stream); ++ if (likely(status == 0)) ++ itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); ++ else ++ usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); ++done_not_linked: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++done: ++ return status; ++} ++ ++static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, ++ unsigned now_frame, bool live) ++{ ++ unsigned uf; ++ bool modified; ++ union fotg210_shadow q, *q_p; ++ __hc32 type, *hw_p; ++ ++ /* scan each element in frame's queue for completions */ ++ q_p = &fotg210->pshadow[frame]; ++ hw_p = &fotg210->periodic[frame]; ++ q.ptr = q_p->ptr; ++ type = Q_NEXT_TYPE(fotg210, *hw_p); ++ modified = false; ++ ++ while (q.ptr) { ++ switch (hc32_to_cpu(fotg210, type)) { ++ case Q_TYPE_ITD: ++ /* If this ITD is still active, leave it for ++ * later processing ... check the next entry. ++ * No need to check for activity unless the ++ * frame is current. ++ */ ++ if (frame == now_frame && live) { ++ rmb(); ++ for (uf = 0; uf < 8; uf++) { ++ if (q.itd->hw_transaction[uf] & ++ ITD_ACTIVE(fotg210)) ++ break; ++ } ++ if (uf < 8) { ++ q_p = &q.itd->itd_next; ++ hw_p = &q.itd->hw_next; ++ type = Q_NEXT_TYPE(fotg210, ++ q.itd->hw_next); ++ q = *q_p; ++ break; ++ } ++ } ++ ++ /* Take finished ITDs out of the schedule ++ * and process them: recycle, maybe report ++ * URB completion. HC won't cache the ++ * pointer for much longer, if at all. ++ */ ++ *q_p = q.itd->itd_next; ++ *hw_p = q.itd->hw_next; ++ type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); ++ wmb(); ++ modified = itd_complete(fotg210, q.itd); ++ q = *q_p; ++ break; ++ default: ++ fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", ++ type, frame, q.ptr); ++ fallthrough; ++ case Q_TYPE_QH: ++ case Q_TYPE_FSTN: ++ /* End of the iTDs and siTDs */ ++ q.ptr = NULL; ++ break; ++ } ++ ++ /* assume completion callbacks modify the queue */ ++ if (unlikely(modified && fotg210->isoc_count > 0)) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void scan_isoc(struct fotg210_hcd *fotg210) ++{ ++ unsigned uf, now_frame, frame, ret; ++ unsigned fmask = fotg210->periodic_size - 1; ++ bool live; ++ ++ /* ++ * When running, scan from last scan point up to "now" ++ * else clean up by scanning everything that's left. ++ * Touches as few pages as possible: cache-friendly. ++ */ ++ if (fotg210->rh_state >= FOTG210_RH_RUNNING) { ++ uf = fotg210_read_frame_index(fotg210); ++ now_frame = (uf >> 3) & fmask; ++ live = true; ++ } else { ++ now_frame = (fotg210->next_frame - 1) & fmask; ++ live = false; ++ } ++ fotg210->now_frame = now_frame; ++ ++ frame = fotg210->next_frame; ++ for (;;) { ++ ret = 1; ++ while (ret != 0) ++ ret = scan_frame_queue(fotg210, frame, ++ now_frame, live); ++ ++ /* Stop when we have reached the current frame */ ++ if (frame == now_frame) ++ break; ++ frame = (frame + 1) & fmask; ++ } ++ fotg210->next_frame = now_frame; ++} ++ ++/* Display / Set uframe_periodic_max ++ */ ++static ssize_t uframe_periodic_max_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fotg210_hcd *fotg210; ++ int n; ++ ++ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); ++ n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); ++ return n; ++} ++ ++ ++static ssize_t uframe_periodic_max_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct fotg210_hcd *fotg210; ++ unsigned uframe_periodic_max; ++ unsigned frame, uframe; ++ unsigned short allocated_max; ++ unsigned long flags; ++ ssize_t ret; ++ ++ fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); ++ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) ++ return -EINVAL; ++ ++ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { ++ fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", ++ uframe_periodic_max); ++ return -EINVAL; ++ } ++ ++ ret = -EINVAL; ++ ++ /* ++ * lock, so that our checking does not race with possible periodic ++ * bandwidth allocation through submitting new urbs. ++ */ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ /* ++ * for request to decrease max periodic bandwidth, we have to check ++ * every microframe in the schedule to see whether the decrease is ++ * possible. ++ */ ++ if (uframe_periodic_max < fotg210->uframe_periodic_max) { ++ allocated_max = 0; ++ ++ for (frame = 0; frame < fotg210->periodic_size; ++frame) ++ for (uframe = 0; uframe < 7; ++uframe) ++ allocated_max = max(allocated_max, ++ periodic_usecs(fotg210, frame, ++ uframe)); ++ ++ if (allocated_max > uframe_periodic_max) { ++ fotg210_info(fotg210, ++ "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", ++ allocated_max, uframe_periodic_max); ++ goto out_unlock; ++ } ++ } ++ ++ /* increasing is always ok */ ++ ++ fotg210_info(fotg210, ++ "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", ++ 100 * uframe_periodic_max/125, uframe_periodic_max); ++ ++ if (uframe_periodic_max != 100) ++ fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); ++ ++ fotg210->uframe_periodic_max = uframe_periodic_max; ++ ret = count; ++ ++out_unlock: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return ret; ++} ++ ++static DEVICE_ATTR_RW(uframe_periodic_max); ++ ++static inline int create_sysfs_files(struct fotg210_hcd *fotg210) ++{ ++ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; ++ ++ return device_create_file(controller, &dev_attr_uframe_periodic_max); ++} ++ ++static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) ++{ ++ struct device *controller = fotg210_to_hcd(fotg210)->self.controller; ++ ++ device_remove_file(controller, &dev_attr_uframe_periodic_max); ++} ++/* On some systems, leaving remote wakeup enabled prevents system shutdown. ++ * The firmware seems to think that powering off is a wakeup event! ++ * This routine turns off remote wakeup and everything else, on all ports. ++ */ ++static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) ++{ ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ ++ fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); ++} ++ ++/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. ++ * Must be called with interrupts enabled and the lock not held. ++ */ ++static void fotg210_silence_controller(struct fotg210_hcd *fotg210) ++{ ++ fotg210_halt(fotg210); ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->rh_state = FOTG210_RH_HALTED; ++ fotg210_turn_off_all_ports(fotg210); ++ spin_unlock_irq(&fotg210->lock); ++} ++ ++/* fotg210_shutdown kick in for silicon on any bus (not just pci, etc). ++ * This forcibly disables dma and IRQs, helping kexec and other cases ++ * where the next system software may expect clean state. ++ */ ++static void fotg210_shutdown(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->shutdown = true; ++ fotg210->rh_state = FOTG210_RH_STOPPING; ++ fotg210->enabled_hrtimer_events = 0; ++ spin_unlock_irq(&fotg210->lock); ++ ++ fotg210_silence_controller(fotg210); ++ ++ hrtimer_cancel(&fotg210->hrtimer); ++} ++ ++/* fotg210_work is called from some interrupts, timers, and so on. ++ * it calls driver completion functions, after dropping fotg210->lock. ++ */ ++static void fotg210_work(struct fotg210_hcd *fotg210) ++{ ++ /* another CPU may drop fotg210->lock during a schedule scan while ++ * it reports urb completions. this flag guards against bogus ++ * attempts at re-entrant schedule scanning. ++ */ ++ if (fotg210->scanning) { ++ fotg210->need_rescan = true; ++ return; ++ } ++ fotg210->scanning = true; ++ ++rescan: ++ fotg210->need_rescan = false; ++ if (fotg210->async_count) ++ scan_async(fotg210); ++ if (fotg210->intr_count > 0) ++ scan_intr(fotg210); ++ if (fotg210->isoc_count > 0) ++ scan_isoc(fotg210); ++ if (fotg210->need_rescan) ++ goto rescan; ++ fotg210->scanning = false; ++ ++ /* the IO watchdog guards against hardware or driver bugs that ++ * misplace IRQs, and should let us run completely without IRQs. ++ * such lossage has been observed on both VT6202 and VT8235. ++ */ ++ turn_on_io_watchdog(fotg210); ++} ++ ++/* Called when the fotg210_hcd module is removed. ++ */ ++static void fotg210_stop(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ fotg210_dbg(fotg210, "stop\n"); ++ ++ /* no more interrupts ... */ ++ ++ spin_lock_irq(&fotg210->lock); ++ fotg210->enabled_hrtimer_events = 0; ++ spin_unlock_irq(&fotg210->lock); ++ ++ fotg210_quiesce(fotg210); ++ fotg210_silence_controller(fotg210); ++ fotg210_reset(fotg210); ++ ++ hrtimer_cancel(&fotg210->hrtimer); ++ remove_sysfs_files(fotg210); ++ remove_debug_files(fotg210); ++ ++ /* root hub is shut down separately (first, when possible) */ ++ spin_lock_irq(&fotg210->lock); ++ end_free_itds(fotg210); ++ spin_unlock_irq(&fotg210->lock); ++ fotg210_mem_cleanup(fotg210); ++ ++#ifdef FOTG210_STATS ++ fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", ++ fotg210->stats.normal, fotg210->stats.error, ++ fotg210->stats.iaa, fotg210->stats.lost_iaa); ++ fotg210_dbg(fotg210, "complete %ld unlink %ld\n", ++ fotg210->stats.complete, fotg210->stats.unlink); ++#endif ++ ++ dbg_status(fotg210, "fotg210_stop completed", ++ fotg210_readl(fotg210, &fotg210->regs->status)); ++} ++ ++/* one-time init, only for memory state */ ++static int hcd_fotg210_init(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp; ++ int retval; ++ u32 hcc_params; ++ struct fotg210_qh_hw *hw; ++ ++ spin_lock_init(&fotg210->lock); ++ ++ /* ++ * keep io watchdog by default, those good HCDs could turn off it later ++ */ ++ fotg210->need_io_watchdog = 1; ++ ++ hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ++ fotg210->hrtimer.function = fotg210_hrtimer_func; ++ fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; ++ ++ hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ /* ++ * by default set standard 80% (== 100 usec/uframe) max periodic ++ * bandwidth as required by USB 2.0 ++ */ ++ fotg210->uframe_periodic_max = 100; ++ ++ /* ++ * hw default: 1K periodic list heads, one per frame. ++ * periodic_size can shrink by USBCMD update if hcc_params allows. ++ */ ++ fotg210->periodic_size = DEFAULT_I_TDPS; ++ INIT_LIST_HEAD(&fotg210->intr_qh_list); ++ INIT_LIST_HEAD(&fotg210->cached_itd_list); ++ ++ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { ++ /* periodic schedule size can be smaller than default */ ++ switch (FOTG210_TUNE_FLS) { ++ case 0: ++ fotg210->periodic_size = 1024; ++ break; ++ case 1: ++ fotg210->periodic_size = 512; ++ break; ++ case 2: ++ fotg210->periodic_size = 256; ++ break; ++ default: ++ BUG(); ++ } ++ } ++ retval = fotg210_mem_init(fotg210, GFP_KERNEL); ++ if (retval < 0) ++ return retval; ++ ++ /* controllers may cache some of the periodic schedule ... */ ++ fotg210->i_thresh = 2; ++ ++ /* ++ * dedicate a qh for the async ring head, since we couldn't unlink ++ * a 'real' qh without stopping the async schedule [4.8]. use it ++ * as the 'reclamation list head' too. ++ * its dummy is used in hw_alt_next of many tds, to prevent the qh ++ * from automatically advancing to the next td after short reads. ++ */ ++ fotg210->async->qh_next.qh = NULL; ++ hw = fotg210->async->hw; ++ hw->hw_next = QH_NEXT(fotg210, fotg210->async->qh_dma); ++ hw->hw_info1 = cpu_to_hc32(fotg210, QH_HEAD); ++ hw->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT); ++ hw->hw_qtd_next = FOTG210_LIST_END(fotg210); ++ fotg210->async->qh_state = QH_STATE_LINKED; ++ hw->hw_alt_next = QTD_NEXT(fotg210, fotg210->async->dummy->qtd_dma); ++ ++ /* clear interrupt enables, set irq latency */ ++ if (log2_irq_thresh < 0 || log2_irq_thresh > 6) ++ log2_irq_thresh = 0; ++ temp = 1 << (16 + log2_irq_thresh); ++ if (HCC_CANPARK(hcc_params)) { ++ /* HW default park == 3, on hardware that supports it (like ++ * NVidia and ALI silicon), maximizes throughput on the async ++ * schedule by avoiding QH fetches between transfers. ++ * ++ * With fast usb storage devices and NForce2, "park" seems to ++ * make problems: throughput reduction (!), data errors... ++ */ ++ if (park) { ++ park = min_t(unsigned, park, 3); ++ temp |= CMD_PARK; ++ temp |= park << 8; ++ } ++ fotg210_dbg(fotg210, "park %d\n", park); ++ } ++ if (HCC_PGM_FRAMELISTLEN(hcc_params)) { ++ /* periodic schedule size can be smaller than default */ ++ temp &= ~(3 << 2); ++ temp |= (FOTG210_TUNE_FLS << 2); ++ } ++ fotg210->command = temp; ++ ++ /* Accept arbitrarily long scatter-gather lists */ ++ if (!hcd->localmem_pool) ++ hcd->self.sg_tablesize = ~0; ++ return 0; ++} ++ ++/* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ ++static int fotg210_run(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 temp; ++ ++ hcd->uses_new_polling = 1; ++ ++ /* EHCI spec section 4.1 */ ++ ++ fotg210_writel(fotg210, fotg210->periodic_dma, ++ &fotg210->regs->frame_list); ++ fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, ++ &fotg210->regs->async_next); ++ ++ /* ++ * hcc_params controls whether fotg210->regs->segment must (!!!) ++ * be used; it constrains QH/ITD/SITD and QTD locations. ++ * dma_pool consistent memory always uses segment zero. ++ * streaming mappings for I/O buffers, like dma_map_single(), ++ * can return segments above 4GB, if the device allows. ++ * ++ * NOTE: the dma mask is visible through dev->dma_mask, so ++ * drivers can pass this info along ... like NETIF_F_HIGHDMA, ++ * Scsi_Host.highmem_io, and so forth. It's readonly to all ++ * host side drivers though. ++ */ ++ fotg210_readl(fotg210, &fotg210->caps->hcc_params); ++ ++ /* ++ * Philips, Intel, and maybe others need CMD_RUN before the ++ * root hub will detect new devices (why?); NEC doesn't ++ */ ++ fotg210->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ++ fotg210->command |= CMD_RUN; ++ fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); ++ dbg_cmd(fotg210, "init", fotg210->command); ++ ++ /* ++ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices ++ * are explicitly handed to companion controller(s), so no TT is ++ * involved with the root hub. (Except where one is integrated, ++ * and there's no companion controller unless maybe for USB OTG.) ++ * ++ * Turning on the CF flag will transfer ownership of all ports ++ * from the companions to the EHCI controller. If any of the ++ * companions are in the middle of a port reset at the time, it ++ * could cause trouble. Write-locking ehci_cf_port_reset_rwsem ++ * guarantees that no resets are in progress. After we set CF, ++ * a short delay lets the hardware catch up; new resets shouldn't ++ * be started before the port switching actions could complete. ++ */ ++ down_write(&ehci_cf_port_reset_rwsem); ++ fotg210->rh_state = FOTG210_RH_RUNNING; ++ /* unblock posted writes */ ++ fotg210_readl(fotg210, &fotg210->regs->command); ++ usleep_range(5000, 10000); ++ up_write(&ehci_cf_port_reset_rwsem); ++ fotg210->last_periodic_enable = ktime_get_real(); ++ ++ temp = HC_VERSION(fotg210, ++ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); ++ fotg210_info(fotg210, ++ "USB %x.%x started, EHCI %x.%02x\n", ++ ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), ++ temp >> 8, temp & 0xff); ++ ++ fotg210_writel(fotg210, INTR_MASK, ++ &fotg210->regs->intr_enable); /* Turn On Interrupts */ ++ ++ /* GRR this is run-once init(), being done every time the HC starts. ++ * So long as they're part of class devices, we can't do it init() ++ * since the class device isn't created that early. ++ */ ++ create_debug_files(fotg210); ++ create_sysfs_files(fotg210); ++ ++ return 0; ++} ++ ++static int fotg210_setup(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ int retval; ++ ++ fotg210->regs = (void __iomem *)fotg210->caps + ++ HC_LENGTH(fotg210, ++ fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); ++ dbg_hcs_params(fotg210, "reset"); ++ dbg_hcc_params(fotg210, "reset"); ++ ++ /* cache this readonly data; minimize chip reads */ ++ fotg210->hcs_params = fotg210_readl(fotg210, ++ &fotg210->caps->hcs_params); ++ ++ fotg210->sbrn = HCD_USB2; ++ ++ /* data structure init */ ++ retval = hcd_fotg210_init(hcd); ++ if (retval) ++ return retval; ++ ++ retval = fotg210_halt(fotg210); ++ if (retval) ++ return retval; ++ ++ fotg210_reset(fotg210); ++ ++ return 0; ++} ++ ++static irqreturn_t fotg210_irq(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ u32 status, masked_status, pcd_status = 0, cmd; ++ int bh; ++ ++ spin_lock(&fotg210->lock); ++ ++ status = fotg210_readl(fotg210, &fotg210->regs->status); ++ ++ /* e.g. cardbus physical eject */ ++ if (status == ~(u32) 0) { ++ fotg210_dbg(fotg210, "device removed\n"); ++ goto dead; ++ } ++ ++ /* ++ * We don't use STS_FLR, but some controllers don't like it to ++ * remain on, so mask it out along with the other status bits. ++ */ ++ masked_status = status & (INTR_MASK | STS_FLR); ++ ++ /* Shared IRQ? */ ++ if (!masked_status || ++ unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { ++ spin_unlock(&fotg210->lock); ++ return IRQ_NONE; ++ } ++ ++ /* clear (just) interrupts */ ++ fotg210_writel(fotg210, masked_status, &fotg210->regs->status); ++ cmd = fotg210_readl(fotg210, &fotg210->regs->command); ++ bh = 0; ++ ++ /* unrequested/ignored: Frame List Rollover */ ++ dbg_status(fotg210, "irq", status); ++ ++ /* INT, ERR, and IAA interrupt rates can be throttled */ ++ ++ /* normal [4.15.1.2] or error [4.15.1.1] completion */ ++ if (likely((status & (STS_INT|STS_ERR)) != 0)) { ++ if (likely((status & STS_ERR) == 0)) ++ INCR(fotg210->stats.normal); ++ else ++ INCR(fotg210->stats.error); ++ bh = 1; ++ } ++ ++ /* complete the unlinking of some qh [4.15.2.3] */ ++ if (status & STS_IAA) { ++ ++ /* Turn off the IAA watchdog */ ++ fotg210->enabled_hrtimer_events &= ++ ~BIT(FOTG210_HRTIMER_IAA_WATCHDOG); ++ ++ /* ++ * Mild optimization: Allow another IAAD to reset the ++ * hrtimer, if one occurs before the next expiration. ++ * In theory we could always cancel the hrtimer, but ++ * tests show that about half the time it will be reset ++ * for some other event anyway. ++ */ ++ if (fotg210->next_hrtimer_event == FOTG210_HRTIMER_IAA_WATCHDOG) ++ ++fotg210->next_hrtimer_event; ++ ++ /* guard against (alleged) silicon errata */ ++ if (cmd & CMD_IAAD) ++ fotg210_dbg(fotg210, "IAA with IAAD still set?\n"); ++ if (fotg210->async_iaa) { ++ INCR(fotg210->stats.iaa); ++ end_unlink_async(fotg210); ++ } else ++ fotg210_dbg(fotg210, "IAA with nothing unlinked?\n"); ++ } ++ ++ /* remote wakeup [4.3.1] */ ++ if (status & STS_PCD) { ++ int pstatus; ++ u32 __iomem *status_reg = &fotg210->regs->port_status; ++ ++ /* kick root hub later */ ++ pcd_status = status; ++ ++ /* resume root hub? */ ++ if (fotg210->rh_state == FOTG210_RH_SUSPENDED) ++ usb_hcd_resume_root_hub(hcd); ++ ++ pstatus = fotg210_readl(fotg210, status_reg); ++ ++ if (test_bit(0, &fotg210->suspended_ports) && ++ ((pstatus & PORT_RESUME) || ++ !(pstatus & PORT_SUSPEND)) && ++ (pstatus & PORT_PE) && ++ fotg210->reset_done[0] == 0) { ++ ++ /* start 20 msec resume signaling from this port, ++ * and make hub_wq collect PORT_STAT_C_SUSPEND to ++ * stop that signaling. Use 5 ms extra for safety, ++ * like usb_port_resume() does. ++ */ ++ fotg210->reset_done[0] = jiffies + msecs_to_jiffies(25); ++ set_bit(0, &fotg210->resuming_ports); ++ fotg210_dbg(fotg210, "port 1 remote wakeup\n"); ++ mod_timer(&hcd->rh_timer, fotg210->reset_done[0]); ++ } ++ } ++ ++ /* PCI errors [4.15.2.4] */ ++ if (unlikely((status & STS_FATAL) != 0)) { ++ fotg210_err(fotg210, "fatal error\n"); ++ dbg_cmd(fotg210, "fatal", cmd); ++ dbg_status(fotg210, "fatal", status); ++dead: ++ usb_hc_died(hcd); ++ ++ /* Don't let the controller do anything more */ ++ fotg210->shutdown = true; ++ fotg210->rh_state = FOTG210_RH_STOPPING; ++ fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); ++ fotg210_writel(fotg210, fotg210->command, ++ &fotg210->regs->command); ++ fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); ++ fotg210_handle_controller_death(fotg210); ++ ++ /* Handle completions when the controller stops */ ++ bh = 0; ++ } ++ ++ if (bh) ++ fotg210_work(fotg210); ++ spin_unlock(&fotg210->lock); ++ if (pcd_status) ++ usb_hcd_poll_rh_status(hcd); ++ return IRQ_HANDLED; ++} ++ ++/* non-error returns are a promise to giveback() the urb later ++ * we drop ownership so next owner (or urb unlink) can get it ++ * ++ * urb + dev is in hcd.self.controller.urb_list ++ * we're queueing TDs onto software and hardware lists ++ * ++ * hcd-specific init for hcpriv hasn't been done yet ++ * ++ * NOTE: control, bulk, and interrupt share the same code to append TDs ++ * to a (possibly active) QH, and the same QH scanning code. ++ */ ++static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ++ gfp_t mem_flags) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct list_head qtd_list; ++ ++ INIT_LIST_HEAD(&qtd_list); ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ /* qh_completions() code doesn't handle all the fault cases ++ * in multi-TD control transfers. Even 1KB is rare anyway. ++ */ ++ if (urb->transfer_buffer_length > (16 * 1024)) ++ return -EMSGSIZE; ++ fallthrough; ++ /* case PIPE_BULK: */ ++ default: ++ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) ++ return -ENOMEM; ++ return submit_async(fotg210, urb, &qtd_list, mem_flags); ++ ++ case PIPE_INTERRUPT: ++ if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) ++ return -ENOMEM; ++ return intr_submit(fotg210, urb, &qtd_list, mem_flags); ++ ++ case PIPE_ISOCHRONOUS: ++ return itd_submit(fotg210, urb, mem_flags); ++ } ++} ++ ++/* remove from hardware lists ++ * completions normally happen asynchronously ++ */ ++ ++static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh; ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ rc = usb_hcd_check_unlink_urb(hcd, urb, status); ++ if (rc) ++ goto done; ++ ++ switch (usb_pipetype(urb->pipe)) { ++ /* case PIPE_CONTROL: */ ++ /* case PIPE_BULK:*/ ++ default: ++ qh = (struct fotg210_qh *) urb->hcpriv; ++ if (!qh) ++ break; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ start_unlink_async(fotg210, qh); ++ break; ++ case QH_STATE_UNLINK: ++ case QH_STATE_UNLINK_WAIT: ++ /* already started */ ++ break; ++ case QH_STATE_IDLE: ++ /* QH might be waiting for a Clear-TT-Buffer */ ++ qh_completions(fotg210, qh); ++ break; ++ } ++ break; ++ ++ case PIPE_INTERRUPT: ++ qh = (struct fotg210_qh *) urb->hcpriv; ++ if (!qh) ++ break; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ start_unlink_intr(fotg210, qh); ++ break; ++ case QH_STATE_IDLE: ++ qh_completions(fotg210, qh); ++ break; ++ default: ++ fotg210_dbg(fotg210, "bogus qh %p state %d\n", ++ qh, qh->qh_state); ++ goto done; ++ } ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ /* itd... */ ++ ++ /* wait till next completion, do it then. */ ++ /* completion irqs can wait up to 1024 msec, */ ++ break; ++ } ++done: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ return rc; ++} ++ ++/* bulk qh holds the data toggle */ ++ ++static void fotg210_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ unsigned long flags; ++ struct fotg210_qh *qh, *tmp; ++ ++ /* ASSERT: any requests/urbs are being unlinked */ ++ /* ASSERT: nobody can be submitting urbs for this any more */ ++ ++rescan: ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh = ep->hcpriv; ++ if (!qh) ++ goto done; ++ ++ /* endpoints can be iso streams. for now, we don't ++ * accelerate iso completions ... so spin a while. ++ */ ++ if (qh->hw == NULL) { ++ struct fotg210_iso_stream *stream = ep->hcpriv; ++ ++ if (!list_empty(&stream->td_list)) ++ goto idle_timeout; ++ ++ /* BUG_ON(!list_empty(&stream->free_list)); */ ++ kfree(stream); ++ goto done; ++ } ++ ++ if (fotg210->rh_state < FOTG210_RH_RUNNING) ++ qh->qh_state = QH_STATE_IDLE; ++ switch (qh->qh_state) { ++ case QH_STATE_LINKED: ++ case QH_STATE_COMPLETING: ++ for (tmp = fotg210->async->qh_next.qh; ++ tmp && tmp != qh; ++ tmp = tmp->qh_next.qh) ++ continue; ++ /* periodic qh self-unlinks on empty, and a COMPLETING qh ++ * may already be unlinked. ++ */ ++ if (tmp) ++ start_unlink_async(fotg210, qh); ++ fallthrough; ++ case QH_STATE_UNLINK: /* wait for hw to finish? */ ++ case QH_STATE_UNLINK_WAIT: ++idle_timeout: ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ schedule_timeout_uninterruptible(1); ++ goto rescan; ++ case QH_STATE_IDLE: /* fully unlinked */ ++ if (qh->clearing_tt) ++ goto idle_timeout; ++ if (list_empty(&qh->qtd_list)) { ++ qh_destroy(fotg210, qh); ++ break; ++ } ++ fallthrough; ++ default: ++ /* caller was supposed to have unlinked any requests; ++ * that's not our job. just leak this memory. ++ */ ++ fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", ++ qh, ep->desc.bEndpointAddress, qh->qh_state, ++ list_empty(&qh->qtd_list) ? "" : "(has tds)"); ++ break; ++ } ++done: ++ ep->hcpriv = NULL; ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static void fotg210_endpoint_reset(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ struct fotg210_qh *qh; ++ int eptype = usb_endpoint_type(&ep->desc); ++ int epnum = usb_endpoint_num(&ep->desc); ++ int is_out = usb_endpoint_dir_out(&ep->desc); ++ unsigned long flags; ++ ++ if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) ++ return; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ qh = ep->hcpriv; ++ ++ /* For Bulk and Interrupt endpoints we maintain the toggle state ++ * in the hardware; the toggle bits in udev aren't used at all. ++ * When an endpoint is reset by usb_clear_halt() we must reset ++ * the toggle bit in the QH. ++ */ ++ if (qh) { ++ usb_settoggle(qh->dev, epnum, is_out, 0); ++ if (!list_empty(&qh->qtd_list)) { ++ WARN_ONCE(1, "clear_halt for a busy endpoint\n"); ++ } else if (qh->qh_state == QH_STATE_LINKED || ++ qh->qh_state == QH_STATE_COMPLETING) { ++ ++ /* The toggle value in the QH can't be updated ++ * while the QH is active. Unlink it now; ++ * re-linking will call qh_refresh(). ++ */ ++ if (eptype == USB_ENDPOINT_XFER_BULK) ++ start_unlink_async(fotg210, qh); ++ else ++ start_unlink_intr(fotg210, qh); ++ } ++ } ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++} ++ ++static int fotg210_get_frame(struct usb_hcd *hcd) ++{ ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ return (fotg210_read_frame_index(fotg210) >> 3) % ++ fotg210->periodic_size; ++} ++ ++/* The EHCI in ChipIdea HDRC cannot be a separate module or device, ++ * because its registers (and irq) are shared between host/gadget/otg ++ * functions and in order to facilitate role switching we cannot ++ * give the fotg210 driver exclusive access to those. ++ */ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_LICENSE("GPL"); ++ ++static const struct hc_driver fotg210_fotg210_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Faraday USB2.0 Host Controller", ++ .hcd_priv_size = sizeof(struct fotg210_hcd), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = fotg210_irq, ++ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, ++ ++ /* ++ * basic lifecycle operations ++ */ ++ .reset = hcd_fotg210_init, ++ .start = fotg210_run, ++ .stop = fotg210_stop, ++ .shutdown = fotg210_shutdown, ++ ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ .urb_enqueue = fotg210_urb_enqueue, ++ .urb_dequeue = fotg210_urb_dequeue, ++ .endpoint_disable = fotg210_endpoint_disable, ++ .endpoint_reset = fotg210_endpoint_reset, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = fotg210_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = fotg210_hub_status_data, ++ .hub_control = fotg210_hub_control, ++ .bus_suspend = fotg210_bus_suspend, ++ .bus_resume = fotg210_bus_resume, ++ ++ .relinquish_port = fotg210_relinquish_port, ++ .port_handed_over = fotg210_port_handed_over, ++ ++ .clear_tt_buffer_complete = fotg210_clear_tt_buffer_complete, ++}; ++ ++static void fotg210_init(struct fotg210_hcd *fotg210) ++{ ++ u32 value; ++ ++ iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, ++ &fotg210->regs->gmir); ++ ++ value = ioread32(&fotg210->regs->otgcsr); ++ value &= ~OTGCSR_A_BUS_DROP; ++ value |= OTGCSR_A_BUS_REQ; ++ iowrite32(value, &fotg210->regs->otgcsr); ++} ++ ++/* ++ * fotg210_hcd_probe - initialize faraday FOTG210 HCDs ++ * ++ * Allocates basic resources for this USB host controller, and ++ * then invokes the start() method for the HCD associated with it ++ * through the hotplug entry's driver_data. ++ */ ++static int fotg210_hcd_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct usb_hcd *hcd; ++ struct resource *res; ++ int irq; ++ int retval; ++ struct fotg210_hcd *fotg210; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ pdev->dev.power.power_state = PMSG_ON; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ hcd = usb_create_hcd(&fotg210_fotg210_hc_driver, dev, ++ dev_name(dev)); ++ if (!hcd) { ++ dev_err(dev, "failed to create hcd\n"); ++ retval = -ENOMEM; ++ goto fail_create_hcd; ++ } ++ ++ hcd->has_tt = 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hcd->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hcd->regs)) { ++ retval = PTR_ERR(hcd->regs); ++ goto failed_put_hcd; ++ } ++ ++ hcd->rsrc_start = res->start; ++ hcd->rsrc_len = resource_size(res); ++ ++ fotg210 = hcd_to_fotg210(hcd); ++ ++ fotg210->caps = hcd->regs; ++ ++ /* It's OK not to supply this clock */ ++ fotg210->pclk = clk_get(dev, "PCLK"); ++ if (!IS_ERR(fotg210->pclk)) { ++ retval = clk_prepare_enable(fotg210->pclk); ++ if (retval) { ++ dev_err(dev, "failed to enable PCLK\n"); ++ goto failed_put_hcd; ++ } ++ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { ++ /* ++ * Percolate deferrals, for anything else, ++ * just live without the clocking. ++ */ ++ retval = PTR_ERR(fotg210->pclk); ++ goto failed_dis_clk; ++ } ++ ++ retval = fotg210_setup(hcd); ++ if (retval) ++ goto failed_dis_clk; ++ ++ fotg210_init(fotg210); ++ ++ retval = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (retval) { ++ dev_err(dev, "failed to add hcd with err %d\n", retval); ++ goto failed_dis_clk; ++ } ++ device_wakeup_enable(hcd->self.controller); ++ platform_set_drvdata(pdev, hcd); ++ ++ return retval; ++ ++failed_dis_clk: ++ if (!IS_ERR(fotg210->pclk)) { ++ clk_disable_unprepare(fotg210->pclk); ++ clk_put(fotg210->pclk); ++ } ++failed_put_hcd: ++ usb_put_hcd(hcd); ++fail_create_hcd: ++ dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); ++ return retval; ++} ++ ++/* ++ * fotg210_hcd_remove - shutdown processing for EHCI HCDs ++ * @dev: USB Host Controller being removed ++ * ++ */ ++static int fotg210_hcd_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); ++ ++ if (!IS_ERR(fotg210->pclk)) { ++ clk_disable_unprepare(fotg210->pclk); ++ clk_put(fotg210->pclk); ++ } ++ ++ usb_remove_hcd(hcd); ++ usb_put_hcd(hcd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg210" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, fotg210_of_match); ++#endif ++ ++static struct platform_driver fotg210_hcd_driver = { ++ .driver = { ++ .name = "fotg210-hcd", ++ .of_match_table = of_match_ptr(fotg210_of_match), ++ }, ++ .probe = fotg210_hcd_probe, ++ .remove = fotg210_hcd_remove, ++}; ++ ++static int __init fotg210_hcd_init(void) ++{ ++ int retval = 0; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++ if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || ++ test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) ++ pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); ++ ++ pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", ++ hcd_name, sizeof(struct fotg210_qh), ++ sizeof(struct fotg210_qtd), ++ sizeof(struct fotg210_itd)); ++ ++ fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); ++ ++ retval = platform_driver_register(&fotg210_hcd_driver); ++ if (retval < 0) ++ goto clean; ++ return retval; ++ ++clean: ++ debugfs_remove(fotg210_debug_root); ++ fotg210_debug_root = NULL; ++ ++ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++ return retval; ++} ++module_init(fotg210_hcd_init); ++ ++static void __exit fotg210_hcd_cleanup(void) ++{ ++ platform_driver_unregister(&fotg210_hcd_driver); ++ debugfs_remove(fotg210_debug_root); ++ clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); ++} ++module_exit(fotg210_hcd_cleanup); +--- a/drivers/usb/gadget/udc/fotg210-udc.c ++++ /dev/null +@@ -1,1239 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * FOTG210 UDC Driver supports Bulk transfer so far +- * +- * Copyright (C) 2013 Faraday Technology Corporation +- * +- * Author : Yuan-Hsin Chen +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "fotg210.h" +- +-#define DRIVER_DESC "FOTG210 USB Device Controller Driver" +-#define DRIVER_VERSION "30-April-2013" +- +-static const char udc_name[] = "fotg210_udc"; +-static const char * const fotg210_ep_name[] = { +- "ep0", "ep1", "ep2", "ep3", "ep4"}; +- +-static void fotg210_disable_fifo_int(struct fotg210_ep *ep) +-{ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +- +- if (ep->dir_in) +- value |= DMISGR1_MF_IN_INT(ep->epnum - 1); +- else +- value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +-} +- +-static void fotg210_enable_fifo_int(struct fotg210_ep *ep) +-{ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +- +- if (ep->dir_in) +- value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); +- else +- value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +-} +- +-static void fotg210_set_cxdone(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); +- +- value |= DCFESR_CX_DONE; +- iowrite32(value, fotg210->reg + FOTG210_DCFESR); +-} +- +-static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, +- int status) +-{ +- list_del_init(&req->queue); +- +- /* don't modify queue heads during completion callback */ +- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) +- req->req.status = -ESHUTDOWN; +- else +- req->req.status = status; +- +- spin_unlock(&ep->fotg210->lock); +- usb_gadget_giveback_request(&ep->ep, &req->req); +- spin_lock(&ep->fotg210->lock); +- +- if (ep->epnum) { +- if (list_empty(&ep->queue)) +- fotg210_disable_fifo_int(ep); +- } else { +- fotg210_set_cxdone(ep->fotg210); +- } +-} +- +-static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, +- u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- /* Driver should map an ep to a fifo and then map the fifo +- * to the ep. What a brain-damaged design! +- */ +- +- /* map a fifo to an ep */ +- val = ioread32(fotg210->reg + FOTG210_EPMAP); +- val &= ~EPMAP_FIFONOMSK(epnum, dir_in); +- val |= EPMAP_FIFONO(epnum, dir_in); +- iowrite32(val, fotg210->reg + FOTG210_EPMAP); +- +- /* map the ep to the fifo */ +- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); +- val &= ~FIFOMAP_EPNOMSK(epnum); +- val |= FIFOMAP_EPNO(epnum); +- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +- +- /* enable fifo */ +- val = ioread32(fotg210->reg + FOTG210_FIFOCF); +- val |= FIFOCF_FIFO_EN(epnum - 1); +- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +-} +- +-static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- val = ioread32(fotg210->reg + FOTG210_FIFOMAP); +- val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); +- iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +-} +- +-static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- +- val = ioread32(fotg210->reg + FOTG210_FIFOCF); +- val |= FIFOCF_TYPE(type, epnum - 1); +- iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +-} +- +-static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, +- u32 dir_in) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 val; +- u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : +- FOTG210_OUTEPMPSR(epnum); +- +- val = ioread32(fotg210->reg + offset); +- val |= INOUTEPMPSR_MPS(mps); +- iowrite32(val, fotg210->reg + offset); +-} +- +-static int fotg210_config_ep(struct fotg210_ep *ep, +- const struct usb_endpoint_descriptor *desc) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- +- fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); +- fotg210_set_tfrtype(ep, ep->epnum, ep->type); +- fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); +- fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); +- +- fotg210->ep[ep->epnum] = ep; +- +- return 0; +-} +- +-static int fotg210_ep_enable(struct usb_ep *_ep, +- const struct usb_endpoint_descriptor *desc) +-{ +- struct fotg210_ep *ep; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- ep->desc = desc; +- ep->epnum = usb_endpoint_num(desc); +- ep->type = usb_endpoint_type(desc); +- ep->dir_in = usb_endpoint_dir_in(desc); +- ep->ep.maxpacket = usb_endpoint_maxp(desc); +- +- return fotg210_config_ep(ep, desc); +-} +- +-static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) +-{ +- struct fotg210_ep *ep = fotg210->ep[epnum]; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(epnum); +- +- /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ +- * bit. Controller wouldn't clear this bit. WTF!!! +- */ +- +- value = ioread32(reg); +- value |= INOUTEPMPSR_RESET_TSEQ; +- iowrite32(value, reg); +- +- value = ioread32(reg); +- value &= ~INOUTEPMPSR_RESET_TSEQ; +- iowrite32(value, reg); +-} +- +-static int fotg210_ep_release(struct fotg210_ep *ep) +-{ +- if (!ep->epnum) +- return 0; +- ep->epnum = 0; +- ep->stall = 0; +- ep->wedged = 0; +- +- fotg210_reset_tseq(ep->fotg210, ep->epnum); +- +- return 0; +-} +- +-static int fotg210_ep_disable(struct usb_ep *_ep) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- +- BUG_ON(!_ep); +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- while (!list_empty(&ep->queue)) { +- req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- fotg210_done(ep, req, -ECONNRESET); +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- } +- +- return fotg210_ep_release(ep); +-} +- +-static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, +- gfp_t gfp_flags) +-{ +- struct fotg210_request *req; +- +- req = kzalloc(sizeof(struct fotg210_request), gfp_flags); +- if (!req) +- return NULL; +- +- INIT_LIST_HEAD(&req->queue); +- +- return &req->req; +-} +- +-static void fotg210_ep_free_request(struct usb_ep *_ep, +- struct usb_request *_req) +-{ +- struct fotg210_request *req; +- +- req = container_of(_req, struct fotg210_request, req); +- kfree(req); +-} +- +-static void fotg210_enable_dma(struct fotg210_ep *ep, +- dma_addr_t d, u32 len) +-{ +- u32 value; +- struct fotg210_udc *fotg210 = ep->fotg210; +- +- /* set transfer length and direction */ +- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); +- value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); +- value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); +- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +- +- /* set device DMA target FIFO number */ +- value = ioread32(fotg210->reg + FOTG210_DMATFNR); +- if (ep->epnum) +- value |= DMATFNR_ACC_FN(ep->epnum - 1); +- else +- value |= DMATFNR_ACC_CXF; +- iowrite32(value, fotg210->reg + FOTG210_DMATFNR); +- +- /* set DMA memory address */ +- iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); +- +- /* enable MDMA_EROR and MDMA_CMPLT interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMISGR2); +- value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); +- iowrite32(value, fotg210->reg + FOTG210_DMISGR2); +- +- /* start DMA */ +- value = ioread32(fotg210->reg + FOTG210_DMACPSR1); +- value |= DMACPSR1_DMA_START; +- iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +-} +- +-static void fotg210_disable_dma(struct fotg210_ep *ep) +-{ +- iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); +-} +- +-static void fotg210_wait_dma_done(struct fotg210_ep *ep) +-{ +- u32 value; +- +- do { +- value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); +- if ((value & DISGR2_USBRST_INT) || +- (value & DISGR2_DMA_ERROR)) +- goto dma_reset; +- } while (!(value & DISGR2_DMA_CMPLT)); +- +- value &= ~DISGR2_DMA_CMPLT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); +- return; +- +-dma_reset: +- value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); +- value |= DMACPSR1_DMA_ABORT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); +- +- /* reset fifo */ +- if (ep->epnum) { +- value = ioread32(ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)); +- value |= FIBCR_FFRST; +- iowrite32(value, ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)); +- } else { +- value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); +- value |= DCFESR_CX_CLR; +- iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); +- } +-} +- +-static void fotg210_start_dma(struct fotg210_ep *ep, +- struct fotg210_request *req) +-{ +- struct device *dev = &ep->fotg210->gadget.dev; +- dma_addr_t d; +- u8 *buffer; +- u32 length; +- +- if (ep->epnum) { +- if (ep->dir_in) { +- buffer = req->req.buf; +- length = req->req.length; +- } else { +- buffer = req->req.buf + req->req.actual; +- length = ioread32(ep->fotg210->reg + +- FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; +- if (length > req->req.length - req->req.actual) +- length = req->req.length - req->req.actual; +- } +- } else { +- buffer = req->req.buf + req->req.actual; +- if (req->req.length - req->req.actual > ep->ep.maxpacket) +- length = ep->ep.maxpacket; +- else +- length = req->req.length - req->req.actual; +- } +- +- d = dma_map_single(dev, buffer, length, +- ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); +- +- if (dma_mapping_error(dev, d)) { +- pr_err("dma_mapping_error\n"); +- return; +- } +- +- fotg210_enable_dma(ep, d, length); +- +- /* check if dma is done */ +- fotg210_wait_dma_done(ep); +- +- fotg210_disable_dma(ep); +- +- /* update actual transfer length */ +- req->req.actual += length; +- +- dma_unmap_single(dev, d, length, DMA_TO_DEVICE); +-} +- +-static void fotg210_ep0_queue(struct fotg210_ep *ep, +- struct fotg210_request *req) +-{ +- if (!req->req.length) { +- fotg210_done(ep, req, 0); +- return; +- } +- if (ep->dir_in) { /* if IN */ +- fotg210_start_dma(ep, req); +- if (req->req.length == req->req.actual) +- fotg210_done(ep, req, 0); +- } else { /* OUT */ +- u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); +- +- value &= ~DMISGR0_MCX_OUT_INT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); +- } +-} +- +-static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, +- gfp_t gfp_flags) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- int request = 0; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- req = container_of(_req, struct fotg210_request, req); +- +- if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) +- return -ESHUTDOWN; +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- +- if (list_empty(&ep->queue)) +- request = 1; +- +- list_add_tail(&req->queue, &ep->queue); +- +- req->req.actual = 0; +- req->req.status = -EINPROGRESS; +- +- if (!ep->epnum) /* ep0 */ +- fotg210_ep0_queue(ep, req); +- else if (request && !ep->stall) +- fotg210_enable_fifo_int(ep); +- +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- +- return 0; +-} +- +-static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +-{ +- struct fotg210_ep *ep; +- struct fotg210_request *req; +- unsigned long flags; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- req = container_of(_req, struct fotg210_request, req); +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- if (!list_empty(&ep->queue)) +- fotg210_done(ep, req, -ECONNRESET); +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- +- return 0; +-} +- +-static void fotg210_set_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- /* check if IN FIFO is empty before stall */ +- if (ep->dir_in) { +- do { +- value = ioread32(fotg210->reg + FOTG210_DCFESR); +- } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); +- } +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- value |= INOUTEPMPSR_STL_EP; +- iowrite32(value, reg); +-} +- +-static void fotg210_clear_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- value &= ~INOUTEPMPSR_STL_EP; +- iowrite32(value, reg); +-} +- +-static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +-{ +- struct fotg210_ep *ep; +- struct fotg210_udc *fotg210; +- unsigned long flags; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- +- fotg210 = ep->fotg210; +- +- spin_lock_irqsave(&ep->fotg210->lock, flags); +- +- if (value) { +- fotg210_set_epnstall(ep); +- ep->stall = 1; +- if (wedge) +- ep->wedged = 1; +- } else { +- fotg210_reset_tseq(fotg210, ep->epnum); +- fotg210_clear_epnstall(ep); +- ep->stall = 0; +- ep->wedged = 0; +- if (!list_empty(&ep->queue)) +- fotg210_enable_fifo_int(ep); +- } +- +- spin_unlock_irqrestore(&ep->fotg210->lock, flags); +- return 0; +-} +- +-static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) +-{ +- return fotg210_set_halt_and_wedge(_ep, value, 0); +-} +- +-static int fotg210_ep_set_wedge(struct usb_ep *_ep) +-{ +- return fotg210_set_halt_and_wedge(_ep, 1, 1); +-} +- +-static void fotg210_ep_fifo_flush(struct usb_ep *_ep) +-{ +-} +- +-static const struct usb_ep_ops fotg210_ep_ops = { +- .enable = fotg210_ep_enable, +- .disable = fotg210_ep_disable, +- +- .alloc_request = fotg210_ep_alloc_request, +- .free_request = fotg210_ep_free_request, +- +- .queue = fotg210_ep_queue, +- .dequeue = fotg210_ep_dequeue, +- +- .set_halt = fotg210_ep_set_halt, +- .fifo_flush = fotg210_ep_fifo_flush, +- .set_wedge = fotg210_ep_set_wedge, +-}; +- +-static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); +- +- value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 +- | TX0BYTE_EP4); +- iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); +-} +- +-static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); +- +- value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 +- | RX0BYTE_EP4); +- iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); +-} +- +-/* read 8-byte setup packet only */ +-static void fotg210_rdsetupp(struct fotg210_udc *fotg210, +- u8 *buffer) +-{ +- int i = 0; +- u8 *tmp = buffer; +- u32 data; +- u32 length = 8; +- +- iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); +- +- for (i = (length >> 2); i > 0; i--) { +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- *(tmp + 2) = (data >> 16) & 0xFF; +- *(tmp + 3) = (data >> 24) & 0xFF; +- tmp = tmp + 4; +- } +- +- switch (length % 4) { +- case 1: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- break; +- case 2: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- break; +- case 3: +- data = ioread32(fotg210->reg + FOTG210_CXPORT); +- *tmp = data & 0xFF; +- *(tmp + 1) = (data >> 8) & 0xFF; +- *(tmp + 2) = (data >> 16) & 0xFF; +- break; +- default: +- break; +- } +- +- iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); +-} +- +-static void fotg210_set_configuration(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DAR); +- +- value |= DAR_AFT_CONF; +- iowrite32(value, fotg210->reg + FOTG210_DAR); +-} +- +-static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DAR); +- +- value |= (addr & 0x7F); +- iowrite32(value, fotg210->reg + FOTG210_DAR); +-} +- +-static void fotg210_set_cxstall(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); +- +- value |= DCFESR_CX_STL; +- iowrite32(value, fotg210->reg + FOTG210_DCFESR); +-} +- +-static void fotg210_request_error(struct fotg210_udc *fotg210) +-{ +- fotg210_set_cxstall(fotg210); +- pr_err("request error!!\n"); +-} +- +-static void fotg210_set_address(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- if (le16_to_cpu(ctrl->wValue) >= 0x0100) { +- fotg210_request_error(fotg210); +- } else { +- fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); +- fotg210_set_cxdone(fotg210); +- } +-} +- +-static void fotg210_set_feature(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_INTERFACE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_ENDPOINT: { +- u8 epnum; +- epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; +- if (epnum) +- fotg210_set_epnstall(fotg210->ep[epnum]); +- else +- fotg210_set_cxstall(fotg210); +- fotg210_set_cxdone(fotg210); +- } +- break; +- default: +- fotg210_request_error(fotg210); +- break; +- } +-} +- +-static void fotg210_clear_feature(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- struct fotg210_ep *ep = +- fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; +- +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_INTERFACE: +- fotg210_set_cxdone(fotg210); +- break; +- case USB_RECIP_ENDPOINT: +- if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { +- if (ep->wedged) { +- fotg210_set_cxdone(fotg210); +- break; +- } +- if (ep->stall) +- fotg210_set_halt_and_wedge(&ep->ep, 0, 0); +- } +- fotg210_set_cxdone(fotg210); +- break; +- default: +- fotg210_request_error(fotg210); +- break; +- } +-} +- +-static int fotg210_is_epnstall(struct fotg210_ep *ep) +-{ +- struct fotg210_udc *fotg210 = ep->fotg210; +- u32 value; +- void __iomem *reg; +- +- reg = (ep->dir_in) ? +- fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : +- fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); +- value = ioread32(reg); +- return value & INOUTEPMPSR_STL_EP ? 1 : 0; +-} +- +-/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ +-static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) +-{ +- struct fotg210_ep *ep; +- struct fotg210_udc *fotg210; +- +- ep = container_of(_ep, struct fotg210_ep, ep); +- fotg210 = ep->fotg210; +- +- if (req->status || req->actual != req->length) { +- dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); +- } +-} +- +-static void fotg210_get_status(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- u8 epnum; +- +- switch (ctrl->bRequestType & USB_RECIP_MASK) { +- case USB_RECIP_DEVICE: +- fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); +- break; +- case USB_RECIP_INTERFACE: +- fotg210->ep0_data = cpu_to_le16(0); +- break; +- case USB_RECIP_ENDPOINT: +- epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; +- if (epnum) +- fotg210->ep0_data = +- cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) +- << USB_ENDPOINT_HALT); +- else +- fotg210_request_error(fotg210); +- break; +- +- default: +- fotg210_request_error(fotg210); +- return; /* exit */ +- } +- +- fotg210->ep0_req->buf = &fotg210->ep0_data; +- fotg210->ep0_req->length = 2; +- +- spin_unlock(&fotg210->lock); +- fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); +- spin_lock(&fotg210->lock); +-} +- +-static int fotg210_setup_packet(struct fotg210_udc *fotg210, +- struct usb_ctrlrequest *ctrl) +-{ +- u8 *p = (u8 *)ctrl; +- u8 ret = 0; +- +- fotg210_rdsetupp(fotg210, p); +- +- fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; +- +- if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { +- u32 value = ioread32(fotg210->reg + FOTG210_DMCR); +- fotg210->gadget.speed = value & DMCR_HS_EN ? +- USB_SPEED_HIGH : USB_SPEED_FULL; +- } +- +- /* check request */ +- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { +- switch (ctrl->bRequest) { +- case USB_REQ_GET_STATUS: +- fotg210_get_status(fotg210, ctrl); +- break; +- case USB_REQ_CLEAR_FEATURE: +- fotg210_clear_feature(fotg210, ctrl); +- break; +- case USB_REQ_SET_FEATURE: +- fotg210_set_feature(fotg210, ctrl); +- break; +- case USB_REQ_SET_ADDRESS: +- fotg210_set_address(fotg210, ctrl); +- break; +- case USB_REQ_SET_CONFIGURATION: +- fotg210_set_configuration(fotg210); +- ret = 1; +- break; +- default: +- ret = 1; +- break; +- } +- } else { +- ret = 1; +- } +- +- return ret; +-} +- +-static void fotg210_ep0out(struct fotg210_udc *fotg210) +-{ +- struct fotg210_ep *ep = fotg210->ep[0]; +- +- if (!list_empty(&ep->queue) && !ep->dir_in) { +- struct fotg210_request *req; +- +- req = list_first_entry(&ep->queue, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- +- if ((req->req.length - req->req.actual) < ep->ep.maxpacket) +- fotg210_done(ep, req, 0); +- } else { +- pr_err("%s : empty queue\n", __func__); +- } +-} +- +-static void fotg210_ep0in(struct fotg210_udc *fotg210) +-{ +- struct fotg210_ep *ep = fotg210->ep[0]; +- +- if ((!list_empty(&ep->queue)) && (ep->dir_in)) { +- struct fotg210_request *req; +- +- req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- +- if (req->req.actual == req->req.length) +- fotg210_done(ep, req, 0); +- } else { +- fotg210_set_cxdone(fotg210); +- } +-} +- +-static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); +- +- value &= ~DISGR0_CX_COMABT_INT; +- iowrite32(value, fotg210->reg + FOTG210_DISGR0); +-} +- +-static void fotg210_in_fifo_handler(struct fotg210_ep *ep) +-{ +- struct fotg210_request *req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- +- if (req->req.length) +- fotg210_start_dma(ep, req); +- fotg210_done(ep, req, 0); +-} +- +-static void fotg210_out_fifo_handler(struct fotg210_ep *ep) +-{ +- struct fotg210_request *req = list_entry(ep->queue.next, +- struct fotg210_request, queue); +- int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); +- +- fotg210_start_dma(ep, req); +- +- /* Complete the request when it's full or a short packet arrived. +- * Like other drivers, short_not_ok isn't handled. +- */ +- +- if (req->req.length == req->req.actual || +- (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) +- fotg210_done(ep, req, 0); +-} +- +-static irqreturn_t fotg210_irq(int irq, void *_fotg210) +-{ +- struct fotg210_udc *fotg210 = _fotg210; +- u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); +- u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); +- +- int_grp &= ~int_msk; +- +- spin_lock(&fotg210->lock); +- +- if (int_grp & DIGR_INT_G2) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR2; +- u32 int_grp2 = ioread32(reg); +- u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); +- u32 value; +- +- int_grp2 &= ~int_msk2; +- +- if (int_grp2 & DISGR2_USBRST_INT) { +- usb_gadget_udc_reset(&fotg210->gadget, +- fotg210->driver); +- value = ioread32(reg); +- value &= ~DISGR2_USBRST_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc reset\n"); +- } +- if (int_grp2 & DISGR2_SUSP_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_SUSP_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc suspend\n"); +- } +- if (int_grp2 & DISGR2_RESM_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_RESM_INT; +- iowrite32(value, reg); +- pr_info("fotg210 udc resume\n"); +- } +- if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ERR_INT; +- iowrite32(value, reg); +- pr_info("fotg210 iso sequence error\n"); +- } +- if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ABORT_INT; +- iowrite32(value, reg); +- pr_info("fotg210 iso sequence abort\n"); +- } +- if (int_grp2 & DISGR2_TX0BYTE_INT) { +- fotg210_clear_tx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_TX0BYTE_INT; +- iowrite32(value, reg); +- pr_info("fotg210 transferred 0 byte\n"); +- } +- if (int_grp2 & DISGR2_RX0BYTE_INT) { +- fotg210_clear_rx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_RX0BYTE_INT; +- iowrite32(value, reg); +- pr_info("fotg210 received 0 byte\n"); +- } +- if (int_grp2 & DISGR2_DMA_ERROR) { +- value = ioread32(reg); +- value &= ~DISGR2_DMA_ERROR; +- iowrite32(value, reg); +- } +- } +- +- if (int_grp & DIGR_INT_G0) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR0; +- u32 int_grp0 = ioread32(reg); +- u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); +- struct usb_ctrlrequest ctrl; +- +- int_grp0 &= ~int_msk0; +- +- /* the highest priority in this source register */ +- if (int_grp0 & DISGR0_CX_COMABT_INT) { +- fotg210_clear_comabt_int(fotg210); +- pr_info("fotg210 CX command abort\n"); +- } +- +- if (int_grp0 & DISGR0_CX_SETUP_INT) { +- if (fotg210_setup_packet(fotg210, &ctrl)) { +- spin_unlock(&fotg210->lock); +- if (fotg210->driver->setup(&fotg210->gadget, +- &ctrl) < 0) +- fotg210_set_cxstall(fotg210); +- spin_lock(&fotg210->lock); +- } +- } +- if (int_grp0 & DISGR0_CX_COMEND_INT) +- pr_info("fotg210 cmd end\n"); +- +- if (int_grp0 & DISGR0_CX_IN_INT) +- fotg210_ep0in(fotg210); +- +- if (int_grp0 & DISGR0_CX_OUT_INT) +- fotg210_ep0out(fotg210); +- +- if (int_grp0 & DISGR0_CX_COMFAIL_INT) { +- fotg210_set_cxstall(fotg210); +- pr_info("fotg210 ep0 fail\n"); +- } +- } +- +- if (int_grp & DIGR_INT_G1) { +- void __iomem *reg = fotg210->reg + FOTG210_DISGR1; +- u32 int_grp1 = ioread32(reg); +- u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); +- int fifo; +- +- int_grp1 &= ~int_msk1; +- +- for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { +- if (int_grp1 & DISGR1_IN_INT(fifo)) +- fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); +- +- if ((int_grp1 & DISGR1_OUT_INT(fifo)) || +- (int_grp1 & DISGR1_SPK_INT(fifo))) +- fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); +- } +- } +- +- spin_unlock(&fotg210->lock); +- +- return IRQ_HANDLED; +-} +- +-static void fotg210_disable_unplug(struct fotg210_udc *fotg210) +-{ +- u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); +- +- reg &= ~PHYTMSR_UNPLUG; +- iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); +-} +- +-static int fotg210_udc_start(struct usb_gadget *g, +- struct usb_gadget_driver *driver) +-{ +- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); +- u32 value; +- +- /* hook up the driver */ +- fotg210->driver = driver; +- +- /* enable device global interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMCR); +- value |= DMCR_GLINT_EN; +- iowrite32(value, fotg210->reg + FOTG210_DMCR); +- +- return 0; +-} +- +-static void fotg210_init(struct fotg210_udc *fotg210) +-{ +- u32 value; +- +- /* disable global interrupt and set int polarity to active high */ +- iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, +- fotg210->reg + FOTG210_GMIR); +- +- /* disable device global interrupt */ +- value = ioread32(fotg210->reg + FOTG210_DMCR); +- value &= ~DMCR_GLINT_EN; +- iowrite32(value, fotg210->reg + FOTG210_DMCR); +- +- /* enable only grp2 irqs we handle */ +- iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT +- | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT +- | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), +- fotg210->reg + FOTG210_DMISGR2); +- +- /* disable all fifo interrupt */ +- iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); +- +- /* disable cmd end */ +- value = ioread32(fotg210->reg + FOTG210_DMISGR0); +- value |= DMISGR0_MCX_COMEND; +- iowrite32(value, fotg210->reg + FOTG210_DMISGR0); +-} +- +-static int fotg210_udc_stop(struct usb_gadget *g) +-{ +- struct fotg210_udc *fotg210 = gadget_to_fotg210(g); +- unsigned long flags; +- +- spin_lock_irqsave(&fotg210->lock, flags); +- +- fotg210_init(fotg210); +- fotg210->driver = NULL; +- +- spin_unlock_irqrestore(&fotg210->lock, flags); +- +- return 0; +-} +- +-static const struct usb_gadget_ops fotg210_gadget_ops = { +- .udc_start = fotg210_udc_start, +- .udc_stop = fotg210_udc_stop, +-}; +- +-static int fotg210_udc_remove(struct platform_device *pdev) +-{ +- struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); +- int i; +- +- usb_del_gadget_udc(&fotg210->gadget); +- iounmap(fotg210->reg); +- free_irq(platform_get_irq(pdev, 0), fotg210); +- +- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) +- kfree(fotg210->ep[i]); +- kfree(fotg210); +- +- return 0; +-} +- +-static int fotg210_udc_probe(struct platform_device *pdev) +-{ +- struct resource *res, *ires; +- struct fotg210_udc *fotg210 = NULL; +- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; +- int ret = 0; +- int i; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- pr_err("platform_get_resource error.\n"); +- return -ENODEV; +- } +- +- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!ires) { +- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); +- return -ENODEV; +- } +- +- ret = -ENOMEM; +- +- /* initialize udc */ +- fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); +- if (fotg210 == NULL) +- goto err; +- +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); +- if (_ep[i] == NULL) +- goto err_alloc; +- fotg210->ep[i] = _ep[i]; +- } +- +- fotg210->reg = ioremap(res->start, resource_size(res)); +- if (fotg210->reg == NULL) { +- pr_err("ioremap error.\n"); +- goto err_alloc; +- } +- +- spin_lock_init(&fotg210->lock); +- +- platform_set_drvdata(pdev, fotg210); +- +- fotg210->gadget.ops = &fotg210_gadget_ops; +- +- fotg210->gadget.max_speed = USB_SPEED_HIGH; +- fotg210->gadget.dev.parent = &pdev->dev; +- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; +- fotg210->gadget.name = udc_name; +- +- INIT_LIST_HEAD(&fotg210->gadget.ep_list); +- +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- struct fotg210_ep *ep = fotg210->ep[i]; +- +- if (i) { +- INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); +- list_add_tail(&fotg210->ep[i]->ep.ep_list, +- &fotg210->gadget.ep_list); +- } +- ep->fotg210 = fotg210; +- INIT_LIST_HEAD(&ep->queue); +- ep->ep.name = fotg210_ep_name[i]; +- ep->ep.ops = &fotg210_ep_ops; +- usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); +- +- if (i == 0) { +- ep->ep.caps.type_control = true; +- } else { +- ep->ep.caps.type_iso = true; +- ep->ep.caps.type_bulk = true; +- ep->ep.caps.type_int = true; +- } +- +- ep->ep.caps.dir_in = true; +- ep->ep.caps.dir_out = true; +- } +- usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); +- fotg210->gadget.ep0 = &fotg210->ep[0]->ep; +- INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); +- +- fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, +- GFP_KERNEL); +- if (fotg210->ep0_req == NULL) +- goto err_map; +- +- fotg210->ep0_req->complete = fotg210_ep0_complete; +- +- fotg210_init(fotg210); +- +- fotg210_disable_unplug(fotg210); +- +- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, +- udc_name, fotg210); +- if (ret < 0) { +- pr_err("request_irq error (%d)\n", ret); +- goto err_req; +- } +- +- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); +- if (ret) +- goto err_add_udc; +- +- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); +- +- return 0; +- +-err_add_udc: +- free_irq(ires->start, fotg210); +- +-err_req: +- fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); +- +-err_map: +- iounmap(fotg210->reg); +- +-err_alloc: +- for (i = 0; i < FOTG210_MAX_NUM_EP; i++) +- kfree(fotg210->ep[i]); +- kfree(fotg210); +- +-err: +- return ret; +-} +- +-static struct platform_driver fotg210_driver = { +- .driver = { +- .name = udc_name, +- }, +- .probe = fotg210_udc_probe, +- .remove = fotg210_udc_remove, +-}; +- +-module_platform_driver(fotg210_driver); +- +-MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION(DRIVER_DESC); +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -0,0 +1,1239 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * FOTG210 UDC Driver supports Bulk transfer so far ++ * ++ * Copyright (C) 2013 Faraday Technology Corporation ++ * ++ * Author : Yuan-Hsin Chen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fotg210-udc.h" ++ ++#define DRIVER_DESC "FOTG210 USB Device Controller Driver" ++#define DRIVER_VERSION "30-April-2013" ++ ++static const char udc_name[] = "fotg210_udc"; ++static const char * const fotg210_ep_name[] = { ++ "ep0", "ep1", "ep2", "ep3", "ep4"}; ++ ++static void fotg210_disable_fifo_int(struct fotg210_ep *ep) ++{ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); ++ ++ if (ep->dir_in) ++ value |= DMISGR1_MF_IN_INT(ep->epnum - 1); ++ else ++ value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); ++} ++ ++static void fotg210_enable_fifo_int(struct fotg210_ep *ep) ++{ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); ++ ++ if (ep->dir_in) ++ value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); ++ else ++ value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); ++} ++ ++static void fotg210_set_cxdone(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ ++ value |= DCFESR_CX_DONE; ++ iowrite32(value, fotg210->reg + FOTG210_DCFESR); ++} ++ ++static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, ++ int status) ++{ ++ list_del_init(&req->queue); ++ ++ /* don't modify queue heads during completion callback */ ++ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) ++ req->req.status = -ESHUTDOWN; ++ else ++ req->req.status = status; ++ ++ spin_unlock(&ep->fotg210->lock); ++ usb_gadget_giveback_request(&ep->ep, &req->req); ++ spin_lock(&ep->fotg210->lock); ++ ++ if (ep->epnum) { ++ if (list_empty(&ep->queue)) ++ fotg210_disable_fifo_int(ep); ++ } else { ++ fotg210_set_cxdone(ep->fotg210); ++ } ++} ++ ++static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, ++ u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ /* Driver should map an ep to a fifo and then map the fifo ++ * to the ep. What a brain-damaged design! ++ */ ++ ++ /* map a fifo to an ep */ ++ val = ioread32(fotg210->reg + FOTG210_EPMAP); ++ val &= ~EPMAP_FIFONOMSK(epnum, dir_in); ++ val |= EPMAP_FIFONO(epnum, dir_in); ++ iowrite32(val, fotg210->reg + FOTG210_EPMAP); ++ ++ /* map the ep to the fifo */ ++ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); ++ val &= ~FIFOMAP_EPNOMSK(epnum); ++ val |= FIFOMAP_EPNO(epnum); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); ++ ++ /* enable fifo */ ++ val = ioread32(fotg210->reg + FOTG210_FIFOCF); ++ val |= FIFOCF_FIFO_EN(epnum - 1); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); ++} ++ ++static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ val = ioread32(fotg210->reg + FOTG210_FIFOMAP); ++ val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); ++} ++ ++static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ ++ val = ioread32(fotg210->reg + FOTG210_FIFOCF); ++ val |= FIFOCF_TYPE(type, epnum - 1); ++ iowrite32(val, fotg210->reg + FOTG210_FIFOCF); ++} ++ ++static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, ++ u32 dir_in) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 val; ++ u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : ++ FOTG210_OUTEPMPSR(epnum); ++ ++ val = ioread32(fotg210->reg + offset); ++ val |= INOUTEPMPSR_MPS(mps); ++ iowrite32(val, fotg210->reg + offset); ++} ++ ++static int fotg210_config_ep(struct fotg210_ep *ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ ++ fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); ++ fotg210_set_tfrtype(ep, ep->epnum, ep->type); ++ fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); ++ fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); ++ ++ fotg210->ep[ep->epnum] = ep; ++ ++ return 0; ++} ++ ++static int fotg210_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct fotg210_ep *ep; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ ep->desc = desc; ++ ep->epnum = usb_endpoint_num(desc); ++ ep->type = usb_endpoint_type(desc); ++ ep->dir_in = usb_endpoint_dir_in(desc); ++ ep->ep.maxpacket = usb_endpoint_maxp(desc); ++ ++ return fotg210_config_ep(ep, desc); ++} ++ ++static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) ++{ ++ struct fotg210_ep *ep = fotg210->ep[epnum]; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(epnum); ++ ++ /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ ++ * bit. Controller wouldn't clear this bit. WTF!!! ++ */ ++ ++ value = ioread32(reg); ++ value |= INOUTEPMPSR_RESET_TSEQ; ++ iowrite32(value, reg); ++ ++ value = ioread32(reg); ++ value &= ~INOUTEPMPSR_RESET_TSEQ; ++ iowrite32(value, reg); ++} ++ ++static int fotg210_ep_release(struct fotg210_ep *ep) ++{ ++ if (!ep->epnum) ++ return 0; ++ ep->epnum = 0; ++ ep->stall = 0; ++ ep->wedged = 0; ++ ++ fotg210_reset_tseq(ep->fotg210, ep->epnum); ++ ++ return 0; ++} ++ ++static int fotg210_ep_disable(struct usb_ep *_ep) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ ++ BUG_ON(!_ep); ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ fotg210_done(ep, req, -ECONNRESET); ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ } ++ ++ return fotg210_ep_release(ep); ++} ++ ++static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, ++ gfp_t gfp_flags) ++{ ++ struct fotg210_request *req; ++ ++ req = kzalloc(sizeof(struct fotg210_request), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ INIT_LIST_HEAD(&req->queue); ++ ++ return &req->req; ++} ++ ++static void fotg210_ep_free_request(struct usb_ep *_ep, ++ struct usb_request *_req) ++{ ++ struct fotg210_request *req; ++ ++ req = container_of(_req, struct fotg210_request, req); ++ kfree(req); ++} ++ ++static void fotg210_enable_dma(struct fotg210_ep *ep, ++ dma_addr_t d, u32 len) ++{ ++ u32 value; ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ ++ /* set transfer length and direction */ ++ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); ++ value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); ++ value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); ++ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); ++ ++ /* set device DMA target FIFO number */ ++ value = ioread32(fotg210->reg + FOTG210_DMATFNR); ++ if (ep->epnum) ++ value |= DMATFNR_ACC_FN(ep->epnum - 1); ++ else ++ value |= DMATFNR_ACC_CXF; ++ iowrite32(value, fotg210->reg + FOTG210_DMATFNR); ++ ++ /* set DMA memory address */ ++ iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); ++ ++ /* enable MDMA_EROR and MDMA_CMPLT interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMISGR2); ++ value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); ++ iowrite32(value, fotg210->reg + FOTG210_DMISGR2); ++ ++ /* start DMA */ ++ value = ioread32(fotg210->reg + FOTG210_DMACPSR1); ++ value |= DMACPSR1_DMA_START; ++ iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); ++} ++ ++static void fotg210_disable_dma(struct fotg210_ep *ep) ++{ ++ iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); ++} ++ ++static void fotg210_wait_dma_done(struct fotg210_ep *ep) ++{ ++ u32 value; ++ ++ do { ++ value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); ++ if ((value & DISGR2_USBRST_INT) || ++ (value & DISGR2_DMA_ERROR)) ++ goto dma_reset; ++ } while (!(value & DISGR2_DMA_CMPLT)); ++ ++ value &= ~DISGR2_DMA_CMPLT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); ++ return; ++ ++dma_reset: ++ value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); ++ value |= DMACPSR1_DMA_ABORT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); ++ ++ /* reset fifo */ ++ if (ep->epnum) { ++ value = ioread32(ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)); ++ value |= FIBCR_FFRST; ++ iowrite32(value, ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)); ++ } else { ++ value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); ++ value |= DCFESR_CX_CLR; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); ++ } ++} ++ ++static void fotg210_start_dma(struct fotg210_ep *ep, ++ struct fotg210_request *req) ++{ ++ struct device *dev = &ep->fotg210->gadget.dev; ++ dma_addr_t d; ++ u8 *buffer; ++ u32 length; ++ ++ if (ep->epnum) { ++ if (ep->dir_in) { ++ buffer = req->req.buf; ++ length = req->req.length; ++ } else { ++ buffer = req->req.buf + req->req.actual; ++ length = ioread32(ep->fotg210->reg + ++ FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; ++ if (length > req->req.length - req->req.actual) ++ length = req->req.length - req->req.actual; ++ } ++ } else { ++ buffer = req->req.buf + req->req.actual; ++ if (req->req.length - req->req.actual > ep->ep.maxpacket) ++ length = ep->ep.maxpacket; ++ else ++ length = req->req.length - req->req.actual; ++ } ++ ++ d = dma_map_single(dev, buffer, length, ++ ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(dev, d)) { ++ pr_err("dma_mapping_error\n"); ++ return; ++ } ++ ++ fotg210_enable_dma(ep, d, length); ++ ++ /* check if dma is done */ ++ fotg210_wait_dma_done(ep); ++ ++ fotg210_disable_dma(ep); ++ ++ /* update actual transfer length */ ++ req->req.actual += length; ++ ++ dma_unmap_single(dev, d, length, DMA_TO_DEVICE); ++} ++ ++static void fotg210_ep0_queue(struct fotg210_ep *ep, ++ struct fotg210_request *req) ++{ ++ if (!req->req.length) { ++ fotg210_done(ep, req, 0); ++ return; ++ } ++ if (ep->dir_in) { /* if IN */ ++ fotg210_start_dma(ep, req); ++ if (req->req.length == req->req.actual) ++ fotg210_done(ep, req, 0); ++ } else { /* OUT */ ++ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); ++ ++ value &= ~DMISGR0_MCX_OUT_INT; ++ iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); ++ } ++} ++ ++static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, ++ gfp_t gfp_flags) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ int request = 0; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ req = container_of(_req, struct fotg210_request, req); ++ ++ if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ ++ if (list_empty(&ep->queue)) ++ request = 1; ++ ++ list_add_tail(&req->queue, &ep->queue); ++ ++ req->req.actual = 0; ++ req->req.status = -EINPROGRESS; ++ ++ if (!ep->epnum) /* ep0 */ ++ fotg210_ep0_queue(ep, req); ++ else if (request && !ep->stall) ++ fotg210_enable_fifo_int(ep); ++ ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_request *req; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ req = container_of(_req, struct fotg210_request, req); ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ if (!list_empty(&ep->queue)) ++ fotg210_done(ep, req, -ECONNRESET); ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static void fotg210_set_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ /* check if IN FIFO is empty before stall */ ++ if (ep->dir_in) { ++ do { ++ value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); ++ } ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ value |= INOUTEPMPSR_STL_EP; ++ iowrite32(value, reg); ++} ++ ++static void fotg210_clear_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ value &= ~INOUTEPMPSR_STL_EP; ++ iowrite32(value, reg); ++} ++ ++static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_udc *fotg210; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ ++ fotg210 = ep->fotg210; ++ ++ spin_lock_irqsave(&ep->fotg210->lock, flags); ++ ++ if (value) { ++ fotg210_set_epnstall(ep); ++ ep->stall = 1; ++ if (wedge) ++ ep->wedged = 1; ++ } else { ++ fotg210_reset_tseq(fotg210, ep->epnum); ++ fotg210_clear_epnstall(ep); ++ ep->stall = 0; ++ ep->wedged = 0; ++ if (!list_empty(&ep->queue)) ++ fotg210_enable_fifo_int(ep); ++ } ++ ++ spin_unlock_irqrestore(&ep->fotg210->lock, flags); ++ return 0; ++} ++ ++static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ return fotg210_set_halt_and_wedge(_ep, value, 0); ++} ++ ++static int fotg210_ep_set_wedge(struct usb_ep *_ep) ++{ ++ return fotg210_set_halt_and_wedge(_ep, 1, 1); ++} ++ ++static void fotg210_ep_fifo_flush(struct usb_ep *_ep) ++{ ++} ++ ++static const struct usb_ep_ops fotg210_ep_ops = { ++ .enable = fotg210_ep_enable, ++ .disable = fotg210_ep_disable, ++ ++ .alloc_request = fotg210_ep_alloc_request, ++ .free_request = fotg210_ep_free_request, ++ ++ .queue = fotg210_ep_queue, ++ .dequeue = fotg210_ep_dequeue, ++ ++ .set_halt = fotg210_ep_set_halt, ++ .fifo_flush = fotg210_ep_fifo_flush, ++ .set_wedge = fotg210_ep_set_wedge, ++}; ++ ++static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); ++ ++ value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 ++ | TX0BYTE_EP4); ++ iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); ++} ++ ++static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); ++ ++ value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 ++ | RX0BYTE_EP4); ++ iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); ++} ++ ++/* read 8-byte setup packet only */ ++static void fotg210_rdsetupp(struct fotg210_udc *fotg210, ++ u8 *buffer) ++{ ++ int i = 0; ++ u8 *tmp = buffer; ++ u32 data; ++ u32 length = 8; ++ ++ iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); ++ ++ for (i = (length >> 2); i > 0; i--) { ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ *(tmp + 2) = (data >> 16) & 0xFF; ++ *(tmp + 3) = (data >> 24) & 0xFF; ++ tmp = tmp + 4; ++ } ++ ++ switch (length % 4) { ++ case 1: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ break; ++ case 2: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ break; ++ case 3: ++ data = ioread32(fotg210->reg + FOTG210_CXPORT); ++ *tmp = data & 0xFF; ++ *(tmp + 1) = (data >> 8) & 0xFF; ++ *(tmp + 2) = (data >> 16) & 0xFF; ++ break; ++ default: ++ break; ++ } ++ ++ iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); ++} ++ ++static void fotg210_set_configuration(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DAR); ++ ++ value |= DAR_AFT_CONF; ++ iowrite32(value, fotg210->reg + FOTG210_DAR); ++} ++ ++static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DAR); ++ ++ value |= (addr & 0x7F); ++ iowrite32(value, fotg210->reg + FOTG210_DAR); ++} ++ ++static void fotg210_set_cxstall(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); ++ ++ value |= DCFESR_CX_STL; ++ iowrite32(value, fotg210->reg + FOTG210_DCFESR); ++} ++ ++static void fotg210_request_error(struct fotg210_udc *fotg210) ++{ ++ fotg210_set_cxstall(fotg210); ++ pr_err("request error!!\n"); ++} ++ ++static void fotg210_set_address(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ if (le16_to_cpu(ctrl->wValue) >= 0x0100) { ++ fotg210_request_error(fotg210); ++ } else { ++ fotg210_set_dev_addr(fotg210, le16_to_cpu(ctrl->wValue)); ++ fotg210_set_cxdone(fotg210); ++ } ++} ++ ++static void fotg210_set_feature(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_ENDPOINT: { ++ u8 epnum; ++ epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; ++ if (epnum) ++ fotg210_set_epnstall(fotg210->ep[epnum]); ++ else ++ fotg210_set_cxstall(fotg210); ++ fotg210_set_cxdone(fotg210); ++ } ++ break; ++ default: ++ fotg210_request_error(fotg210); ++ break; ++ } ++} ++ ++static void fotg210_clear_feature(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ struct fotg210_ep *ep = ++ fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; ++ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210_set_cxdone(fotg210); ++ break; ++ case USB_RECIP_ENDPOINT: ++ if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { ++ if (ep->wedged) { ++ fotg210_set_cxdone(fotg210); ++ break; ++ } ++ if (ep->stall) ++ fotg210_set_halt_and_wedge(&ep->ep, 0, 0); ++ } ++ fotg210_set_cxdone(fotg210); ++ break; ++ default: ++ fotg210_request_error(fotg210); ++ break; ++ } ++} ++ ++static int fotg210_is_epnstall(struct fotg210_ep *ep) ++{ ++ struct fotg210_udc *fotg210 = ep->fotg210; ++ u32 value; ++ void __iomem *reg; ++ ++ reg = (ep->dir_in) ? ++ fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : ++ fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); ++ value = ioread32(reg); ++ return value & INOUTEPMPSR_STL_EP ? 1 : 0; ++} ++ ++/* For EP0 requests triggered by this driver (currently GET_STATUS response) */ ++static void fotg210_ep0_complete(struct usb_ep *_ep, struct usb_request *req) ++{ ++ struct fotg210_ep *ep; ++ struct fotg210_udc *fotg210; ++ ++ ep = container_of(_ep, struct fotg210_ep, ep); ++ fotg210 = ep->fotg210; ++ ++ if (req->status || req->actual != req->length) { ++ dev_warn(&fotg210->gadget.dev, "EP0 request failed: %d\n", req->status); ++ } ++} ++ ++static void fotg210_get_status(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ u8 epnum; ++ ++ switch (ctrl->bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ fotg210->ep0_data = cpu_to_le16(1 << USB_DEVICE_SELF_POWERED); ++ break; ++ case USB_RECIP_INTERFACE: ++ fotg210->ep0_data = cpu_to_le16(0); ++ break; ++ case USB_RECIP_ENDPOINT: ++ epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; ++ if (epnum) ++ fotg210->ep0_data = ++ cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum]) ++ << USB_ENDPOINT_HALT); ++ else ++ fotg210_request_error(fotg210); ++ break; ++ ++ default: ++ fotg210_request_error(fotg210); ++ return; /* exit */ ++ } ++ ++ fotg210->ep0_req->buf = &fotg210->ep0_data; ++ fotg210->ep0_req->length = 2; ++ ++ spin_unlock(&fotg210->lock); ++ fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC); ++ spin_lock(&fotg210->lock); ++} ++ ++static int fotg210_setup_packet(struct fotg210_udc *fotg210, ++ struct usb_ctrlrequest *ctrl) ++{ ++ u8 *p = (u8 *)ctrl; ++ u8 ret = 0; ++ ++ fotg210_rdsetupp(fotg210, p); ++ ++ fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; ++ ++ if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { ++ u32 value = ioread32(fotg210->reg + FOTG210_DMCR); ++ fotg210->gadget.speed = value & DMCR_HS_EN ? ++ USB_SPEED_HIGH : USB_SPEED_FULL; ++ } ++ ++ /* check request */ ++ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { ++ switch (ctrl->bRequest) { ++ case USB_REQ_GET_STATUS: ++ fotg210_get_status(fotg210, ctrl); ++ break; ++ case USB_REQ_CLEAR_FEATURE: ++ fotg210_clear_feature(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_FEATURE: ++ fotg210_set_feature(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_ADDRESS: ++ fotg210_set_address(fotg210, ctrl); ++ break; ++ case USB_REQ_SET_CONFIGURATION: ++ fotg210_set_configuration(fotg210); ++ ret = 1; ++ break; ++ default: ++ ret = 1; ++ break; ++ } ++ } else { ++ ret = 1; ++ } ++ ++ return ret; ++} ++ ++static void fotg210_ep0out(struct fotg210_udc *fotg210) ++{ ++ struct fotg210_ep *ep = fotg210->ep[0]; ++ ++ if (!list_empty(&ep->queue) && !ep->dir_in) { ++ struct fotg210_request *req; ++ ++ req = list_first_entry(&ep->queue, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ ++ if ((req->req.length - req->req.actual) < ep->ep.maxpacket) ++ fotg210_done(ep, req, 0); ++ } else { ++ pr_err("%s : empty queue\n", __func__); ++ } ++} ++ ++static void fotg210_ep0in(struct fotg210_udc *fotg210) ++{ ++ struct fotg210_ep *ep = fotg210->ep[0]; ++ ++ if ((!list_empty(&ep->queue)) && (ep->dir_in)) { ++ struct fotg210_request *req; ++ ++ req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ ++ if (req->req.actual == req->req.length) ++ fotg210_done(ep, req, 0); ++ } else { ++ fotg210_set_cxdone(fotg210); ++ } ++} ++ ++static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) ++{ ++ u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); ++ ++ value &= ~DISGR0_CX_COMABT_INT; ++ iowrite32(value, fotg210->reg + FOTG210_DISGR0); ++} ++ ++static void fotg210_in_fifo_handler(struct fotg210_ep *ep) ++{ ++ struct fotg210_request *req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ ++ if (req->req.length) ++ fotg210_start_dma(ep, req); ++ fotg210_done(ep, req, 0); ++} ++ ++static void fotg210_out_fifo_handler(struct fotg210_ep *ep) ++{ ++ struct fotg210_request *req = list_entry(ep->queue.next, ++ struct fotg210_request, queue); ++ int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); ++ ++ fotg210_start_dma(ep, req); ++ ++ /* Complete the request when it's full or a short packet arrived. ++ * Like other drivers, short_not_ok isn't handled. ++ */ ++ ++ if (req->req.length == req->req.actual || ++ (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) ++ fotg210_done(ep, req, 0); ++} ++ ++static irqreturn_t fotg210_irq(int irq, void *_fotg210) ++{ ++ struct fotg210_udc *fotg210 = _fotg210; ++ u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); ++ u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); ++ ++ int_grp &= ~int_msk; ++ ++ spin_lock(&fotg210->lock); ++ ++ if (int_grp & DIGR_INT_G2) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR2; ++ u32 int_grp2 = ioread32(reg); ++ u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); ++ u32 value; ++ ++ int_grp2 &= ~int_msk2; ++ ++ if (int_grp2 & DISGR2_USBRST_INT) { ++ usb_gadget_udc_reset(&fotg210->gadget, ++ fotg210->driver); ++ value = ioread32(reg); ++ value &= ~DISGR2_USBRST_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc reset\n"); ++ } ++ if (int_grp2 & DISGR2_SUSP_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_SUSP_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc suspend\n"); ++ } ++ if (int_grp2 & DISGR2_RESM_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_RESM_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 udc resume\n"); ++ } ++ if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_ISO_SEQ_ERR_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 iso sequence error\n"); ++ } ++ if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { ++ value = ioread32(reg); ++ value &= ~DISGR2_ISO_SEQ_ABORT_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 iso sequence abort\n"); ++ } ++ if (int_grp2 & DISGR2_TX0BYTE_INT) { ++ fotg210_clear_tx0byte(fotg210); ++ value = ioread32(reg); ++ value &= ~DISGR2_TX0BYTE_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 transferred 0 byte\n"); ++ } ++ if (int_grp2 & DISGR2_RX0BYTE_INT) { ++ fotg210_clear_rx0byte(fotg210); ++ value = ioread32(reg); ++ value &= ~DISGR2_RX0BYTE_INT; ++ iowrite32(value, reg); ++ pr_info("fotg210 received 0 byte\n"); ++ } ++ if (int_grp2 & DISGR2_DMA_ERROR) { ++ value = ioread32(reg); ++ value &= ~DISGR2_DMA_ERROR; ++ iowrite32(value, reg); ++ } ++ } ++ ++ if (int_grp & DIGR_INT_G0) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR0; ++ u32 int_grp0 = ioread32(reg); ++ u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); ++ struct usb_ctrlrequest ctrl; ++ ++ int_grp0 &= ~int_msk0; ++ ++ /* the highest priority in this source register */ ++ if (int_grp0 & DISGR0_CX_COMABT_INT) { ++ fotg210_clear_comabt_int(fotg210); ++ pr_info("fotg210 CX command abort\n"); ++ } ++ ++ if (int_grp0 & DISGR0_CX_SETUP_INT) { ++ if (fotg210_setup_packet(fotg210, &ctrl)) { ++ spin_unlock(&fotg210->lock); ++ if (fotg210->driver->setup(&fotg210->gadget, ++ &ctrl) < 0) ++ fotg210_set_cxstall(fotg210); ++ spin_lock(&fotg210->lock); ++ } ++ } ++ if (int_grp0 & DISGR0_CX_COMEND_INT) ++ pr_info("fotg210 cmd end\n"); ++ ++ if (int_grp0 & DISGR0_CX_IN_INT) ++ fotg210_ep0in(fotg210); ++ ++ if (int_grp0 & DISGR0_CX_OUT_INT) ++ fotg210_ep0out(fotg210); ++ ++ if (int_grp0 & DISGR0_CX_COMFAIL_INT) { ++ fotg210_set_cxstall(fotg210); ++ pr_info("fotg210 ep0 fail\n"); ++ } ++ } ++ ++ if (int_grp & DIGR_INT_G1) { ++ void __iomem *reg = fotg210->reg + FOTG210_DISGR1; ++ u32 int_grp1 = ioread32(reg); ++ u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); ++ int fifo; ++ ++ int_grp1 &= ~int_msk1; ++ ++ for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { ++ if (int_grp1 & DISGR1_IN_INT(fifo)) ++ fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); ++ ++ if ((int_grp1 & DISGR1_OUT_INT(fifo)) || ++ (int_grp1 & DISGR1_SPK_INT(fifo))) ++ fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); ++ } ++ } ++ ++ spin_unlock(&fotg210->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static void fotg210_disable_unplug(struct fotg210_udc *fotg210) ++{ ++ u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); ++ ++ reg &= ~PHYTMSR_UNPLUG; ++ iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); ++} ++ ++static int fotg210_udc_start(struct usb_gadget *g, ++ struct usb_gadget_driver *driver) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ u32 value; ++ ++ /* hook up the driver */ ++ fotg210->driver = driver; ++ ++ /* enable device global interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value |= DMCR_GLINT_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ ++ return 0; ++} ++ ++static void fotg210_init(struct fotg210_udc *fotg210) ++{ ++ u32 value; ++ ++ /* disable global interrupt and set int polarity to active high */ ++ iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, ++ fotg210->reg + FOTG210_GMIR); ++ ++ /* disable device global interrupt */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value &= ~DMCR_GLINT_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ ++ /* enable only grp2 irqs we handle */ ++ iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT ++ | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT ++ | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), ++ fotg210->reg + FOTG210_DMISGR2); ++ ++ /* disable all fifo interrupt */ ++ iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); ++ ++ /* disable cmd end */ ++ value = ioread32(fotg210->reg + FOTG210_DMISGR0); ++ value |= DMISGR0_MCX_COMEND; ++ iowrite32(value, fotg210->reg + FOTG210_DMISGR0); ++} ++ ++static int fotg210_udc_stop(struct usb_gadget *g) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fotg210->lock, flags); ++ ++ fotg210_init(fotg210); ++ fotg210->driver = NULL; ++ ++ spin_unlock_irqrestore(&fotg210->lock, flags); ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops fotg210_gadget_ops = { ++ .udc_start = fotg210_udc_start, ++ .udc_stop = fotg210_udc_stop, ++}; ++ ++static int fotg210_udc_remove(struct platform_device *pdev) ++{ ++ struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); ++ int i; ++ ++ usb_del_gadget_udc(&fotg210->gadget); ++ iounmap(fotg210->reg); ++ free_irq(platform_get_irq(pdev, 0), fotg210); ++ ++ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) ++ kfree(fotg210->ep[i]); ++ kfree(fotg210); ++ ++ return 0; ++} ++ ++static int fotg210_udc_probe(struct platform_device *pdev) ++{ ++ struct resource *res, *ires; ++ struct fotg210_udc *fotg210 = NULL; ++ struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; ++ int ret = 0; ++ int i; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ pr_err("platform_get_resource error.\n"); ++ return -ENODEV; ++ } ++ ++ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!ires) { ++ pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); ++ return -ENODEV; ++ } ++ ++ ret = -ENOMEM; ++ ++ /* initialize udc */ ++ fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); ++ if (fotg210 == NULL) ++ goto err; ++ ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { ++ _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); ++ if (_ep[i] == NULL) ++ goto err_alloc; ++ fotg210->ep[i] = _ep[i]; ++ } ++ ++ fotg210->reg = ioremap(res->start, resource_size(res)); ++ if (fotg210->reg == NULL) { ++ pr_err("ioremap error.\n"); ++ goto err_alloc; ++ } ++ ++ spin_lock_init(&fotg210->lock); ++ ++ platform_set_drvdata(pdev, fotg210); ++ ++ fotg210->gadget.ops = &fotg210_gadget_ops; ++ ++ fotg210->gadget.max_speed = USB_SPEED_HIGH; ++ fotg210->gadget.dev.parent = &pdev->dev; ++ fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ fotg210->gadget.name = udc_name; ++ ++ INIT_LIST_HEAD(&fotg210->gadget.ep_list); ++ ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { ++ struct fotg210_ep *ep = fotg210->ep[i]; ++ ++ if (i) { ++ INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); ++ list_add_tail(&fotg210->ep[i]->ep.ep_list, ++ &fotg210->gadget.ep_list); ++ } ++ ep->fotg210 = fotg210; ++ INIT_LIST_HEAD(&ep->queue); ++ ep->ep.name = fotg210_ep_name[i]; ++ ep->ep.ops = &fotg210_ep_ops; ++ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ++ ++ if (i == 0) { ++ ep->ep.caps.type_control = true; ++ } else { ++ ep->ep.caps.type_iso = true; ++ ep->ep.caps.type_bulk = true; ++ ep->ep.caps.type_int = true; ++ } ++ ++ ep->ep.caps.dir_in = true; ++ ep->ep.caps.dir_out = true; ++ } ++ usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); ++ fotg210->gadget.ep0 = &fotg210->ep[0]->ep; ++ INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); ++ ++ fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, ++ GFP_KERNEL); ++ if (fotg210->ep0_req == NULL) ++ goto err_map; ++ ++ fotg210->ep0_req->complete = fotg210_ep0_complete; ++ ++ fotg210_init(fotg210); ++ ++ fotg210_disable_unplug(fotg210); ++ ++ ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, ++ udc_name, fotg210); ++ if (ret < 0) { ++ pr_err("request_irq error (%d)\n", ret); ++ goto err_req; ++ } ++ ++ ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); ++ if (ret) ++ goto err_add_udc; ++ ++ dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); ++ ++ return 0; ++ ++err_add_udc: ++ free_irq(ires->start, fotg210); ++ ++err_req: ++ fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); ++ ++err_map: ++ iounmap(fotg210->reg); ++ ++err_alloc: ++ for (i = 0; i < FOTG210_MAX_NUM_EP; i++) ++ kfree(fotg210->ep[i]); ++ kfree(fotg210); ++ ++err: ++ return ret; ++} ++ ++static struct platform_driver fotg210_driver = { ++ .driver = { ++ .name = udc_name, ++ }, ++ .probe = fotg210_udc_probe, ++ .remove = fotg210_udc_remove, ++}; ++ ++module_platform_driver(fotg210_driver); ++ ++MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION(DRIVER_DESC); +--- a/drivers/usb/gadget/udc/Kconfig ++++ b/drivers/usb/gadget/udc/Kconfig +@@ -108,17 +108,6 @@ config USB_FUSB300 + help + Faraday usb device controller FUSB300 driver + +-config USB_FOTG210_UDC +- depends on HAS_DMA +- tristate "Faraday FOTG210 USB Peripheral Controller" +- help +- Faraday USB2.0 OTG controller which can be configured as +- high speed or full speed USB device. This driver supppors +- Bulk Transfer so far. +- +- Say "y" to link the driver statically, or "m" to build a +- dynamically linked module called "fotg210_udc". +- + config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA +--- a/drivers/usb/gadget/udc/Makefile ++++ b/drivers/usb/gadget/udc/Makefile +@@ -34,7 +34,6 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o + obj-$(CONFIG_USB_MV_UDC) += mv_udc.o + mv_udc-y := mv_udc_core.o + obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o + obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o + obj-$(CONFIG_USB_GR_UDC) += gr_udc.o + obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -389,17 +389,6 @@ config USB_ISP1362_HCD + To compile this driver as a module, choose M here: the + module will be called isp1362-hcd. + +-config USB_FOTG210_HCD +- tristate "FOTG210 HCD support" +- depends on USB && HAS_DMA && HAS_IOMEM +- help +- Faraday FOTG210 is an OTG controller which can be configured as +- an USB2.0 host. It is designed to meet USB2.0 EHCI specification +- with minor modification. +- +- To compile this driver as a module, choose M here: the +- module will be called fotg210-hcd. +- + config USB_MAX3421_HCD + tristate "MAX3421 HCD (USB-over-SPI) support" + depends on USB && SPI +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -84,6 +84,5 @@ obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o + obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o + obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o + obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +-obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o + obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o + obj-$(CONFIG_USB_XEN_HCD) += xen-hcd.o +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-hcd.h +@@ -0,0 +1,688 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_FOTG210_H ++#define __LINUX_FOTG210_H ++ ++#include ++ ++/* definitions used for the EHCI driver */ ++ ++/* ++ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to ++ * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on ++ * the host controller implementation. ++ * ++ * To facilitate the strongest possible byte-order checking from "sparse" ++ * and so on, we use __leXX unless that's not practical. ++ */ ++#define __hc32 __le32 ++#define __hc16 __le16 ++ ++/* statistics can be kept for tuning/monitoring */ ++struct fotg210_stats { ++ /* irq usage */ ++ unsigned long normal; ++ unsigned long error; ++ unsigned long iaa; ++ unsigned long lost_iaa; ++ ++ /* termination of urbs from core */ ++ unsigned long complete; ++ unsigned long unlink; ++}; ++ ++/* fotg210_hcd->lock guards shared data against other CPUs: ++ * fotg210_hcd: async, unlink, periodic (and shadow), ... ++ * usb_host_endpoint: hcpriv ++ * fotg210_qh: qh_next, qtd_list ++ * fotg210_qtd: qtd_list ++ * ++ * Also, hold this lock when talking to HC registers or ++ * when updating hw_* fields in shared qh/qtd/... structures. ++ */ ++ ++#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ ++ ++/* ++ * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the ++ * controller may be doing DMA. Lower values mean there's no DMA. ++ */ ++enum fotg210_rh_state { ++ FOTG210_RH_HALTED, ++ FOTG210_RH_SUSPENDED, ++ FOTG210_RH_RUNNING, ++ FOTG210_RH_STOPPING ++}; ++ ++/* ++ * Timer events, ordered by increasing delay length. ++ * Always update event_delays_ns[] and event_handlers[] (defined in ++ * ehci-timer.c) in parallel with this list. ++ */ ++enum fotg210_hrtimer_event { ++ FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ ++ FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ ++ FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ ++ FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ ++ FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ ++ FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ ++ FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ ++ FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ ++ FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ ++ FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ ++ FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ ++}; ++#define FOTG210_HRTIMER_NO_EVENT 99 ++ ++struct fotg210_hcd { /* one per controller */ ++ /* timing support */ ++ enum fotg210_hrtimer_event next_hrtimer_event; ++ unsigned enabled_hrtimer_events; ++ ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; ++ struct hrtimer hrtimer; ++ ++ int PSS_poll_count; ++ int ASS_poll_count; ++ int died_poll_count; ++ ++ /* glue to PCI and HCD framework */ ++ struct fotg210_caps __iomem *caps; ++ struct fotg210_regs __iomem *regs; ++ struct ehci_dbg_port __iomem *debug; ++ ++ __u32 hcs_params; /* cached register copy */ ++ spinlock_t lock; ++ enum fotg210_rh_state rh_state; ++ ++ /* general schedule support */ ++ bool scanning:1; ++ bool need_rescan:1; ++ bool intr_unlinking:1; ++ bool async_unlinking:1; ++ bool shutdown:1; ++ struct fotg210_qh *qh_scan_next; ++ ++ /* async schedule support */ ++ struct fotg210_qh *async; ++ struct fotg210_qh *dummy; /* For AMD quirk use */ ++ struct fotg210_qh *async_unlink; ++ struct fotg210_qh *async_unlink_last; ++ struct fotg210_qh *async_iaa; ++ unsigned async_unlink_cycle; ++ unsigned async_count; /* async activity count */ ++ ++ /* periodic schedule support */ ++#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ ++ unsigned periodic_size; ++ __hc32 *periodic; /* hw periodic table */ ++ dma_addr_t periodic_dma; ++ struct list_head intr_qh_list; ++ unsigned i_thresh; /* uframes HC might cache */ ++ ++ union fotg210_shadow *pshadow; /* mirror hw periodic table */ ++ struct fotg210_qh *intr_unlink; ++ struct fotg210_qh *intr_unlink_last; ++ unsigned intr_unlink_cycle; ++ unsigned now_frame; /* frame from HC hardware */ ++ unsigned next_frame; /* scan periodic, start here */ ++ unsigned intr_count; /* intr activity count */ ++ unsigned isoc_count; /* isoc activity count */ ++ unsigned periodic_count; /* periodic activity count */ ++ /* max periodic time per uframe */ ++ unsigned uframe_periodic_max; ++ ++ ++ /* list of itds completed while now_frame was still active */ ++ struct list_head cached_itd_list; ++ struct fotg210_itd *last_itd_to_free; ++ ++ /* per root hub port */ ++ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; ++ ++ /* bit vectors (one bit per port) ++ * which ports were already suspended at the start of a bus suspend ++ */ ++ unsigned long bus_suspended; ++ ++ /* which ports are edicated to the companion controller */ ++ unsigned long companion_ports; ++ ++ /* which ports are owned by the companion during a bus suspend */ ++ unsigned long owned_ports; ++ ++ /* which ports have the change-suspend feature turned on */ ++ unsigned long port_c_suspend; ++ ++ /* which ports are suspended */ ++ unsigned long suspended_ports; ++ ++ /* which ports have started to resume */ ++ unsigned long resuming_ports; ++ ++ /* per-HC memory pools (could be per-bus, but ...) */ ++ struct dma_pool *qh_pool; /* qh per active urb */ ++ struct dma_pool *qtd_pool; /* one or more per qh */ ++ struct dma_pool *itd_pool; /* itd per iso urb */ ++ ++ unsigned random_frame; ++ unsigned long next_statechange; ++ ktime_t last_periodic_enable; ++ u32 command; ++ ++ /* SILICON QUIRKS */ ++ unsigned need_io_watchdog:1; ++ unsigned fs_i_thresh:1; /* Intel iso scheduling */ ++ ++ u8 sbrn; /* packed release number */ ++ ++ /* irq statistics */ ++#ifdef FOTG210_STATS ++ struct fotg210_stats stats; ++# define INCR(x) ((x)++) ++#else ++# define INCR(x) do {} while (0) ++#endif ++ ++ /* silicon clock */ ++ struct clk *pclk; ++}; ++ ++/* convert between an HCD pointer and the corresponding FOTG210_HCD */ ++static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) ++{ ++ return (struct fotg210_hcd *)(hcd->hcd_priv); ++} ++static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) ++{ ++ return container_of((void *) fotg210, struct usb_hcd, hcd_priv); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ ++ ++/* Section 2.2 Host Controller Capability Registers */ ++struct fotg210_caps { ++ /* these fields are specified as 8 and 16 bit registers, ++ * but some hosts can't perform 8 or 16 bit PCI accesses. ++ * some hosts treat caplength and hciversion as parts of a 32-bit ++ * register, others treat them as two separate registers, this ++ * affects the memory map for big endian controllers. ++ */ ++ u32 hc_capbase; ++#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ ++ (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) ++#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ ++ (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) ++ u32 hcs_params; /* HCSPARAMS - offset 0x4 */ ++#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ ++ ++ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ ++#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ ++#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ ++ u8 portroute[8]; /* nibbles for routing - offset 0xC */ ++}; ++ ++ ++/* Section 2.3 Host Controller Operational Registers */ ++struct fotg210_regs { ++ ++ /* USBCMD: offset 0x00 */ ++ u32 command; ++ ++/* EHCI 1.1 addendum */ ++/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ ++#define CMD_PARK (1<<11) /* enable "park" on async qh */ ++#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ ++#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ ++#define CMD_ASE (1<<5) /* async schedule enable */ ++#define CMD_PSE (1<<4) /* periodic schedule enable */ ++/* 3:2 is periodic frame list size */ ++#define CMD_RESET (1<<1) /* reset HC not bus */ ++#define CMD_RUN (1<<0) /* start/stop HC */ ++ ++ /* USBSTS: offset 0x04 */ ++ u32 status; ++#define STS_ASS (1<<15) /* Async Schedule Status */ ++#define STS_PSS (1<<14) /* Periodic Schedule Status */ ++#define STS_RECL (1<<13) /* Reclamation */ ++#define STS_HALT (1<<12) /* Not running (any reason) */ ++/* some bits reserved */ ++ /* these STS_* flags are also intr_enable bits (USBINTR) */ ++#define STS_IAA (1<<5) /* Interrupted on async advance */ ++#define STS_FATAL (1<<4) /* such as some PCI access errors */ ++#define STS_FLR (1<<3) /* frame list rolled over */ ++#define STS_PCD (1<<2) /* port change detect */ ++#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ ++#define STS_INT (1<<0) /* "normal" completion (short, ...) */ ++ ++ /* USBINTR: offset 0x08 */ ++ u32 intr_enable; ++ ++ /* FRINDEX: offset 0x0C */ ++ u32 frame_index; /* current microframe number */ ++ /* CTRLDSSEGMENT: offset 0x10 */ ++ u32 segment; /* address bits 63:32 if needed */ ++ /* PERIODICLISTBASE: offset 0x14 */ ++ u32 frame_list; /* points to periodic list */ ++ /* ASYNCLISTADDR: offset 0x18 */ ++ u32 async_next; /* address of next async queue head */ ++ ++ u32 reserved1; ++ /* PORTSC: offset 0x20 */ ++ u32 port_status; ++/* 31:23 reserved */ ++#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ ++#define PORT_RESET (1<<8) /* reset port */ ++#define PORT_SUSPEND (1<<7) /* suspend port */ ++#define PORT_RESUME (1<<6) /* resume it */ ++#define PORT_PEC (1<<3) /* port enable change */ ++#define PORT_PE (1<<2) /* port enable */ ++#define PORT_CSC (1<<1) /* connect status change */ ++#define PORT_CONNECT (1<<0) /* device connected */ ++#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) ++ u32 reserved2[19]; ++ ++ /* OTGCSR: offet 0x70 */ ++ u32 otgcsr; ++#define OTGCSR_HOST_SPD_TYP (3 << 22) ++#define OTGCSR_A_BUS_DROP (1 << 5) ++#define OTGCSR_A_BUS_REQ (1 << 4) ++ ++ /* OTGISR: offset 0x74 */ ++ u32 otgisr; ++#define OTGISR_OVC (1 << 10) ++ ++ u32 reserved3[15]; ++ ++ /* GMIR: offset 0xB4 */ ++ u32 gmir; ++#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ ++#define GMIR_MHC_INT (1 << 2) ++#define GMIR_MOTG_INT (1 << 1) ++#define GMIR_MDEV_INT (1 << 0) ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) ++ ++/* ++ * EHCI Specification 0.95 Section 3.5 ++ * QTD: describe data transfer components (buffer, direction, ...) ++ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". ++ * ++ * These are associated only with "QH" (Queue Head) structures, ++ * used with control, bulk, and interrupt transfers. ++ */ ++struct fotg210_qtd { ++ /* first part defined by EHCI spec */ ++ __hc32 hw_next; /* see EHCI 3.5.1 */ ++ __hc32 hw_alt_next; /* see EHCI 3.5.2 */ ++ __hc32 hw_token; /* see EHCI 3.5.3 */ ++#define QTD_TOGGLE (1 << 31) /* data toggle */ ++#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) ++#define QTD_IOC (1 << 15) /* interrupt on complete */ ++#define QTD_CERR(tok) (((tok)>>10) & 0x3) ++#define QTD_PID(tok) (((tok)>>8) & 0x3) ++#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ ++#define QTD_STS_HALT (1 << 6) /* halted on error */ ++#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ ++#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ ++#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ ++#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ ++#define QTD_STS_STS (1 << 1) /* split transaction state */ ++#define QTD_STS_PING (1 << 0) /* issue PING? */ ++ ++#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) ++#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) ++#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) ++ ++ __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ ++ __hc32 hw_buf_hi[5]; /* Appendix B */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t qtd_dma; /* qtd address */ ++ struct list_head qtd_list; /* sw qtd list */ ++ struct urb *urb; /* qtd's urb */ ++ size_t length; /* length of buffer */ ++} __aligned(32); ++ ++/* mask NakCnt+T in qh->hw_alt_next */ ++#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) ++ ++#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* type tag from {qh,itd,fstn}->hw_next */ ++#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) ++ ++/* ++ * Now the following defines are not converted using the ++ * cpu_to_le32() macro anymore, since we have to support ++ * "dynamic" switching between be and le support, so that the driver ++ * can be used on one system with SoC EHCI controller using big-endian ++ * descriptors as well as a normal little-endian PCI EHCI controller. ++ */ ++/* values for that type tag */ ++#define Q_TYPE_ITD (0 << 1) ++#define Q_TYPE_QH (1 << 1) ++#define Q_TYPE_SITD (2 << 1) ++#define Q_TYPE_FSTN (3 << 1) ++ ++/* next async queue entry, or pointer to interrupt/periodic QH */ ++#define QH_NEXT(fotg210, dma) \ ++ (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) ++ ++/* for periodic/async schedules and qtd lists, mark end of list */ ++#define FOTG210_LIST_END(fotg210) \ ++ cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ ++ ++/* ++ * Entries in periodic shadow table are pointers to one of four kinds ++ * of data structure. That's dictated by the hardware; a type tag is ++ * encoded in the low bits of the hardware's periodic schedule. Use ++ * Q_NEXT_TYPE to get the tag. ++ * ++ * For entries in the async schedule, the type tag always says "qh". ++ */ ++union fotg210_shadow { ++ struct fotg210_qh *qh; /* Q_TYPE_QH */ ++ struct fotg210_itd *itd; /* Q_TYPE_ITD */ ++ struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ ++ __hc32 *hw_next; /* (all types) */ ++ void *ptr; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.95 Section 3.6 ++ * QH: describes control/bulk/interrupt endpoints ++ * See Fig 3-7 "Queue Head Structure Layout". ++ * ++ * These appear in both the async and (for interrupt) periodic schedules. ++ */ ++ ++/* first part defined by EHCI spec */ ++struct fotg210_qh_hw { ++ __hc32 hw_next; /* see EHCI 3.6.1 */ ++ __hc32 hw_info1; /* see EHCI 3.6.2 */ ++#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ ++#define QH_HEAD (1 << 15) /* Head of async reclamation list */ ++#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ ++#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ ++#define QH_LOW_SPEED (1 << 12) ++#define QH_FULL_SPEED (0 << 12) ++#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ ++ __hc32 hw_info2; /* see EHCI 3.6.2 */ ++#define QH_SMASK 0x000000ff ++#define QH_CMASK 0x0000ff00 ++#define QH_HUBADDR 0x007f0000 ++#define QH_HUBPORT 0x3f800000 ++#define QH_MULT 0xc0000000 ++ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ ++ ++ /* qtd overlay (hardware parts of a struct fotg210_qtd) */ ++ __hc32 hw_qtd_next; ++ __hc32 hw_alt_next; ++ __hc32 hw_token; ++ __hc32 hw_buf[5]; ++ __hc32 hw_buf_hi[5]; ++} __aligned(32); ++ ++struct fotg210_qh { ++ struct fotg210_qh_hw *hw; /* Must come first */ ++ /* the rest is HCD-private */ ++ dma_addr_t qh_dma; /* address of qh */ ++ union fotg210_shadow qh_next; /* ptr to qh; or periodic */ ++ struct list_head qtd_list; /* sw qtd list */ ++ struct list_head intr_node; /* list of intr QHs */ ++ struct fotg210_qtd *dummy; ++ struct fotg210_qh *unlink_next; /* next on unlink list */ ++ ++ unsigned unlink_cycle; ++ ++ u8 needs_rescan; /* Dequeue during giveback */ ++ u8 qh_state; ++#define QH_STATE_LINKED 1 /* HC sees this */ ++#define QH_STATE_UNLINK 2 /* HC may still see this */ ++#define QH_STATE_IDLE 3 /* HC doesn't see this */ ++#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ ++#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ ++ ++ u8 xacterrs; /* XactErr retry counter */ ++#define QH_XACTERR_MAX 32 /* XactErr retry limit */ ++ ++ /* periodic schedule info */ ++ u8 usecs; /* intr bandwidth */ ++ u8 gap_uf; /* uframes split/csplit gap */ ++ u8 c_usecs; /* ... split completion bw */ ++ u16 tt_usecs; /* tt downstream bandwidth */ ++ unsigned short period; /* polling interval */ ++ unsigned short start; /* where polling starts */ ++#define NO_FRAME ((unsigned short)~0) /* pick new start */ ++ ++ struct usb_device *dev; /* access to TT */ ++ unsigned is_out:1; /* bulk or intr OUT */ ++ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* description of one iso transaction (up to 3 KB data if highspeed) */ ++struct fotg210_iso_packet { ++ /* These will be copied to iTD when scheduling */ ++ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ ++ __hc32 transaction; /* itd->hw_transaction[i] |= */ ++ u8 cross; /* buf crosses pages */ ++ /* for full speed OUT splits */ ++ u32 buf1; ++}; ++ ++/* temporary schedule data for packets from iso urbs (both speeds) ++ * each packet is one logical usb transaction to the device (not TT), ++ * beginning at stream->next_uframe ++ */ ++struct fotg210_iso_sched { ++ struct list_head td_list; ++ unsigned span; ++ struct fotg210_iso_packet packet[]; ++}; ++ ++/* ++ * fotg210_iso_stream - groups all (s)itds for this endpoint. ++ * acts like a qh would, if EHCI had them for ISO. ++ */ ++struct fotg210_iso_stream { ++ /* first field matches fotg210_hq, but is NULL */ ++ struct fotg210_qh_hw *hw; ++ ++ u8 bEndpointAddress; ++ u8 highspeed; ++ struct list_head td_list; /* queued itds */ ++ struct list_head free_list; /* list of unused itds */ ++ struct usb_device *udev; ++ struct usb_host_endpoint *ep; ++ ++ /* output of (re)scheduling */ ++ int next_uframe; ++ __hc32 splits; ++ ++ /* the rest is derived from the endpoint descriptor, ++ * trusting urb->interval == f(epdesc->bInterval) and ++ * including the extra info for hw_bufp[0..2] ++ */ ++ u8 usecs, c_usecs; ++ u16 interval; ++ u16 tt_usecs; ++ u16 maxp; ++ u16 raw_mask; ++ unsigned bandwidth; ++ ++ /* This is used to initialize iTD's hw_bufp fields */ ++ __hc32 buf0; ++ __hc32 buf1; ++ __hc32 buf2; ++ ++ /* this is used to initialize sITD's tt info */ ++ __hc32 address; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.95 Section 3.3 ++ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" ++ * ++ * Schedule records for high speed iso xfers ++ */ ++struct fotg210_itd { ++ /* first part defined by EHCI spec */ ++ __hc32 hw_next; /* see EHCI 3.3.1 */ ++ __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ ++#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ ++#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ ++#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ ++#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ ++#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) ++#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ ++ ++#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) ++ ++ __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ ++ __hc32 hw_bufp_hi[7]; /* Appendix B */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t itd_dma; /* for this itd */ ++ union fotg210_shadow itd_next; /* ptr to periodic q entry */ ++ ++ struct urb *urb; ++ struct fotg210_iso_stream *stream; /* endpoint's queue */ ++ struct list_head itd_list; /* list of stream's itds */ ++ ++ /* any/all hw_transactions here may be used by that urb */ ++ unsigned frame; /* where scheduled */ ++ unsigned pg; ++ unsigned index[8]; /* in urb->iso_frame_desc */ ++} __aligned(32); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * EHCI Specification 0.96 Section 3.7 ++ * Periodic Frame Span Traversal Node (FSTN) ++ * ++ * Manages split interrupt transactions (using TT) that span frame boundaries ++ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN ++ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until ++ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. ++ */ ++struct fotg210_fstn { ++ __hc32 hw_next; /* any periodic q entry */ ++ __hc32 hw_prev; /* qh or FOTG210_LIST_END */ ++ ++ /* the rest is HCD-private */ ++ dma_addr_t fstn_dma; ++ union fotg210_shadow fstn_next; /* ptr to periodic q entry */ ++} __aligned(32); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Prepare the PORTSC wakeup flags during controller suspend/resume */ ++ ++#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ ++ fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) ++ ++#define fotg210_prepare_ports_for_controller_resume(fotg210) \ ++ fotg210_adjust_port_wakeup_flags(fotg210, false, false) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Some EHCI controllers have a Transaction Translator built into the ++ * root hub. This is a non-standard feature. Each controller will need ++ * to add code to the following inline functions, and call them as ++ * needed (mostly in root hub code). ++ */ ++ ++static inline unsigned int ++fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) ++{ ++ return (readl(&fotg210->regs->otgcsr) ++ & OTGCSR_HOST_SPD_TYP) >> 22; ++} ++ ++/* Returns the speed of a device attached to a port on the root hub. */ ++static inline unsigned int ++fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) ++{ ++ switch (fotg210_get_speed(fotg210, portsc)) { ++ case 0: ++ return 0; ++ case 1: ++ return USB_PORT_STAT_LOW_SPEED; ++ case 2: ++ default: ++ return USB_PORT_STAT_HIGH_SPEED; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define fotg210_has_fsl_portno_bug(e) (0) ++ ++/* ++ * While most USB host controllers implement their registers in ++ * little-endian format, a minority (celleb companion chip) implement ++ * them in big endian format. ++ * ++ * This attempts to support either format at compile time without a ++ * runtime penalty, or both formats with the additional overhead ++ * of checking a flag bit. ++ * ++ */ ++ ++#define fotg210_big_endian_mmio(e) 0 ++#define fotg210_big_endian_capbase(e) 0 ++ ++static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, ++ __u32 __iomem *regs) ++{ ++ return readl(regs); ++} ++ ++static inline void fotg210_writel(const struct fotg210_hcd *fotg210, ++ const unsigned int val, __u32 __iomem *regs) ++{ ++ writel(val, regs); ++} ++ ++/* cpu to fotg210 */ ++static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) ++{ ++ return cpu_to_le32(x); ++} ++ ++/* fotg210 to cpu */ ++static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) ++{ ++ return le32_to_cpu(x); ++} ++ ++static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, ++ const __hc32 *x) ++{ ++ return le32_to_cpup(x); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) ++{ ++ return fotg210_readl(fotg210, &fotg210->regs->frame_index); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#endif /* __LINUX_FOTG210_H */ +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -0,0 +1,249 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Faraday FOTG210 USB OTG controller ++ * ++ * Copyright (C) 2013 Faraday Technology Corporation ++ * Author: Yuan-Hsin Chen ++ */ ++ ++#include ++ ++#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ ++#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ ++ ++/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ ++#define FOTG210_GMIR 0xC4 ++#define GMIR_INT_POLARITY 0x8 /*Active High*/ ++#define GMIR_MHC_INT 0x4 ++#define GMIR_MOTG_INT 0x2 ++#define GMIR_MDEV_INT 0x1 ++ ++/* Device Main Control Register(0x100) */ ++#define FOTG210_DMCR 0x100 ++#define DMCR_HS_EN (1 << 6) ++#define DMCR_CHIP_EN (1 << 5) ++#define DMCR_SFRST (1 << 4) ++#define DMCR_GOSUSP (1 << 3) ++#define DMCR_GLINT_EN (1 << 2) ++#define DMCR_HALF_SPEED (1 << 1) ++#define DMCR_CAP_RMWAKUP (1 << 0) ++ ++/* Device Address Register(0x104) */ ++#define FOTG210_DAR 0x104 ++#define DAR_AFT_CONF (1 << 7) ++ ++/* Device Test Register(0x108) */ ++#define FOTG210_DTR 0x108 ++#define DTR_TST_CLRFF (1 << 0) ++ ++/* PHY Test Mode Selector register(0x114) */ ++#define FOTG210_PHYTMSR 0x114 ++#define PHYTMSR_TST_PKT (1 << 4) ++#define PHYTMSR_TST_SE0NAK (1 << 3) ++#define PHYTMSR_TST_KSTA (1 << 2) ++#define PHYTMSR_TST_JSTA (1 << 1) ++#define PHYTMSR_UNPLUG (1 << 0) ++ ++/* Cx configuration and FIFO Empty Status register(0x120) */ ++#define FOTG210_DCFESR 0x120 ++#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) ++#define DCFESR_CX_EMP (1 << 5) ++#define DCFESR_CX_CLR (1 << 3) ++#define DCFESR_CX_STL (1 << 2) ++#define DCFESR_TST_PKDONE (1 << 1) ++#define DCFESR_CX_DONE (1 << 0) ++ ++/* Device IDLE Counter Register(0x124) */ ++#define FOTG210_DICR 0x124 ++ ++/* Device Mask of Interrupt Group Register (0x130) */ ++#define FOTG210_DMIGR 0x130 ++#define DMIGR_MINT_G0 (1 << 0) ++ ++/* Device Mask of Interrupt Source Group 0(0x134) */ ++#define FOTG210_DMISGR0 0x134 ++#define DMISGR0_MCX_COMEND (1 << 3) ++#define DMISGR0_MCX_OUT_INT (1 << 2) ++#define DMISGR0_MCX_IN_INT (1 << 1) ++#define DMISGR0_MCX_SETUP_INT (1 << 0) ++ ++/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ ++#define FOTG210_DMISGR1 0x138 ++#define DMISGR1_MF3_IN_INT (1 << 19) ++#define DMISGR1_MF2_IN_INT (1 << 18) ++#define DMISGR1_MF1_IN_INT (1 << 17) ++#define DMISGR1_MF0_IN_INT (1 << 16) ++#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) ++#define DMISGR1_MF3_SPK_INT (1 << 7) ++#define DMISGR1_MF3_OUT_INT (1 << 6) ++#define DMISGR1_MF2_SPK_INT (1 << 5) ++#define DMISGR1_MF2_OUT_INT (1 << 4) ++#define DMISGR1_MF1_SPK_INT (1 << 3) ++#define DMISGR1_MF1_OUT_INT (1 << 2) ++#define DMISGR1_MF0_SPK_INT (1 << 1) ++#define DMISGR1_MF0_OUT_INT (1 << 0) ++#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) ++ ++/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ ++#define FOTG210_DMISGR2 0x13C ++#define DMISGR2_MDMA_ERROR (1 << 8) ++#define DMISGR2_MDMA_CMPLT (1 << 7) ++ ++/* Device Interrupt group Register (0x140) */ ++#define FOTG210_DIGR 0x140 ++#define DIGR_INT_G2 (1 << 2) ++#define DIGR_INT_G1 (1 << 1) ++#define DIGR_INT_G0 (1 << 0) ++ ++/* Device Interrupt Source Group 0 Register (0x144) */ ++#define FOTG210_DISGR0 0x144 ++#define DISGR0_CX_COMABT_INT (1 << 5) ++#define DISGR0_CX_COMFAIL_INT (1 << 4) ++#define DISGR0_CX_COMEND_INT (1 << 3) ++#define DISGR0_CX_OUT_INT (1 << 2) ++#define DISGR0_CX_IN_INT (1 << 1) ++#define DISGR0_CX_SETUP_INT (1 << 0) ++ ++/* Device Interrupt Source Group 1 Register (0x148) */ ++#define FOTG210_DISGR1 0x148 ++#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) ++#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) ++#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) ++ ++/* Device Interrupt Source Group 2 Register (0x14C) */ ++#define FOTG210_DISGR2 0x14C ++#define DISGR2_DMA_ERROR (1 << 8) ++#define DISGR2_DMA_CMPLT (1 << 7) ++#define DISGR2_RX0BYTE_INT (1 << 6) ++#define DISGR2_TX0BYTE_INT (1 << 5) ++#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) ++#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) ++#define DISGR2_RESM_INT (1 << 2) ++#define DISGR2_SUSP_INT (1 << 1) ++#define DISGR2_USBRST_INT (1 << 0) ++ ++/* Device Receive Zero-Length Data Packet Register (0x150)*/ ++#define FOTG210_RX0BYTE 0x150 ++#define RX0BYTE_EP8 (1 << 7) ++#define RX0BYTE_EP7 (1 << 6) ++#define RX0BYTE_EP6 (1 << 5) ++#define RX0BYTE_EP5 (1 << 4) ++#define RX0BYTE_EP4 (1 << 3) ++#define RX0BYTE_EP3 (1 << 2) ++#define RX0BYTE_EP2 (1 << 1) ++#define RX0BYTE_EP1 (1 << 0) ++ ++/* Device Transfer Zero-Length Data Packet Register (0x154)*/ ++#define FOTG210_TX0BYTE 0x154 ++#define TX0BYTE_EP8 (1 << 7) ++#define TX0BYTE_EP7 (1 << 6) ++#define TX0BYTE_EP6 (1 << 5) ++#define TX0BYTE_EP5 (1 << 4) ++#define TX0BYTE_EP4 (1 << 3) ++#define TX0BYTE_EP3 (1 << 2) ++#define TX0BYTE_EP2 (1 << 1) ++#define TX0BYTE_EP1 (1 << 0) ++ ++/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ ++#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) ++#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) ++#define INOUTEPMPSR_STL_EP (1 << 11) ++#define INOUTEPMPSR_RESET_TSEQ (1 << 12) ++ ++/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ ++#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) ++ ++/* Device Endpoint 1~4 Map Register (0x1A0) */ ++#define FOTG210_EPMAP 0x1A0 ++#define EPMAP_FIFONO(ep, dir) \ ++ ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) ++#define EPMAP_FIFONOMSK(ep, dir) \ ++ ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) ++ ++/* Device FIFO Map Register (0x1A8) */ ++#define FOTG210_FIFOMAP 0x1A8 ++#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) ++#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) ++#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) ++#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) ++#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) ++#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) ++ ++/* Device FIFO Confuguration Register (0x1AC) */ ++#define FOTG210_FIFOCF 0x1AC ++#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) ++#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) ++#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) ++#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) ++#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) ++#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) ++#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) ++ ++/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ ++#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) ++#define FIBCR_BCFX 0x7FF ++#define FIBCR_FFRST (1 << 12) ++ ++/* Device DMA Target FIFO Number Register (0x1C0) */ ++#define FOTG210_DMATFNR 0x1C0 ++#define DMATFNR_ACC_CXF (1 << 4) ++#define DMATFNR_ACC_F3 (1 << 3) ++#define DMATFNR_ACC_F2 (1 << 2) ++#define DMATFNR_ACC_F1 (1 << 1) ++#define DMATFNR_ACC_F0 (1 << 0) ++#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) ++#define DMATFNR_DISDMA 0 ++ ++/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ ++#define FOTG210_DMACPSR1 0x1C8 ++#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) ++#define DMACPSR1_DMA_ABORT (1 << 3) ++#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) ++#define DMACPSR1_DMA_START (1 << 0) ++ ++/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ ++#define FOTG210_DMACPSR2 0x1CC ++ ++/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ ++#define FOTG210_CXPORT 0x1D0 ++ ++struct fotg210_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++struct fotg210_ep { ++ struct usb_ep ep; ++ struct fotg210_udc *fotg210; ++ ++ struct list_head queue; ++ unsigned stall:1; ++ unsigned wedged:1; ++ unsigned use_dma:1; ++ ++ unsigned char epnum; ++ unsigned char type; ++ unsigned char dir_in; ++ unsigned int maxp; ++ const struct usb_endpoint_descriptor *desc; ++}; ++ ++struct fotg210_udc { ++ spinlock_t lock; /* protect the struct */ ++ void __iomem *reg; ++ ++ unsigned long irq_trigger; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; ++ ++ struct usb_request *ep0_req; /* for internal request */ ++ __le16 ep0_data; ++ u8 ep0_dir; /* 0/0x80 out/in */ ++ ++ u8 reenum; /* if re-enumeration */ ++}; ++ ++#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) +--- a/drivers/usb/gadget/udc/fotg210.h ++++ /dev/null +@@ -1,249 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* +- * Faraday FOTG210 USB OTG controller +- * +- * Copyright (C) 2013 Faraday Technology Corporation +- * Author: Yuan-Hsin Chen +- */ +- +-#include +- +-#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ +-#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ +- +-/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ +-#define FOTG210_GMIR 0xC4 +-#define GMIR_INT_POLARITY 0x8 /*Active High*/ +-#define GMIR_MHC_INT 0x4 +-#define GMIR_MOTG_INT 0x2 +-#define GMIR_MDEV_INT 0x1 +- +-/* Device Main Control Register(0x100) */ +-#define FOTG210_DMCR 0x100 +-#define DMCR_HS_EN (1 << 6) +-#define DMCR_CHIP_EN (1 << 5) +-#define DMCR_SFRST (1 << 4) +-#define DMCR_GOSUSP (1 << 3) +-#define DMCR_GLINT_EN (1 << 2) +-#define DMCR_HALF_SPEED (1 << 1) +-#define DMCR_CAP_RMWAKUP (1 << 0) +- +-/* Device Address Register(0x104) */ +-#define FOTG210_DAR 0x104 +-#define DAR_AFT_CONF (1 << 7) +- +-/* Device Test Register(0x108) */ +-#define FOTG210_DTR 0x108 +-#define DTR_TST_CLRFF (1 << 0) +- +-/* PHY Test Mode Selector register(0x114) */ +-#define FOTG210_PHYTMSR 0x114 +-#define PHYTMSR_TST_PKT (1 << 4) +-#define PHYTMSR_TST_SE0NAK (1 << 3) +-#define PHYTMSR_TST_KSTA (1 << 2) +-#define PHYTMSR_TST_JSTA (1 << 1) +-#define PHYTMSR_UNPLUG (1 << 0) +- +-/* Cx configuration and FIFO Empty Status register(0x120) */ +-#define FOTG210_DCFESR 0x120 +-#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) +-#define DCFESR_CX_EMP (1 << 5) +-#define DCFESR_CX_CLR (1 << 3) +-#define DCFESR_CX_STL (1 << 2) +-#define DCFESR_TST_PKDONE (1 << 1) +-#define DCFESR_CX_DONE (1 << 0) +- +-/* Device IDLE Counter Register(0x124) */ +-#define FOTG210_DICR 0x124 +- +-/* Device Mask of Interrupt Group Register (0x130) */ +-#define FOTG210_DMIGR 0x130 +-#define DMIGR_MINT_G0 (1 << 0) +- +-/* Device Mask of Interrupt Source Group 0(0x134) */ +-#define FOTG210_DMISGR0 0x134 +-#define DMISGR0_MCX_COMEND (1 << 3) +-#define DMISGR0_MCX_OUT_INT (1 << 2) +-#define DMISGR0_MCX_IN_INT (1 << 1) +-#define DMISGR0_MCX_SETUP_INT (1 << 0) +- +-/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ +-#define FOTG210_DMISGR1 0x138 +-#define DMISGR1_MF3_IN_INT (1 << 19) +-#define DMISGR1_MF2_IN_INT (1 << 18) +-#define DMISGR1_MF1_IN_INT (1 << 17) +-#define DMISGR1_MF0_IN_INT (1 << 16) +-#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) +-#define DMISGR1_MF3_SPK_INT (1 << 7) +-#define DMISGR1_MF3_OUT_INT (1 << 6) +-#define DMISGR1_MF2_SPK_INT (1 << 5) +-#define DMISGR1_MF2_OUT_INT (1 << 4) +-#define DMISGR1_MF1_SPK_INT (1 << 3) +-#define DMISGR1_MF1_OUT_INT (1 << 2) +-#define DMISGR1_MF0_SPK_INT (1 << 1) +-#define DMISGR1_MF0_OUT_INT (1 << 0) +-#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) +- +-/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ +-#define FOTG210_DMISGR2 0x13C +-#define DMISGR2_MDMA_ERROR (1 << 8) +-#define DMISGR2_MDMA_CMPLT (1 << 7) +- +-/* Device Interrupt group Register (0x140) */ +-#define FOTG210_DIGR 0x140 +-#define DIGR_INT_G2 (1 << 2) +-#define DIGR_INT_G1 (1 << 1) +-#define DIGR_INT_G0 (1 << 0) +- +-/* Device Interrupt Source Group 0 Register (0x144) */ +-#define FOTG210_DISGR0 0x144 +-#define DISGR0_CX_COMABT_INT (1 << 5) +-#define DISGR0_CX_COMFAIL_INT (1 << 4) +-#define DISGR0_CX_COMEND_INT (1 << 3) +-#define DISGR0_CX_OUT_INT (1 << 2) +-#define DISGR0_CX_IN_INT (1 << 1) +-#define DISGR0_CX_SETUP_INT (1 << 0) +- +-/* Device Interrupt Source Group 1 Register (0x148) */ +-#define FOTG210_DISGR1 0x148 +-#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) +-#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) +-#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) +- +-/* Device Interrupt Source Group 2 Register (0x14C) */ +-#define FOTG210_DISGR2 0x14C +-#define DISGR2_DMA_ERROR (1 << 8) +-#define DISGR2_DMA_CMPLT (1 << 7) +-#define DISGR2_RX0BYTE_INT (1 << 6) +-#define DISGR2_TX0BYTE_INT (1 << 5) +-#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) +-#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) +-#define DISGR2_RESM_INT (1 << 2) +-#define DISGR2_SUSP_INT (1 << 1) +-#define DISGR2_USBRST_INT (1 << 0) +- +-/* Device Receive Zero-Length Data Packet Register (0x150)*/ +-#define FOTG210_RX0BYTE 0x150 +-#define RX0BYTE_EP8 (1 << 7) +-#define RX0BYTE_EP7 (1 << 6) +-#define RX0BYTE_EP6 (1 << 5) +-#define RX0BYTE_EP5 (1 << 4) +-#define RX0BYTE_EP4 (1 << 3) +-#define RX0BYTE_EP3 (1 << 2) +-#define RX0BYTE_EP2 (1 << 1) +-#define RX0BYTE_EP1 (1 << 0) +- +-/* Device Transfer Zero-Length Data Packet Register (0x154)*/ +-#define FOTG210_TX0BYTE 0x154 +-#define TX0BYTE_EP8 (1 << 7) +-#define TX0BYTE_EP7 (1 << 6) +-#define TX0BYTE_EP6 (1 << 5) +-#define TX0BYTE_EP5 (1 << 4) +-#define TX0BYTE_EP4 (1 << 3) +-#define TX0BYTE_EP3 (1 << 2) +-#define TX0BYTE_EP2 (1 << 1) +-#define TX0BYTE_EP1 (1 << 0) +- +-/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ +-#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) +-#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) +-#define INOUTEPMPSR_STL_EP (1 << 11) +-#define INOUTEPMPSR_RESET_TSEQ (1 << 12) +- +-/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ +-#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) +- +-/* Device Endpoint 1~4 Map Register (0x1A0) */ +-#define FOTG210_EPMAP 0x1A0 +-#define EPMAP_FIFONO(ep, dir) \ +- ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +-#define EPMAP_FIFONOMSK(ep, dir) \ +- ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +- +-/* Device FIFO Map Register (0x1A8) */ +-#define FOTG210_FIFOMAP 0x1A8 +-#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) +-#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) +-#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) +-#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) +-#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) +-#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) +- +-/* Device FIFO Confuguration Register (0x1AC) */ +-#define FOTG210_FIFOCF 0x1AC +-#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) +-#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) +-#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) +-#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) +-#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) +-#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) +-#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) +- +-/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ +-#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) +-#define FIBCR_BCFX 0x7FF +-#define FIBCR_FFRST (1 << 12) +- +-/* Device DMA Target FIFO Number Register (0x1C0) */ +-#define FOTG210_DMATFNR 0x1C0 +-#define DMATFNR_ACC_CXF (1 << 4) +-#define DMATFNR_ACC_F3 (1 << 3) +-#define DMATFNR_ACC_F2 (1 << 2) +-#define DMATFNR_ACC_F1 (1 << 1) +-#define DMATFNR_ACC_F0 (1 << 0) +-#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) +-#define DMATFNR_DISDMA 0 +- +-/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ +-#define FOTG210_DMACPSR1 0x1C8 +-#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) +-#define DMACPSR1_DMA_ABORT (1 << 3) +-#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) +-#define DMACPSR1_DMA_START (1 << 0) +- +-/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ +-#define FOTG210_DMACPSR2 0x1CC +- +-/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ +-#define FOTG210_CXPORT 0x1D0 +- +-struct fotg210_request { +- struct usb_request req; +- struct list_head queue; +-}; +- +-struct fotg210_ep { +- struct usb_ep ep; +- struct fotg210_udc *fotg210; +- +- struct list_head queue; +- unsigned stall:1; +- unsigned wedged:1; +- unsigned use_dma:1; +- +- unsigned char epnum; +- unsigned char type; +- unsigned char dir_in; +- unsigned int maxp; +- const struct usb_endpoint_descriptor *desc; +-}; +- +-struct fotg210_udc { +- spinlock_t lock; /* protect the struct */ +- void __iomem *reg; +- +- unsigned long irq_trigger; +- +- struct usb_gadget gadget; +- struct usb_gadget_driver *driver; +- +- struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; +- +- struct usb_request *ep0_req; /* for internal request */ +- __le16 ep0_data; +- u8 ep0_dir; /* 0/0x80 out/in */ +- +- u8 reenum; /* if re-enumeration */ +-}; +- +-#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) +--- a/drivers/usb/host/fotg210.h ++++ /dev/null +@@ -1,688 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-#ifndef __LINUX_FOTG210_H +-#define __LINUX_FOTG210_H +- +-#include +- +-/* definitions used for the EHCI driver */ +- +-/* +- * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to +- * __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on +- * the host controller implementation. +- * +- * To facilitate the strongest possible byte-order checking from "sparse" +- * and so on, we use __leXX unless that's not practical. +- */ +-#define __hc32 __le32 +-#define __hc16 __le16 +- +-/* statistics can be kept for tuning/monitoring */ +-struct fotg210_stats { +- /* irq usage */ +- unsigned long normal; +- unsigned long error; +- unsigned long iaa; +- unsigned long lost_iaa; +- +- /* termination of urbs from core */ +- unsigned long complete; +- unsigned long unlink; +-}; +- +-/* fotg210_hcd->lock guards shared data against other CPUs: +- * fotg210_hcd: async, unlink, periodic (and shadow), ... +- * usb_host_endpoint: hcpriv +- * fotg210_qh: qh_next, qtd_list +- * fotg210_qtd: qtd_list +- * +- * Also, hold this lock when talking to HC registers or +- * when updating hw_* fields in shared qh/qtd/... structures. +- */ +- +-#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ +- +-/* +- * fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the +- * controller may be doing DMA. Lower values mean there's no DMA. +- */ +-enum fotg210_rh_state { +- FOTG210_RH_HALTED, +- FOTG210_RH_SUSPENDED, +- FOTG210_RH_RUNNING, +- FOTG210_RH_STOPPING +-}; +- +-/* +- * Timer events, ordered by increasing delay length. +- * Always update event_delays_ns[] and event_handlers[] (defined in +- * ehci-timer.c) in parallel with this list. +- */ +-enum fotg210_hrtimer_event { +- FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */ +- FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ +- FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ +- FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ +- FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ +- FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ +- FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ +- FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ +- FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ +- FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ +- FOTG210_HRTIMER_NUM_EVENTS /* Must come last */ +-}; +-#define FOTG210_HRTIMER_NO_EVENT 99 +- +-struct fotg210_hcd { /* one per controller */ +- /* timing support */ +- enum fotg210_hrtimer_event next_hrtimer_event; +- unsigned enabled_hrtimer_events; +- ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS]; +- struct hrtimer hrtimer; +- +- int PSS_poll_count; +- int ASS_poll_count; +- int died_poll_count; +- +- /* glue to PCI and HCD framework */ +- struct fotg210_caps __iomem *caps; +- struct fotg210_regs __iomem *regs; +- struct ehci_dbg_port __iomem *debug; +- +- __u32 hcs_params; /* cached register copy */ +- spinlock_t lock; +- enum fotg210_rh_state rh_state; +- +- /* general schedule support */ +- bool scanning:1; +- bool need_rescan:1; +- bool intr_unlinking:1; +- bool async_unlinking:1; +- bool shutdown:1; +- struct fotg210_qh *qh_scan_next; +- +- /* async schedule support */ +- struct fotg210_qh *async; +- struct fotg210_qh *dummy; /* For AMD quirk use */ +- struct fotg210_qh *async_unlink; +- struct fotg210_qh *async_unlink_last; +- struct fotg210_qh *async_iaa; +- unsigned async_unlink_cycle; +- unsigned async_count; /* async activity count */ +- +- /* periodic schedule support */ +-#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ +- unsigned periodic_size; +- __hc32 *periodic; /* hw periodic table */ +- dma_addr_t periodic_dma; +- struct list_head intr_qh_list; +- unsigned i_thresh; /* uframes HC might cache */ +- +- union fotg210_shadow *pshadow; /* mirror hw periodic table */ +- struct fotg210_qh *intr_unlink; +- struct fotg210_qh *intr_unlink_last; +- unsigned intr_unlink_cycle; +- unsigned now_frame; /* frame from HC hardware */ +- unsigned next_frame; /* scan periodic, start here */ +- unsigned intr_count; /* intr activity count */ +- unsigned isoc_count; /* isoc activity count */ +- unsigned periodic_count; /* periodic activity count */ +- /* max periodic time per uframe */ +- unsigned uframe_periodic_max; +- +- +- /* list of itds completed while now_frame was still active */ +- struct list_head cached_itd_list; +- struct fotg210_itd *last_itd_to_free; +- +- /* per root hub port */ +- unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; +- +- /* bit vectors (one bit per port) +- * which ports were already suspended at the start of a bus suspend +- */ +- unsigned long bus_suspended; +- +- /* which ports are edicated to the companion controller */ +- unsigned long companion_ports; +- +- /* which ports are owned by the companion during a bus suspend */ +- unsigned long owned_ports; +- +- /* which ports have the change-suspend feature turned on */ +- unsigned long port_c_suspend; +- +- /* which ports are suspended */ +- unsigned long suspended_ports; +- +- /* which ports have started to resume */ +- unsigned long resuming_ports; +- +- /* per-HC memory pools (could be per-bus, but ...) */ +- struct dma_pool *qh_pool; /* qh per active urb */ +- struct dma_pool *qtd_pool; /* one or more per qh */ +- struct dma_pool *itd_pool; /* itd per iso urb */ +- +- unsigned random_frame; +- unsigned long next_statechange; +- ktime_t last_periodic_enable; +- u32 command; +- +- /* SILICON QUIRKS */ +- unsigned need_io_watchdog:1; +- unsigned fs_i_thresh:1; /* Intel iso scheduling */ +- +- u8 sbrn; /* packed release number */ +- +- /* irq statistics */ +-#ifdef FOTG210_STATS +- struct fotg210_stats stats; +-# define INCR(x) ((x)++) +-#else +-# define INCR(x) do {} while (0) +-#endif +- +- /* silicon clock */ +- struct clk *pclk; +-}; +- +-/* convert between an HCD pointer and the corresponding FOTG210_HCD */ +-static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd) +-{ +- return (struct fotg210_hcd *)(hcd->hcd_priv); +-} +-static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210) +-{ +- return container_of((void *) fotg210, struct usb_hcd, hcd_priv); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ +- +-/* Section 2.2 Host Controller Capability Registers */ +-struct fotg210_caps { +- /* these fields are specified as 8 and 16 bit registers, +- * but some hosts can't perform 8 or 16 bit PCI accesses. +- * some hosts treat caplength and hciversion as parts of a 32-bit +- * register, others treat them as two separate registers, this +- * affects the memory map for big endian controllers. +- */ +- u32 hc_capbase; +-#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ +- (fotg210_big_endian_capbase(fotg210) ? 24 : 0))) +-#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ +- (fotg210_big_endian_capbase(fotg210) ? 0 : 16))) +- u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +-#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ +- +- u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +-#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +-#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +- u8 portroute[8]; /* nibbles for routing - offset 0xC */ +-}; +- +- +-/* Section 2.3 Host Controller Operational Registers */ +-struct fotg210_regs { +- +- /* USBCMD: offset 0x00 */ +- u32 command; +- +-/* EHCI 1.1 addendum */ +-/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +-#define CMD_PARK (1<<11) /* enable "park" on async qh */ +-#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +-#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +-#define CMD_ASE (1<<5) /* async schedule enable */ +-#define CMD_PSE (1<<4) /* periodic schedule enable */ +-/* 3:2 is periodic frame list size */ +-#define CMD_RESET (1<<1) /* reset HC not bus */ +-#define CMD_RUN (1<<0) /* start/stop HC */ +- +- /* USBSTS: offset 0x04 */ +- u32 status; +-#define STS_ASS (1<<15) /* Async Schedule Status */ +-#define STS_PSS (1<<14) /* Periodic Schedule Status */ +-#define STS_RECL (1<<13) /* Reclamation */ +-#define STS_HALT (1<<12) /* Not running (any reason) */ +-/* some bits reserved */ +- /* these STS_* flags are also intr_enable bits (USBINTR) */ +-#define STS_IAA (1<<5) /* Interrupted on async advance */ +-#define STS_FATAL (1<<4) /* such as some PCI access errors */ +-#define STS_FLR (1<<3) /* frame list rolled over */ +-#define STS_PCD (1<<2) /* port change detect */ +-#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +-#define STS_INT (1<<0) /* "normal" completion (short, ...) */ +- +- /* USBINTR: offset 0x08 */ +- u32 intr_enable; +- +- /* FRINDEX: offset 0x0C */ +- u32 frame_index; /* current microframe number */ +- /* CTRLDSSEGMENT: offset 0x10 */ +- u32 segment; /* address bits 63:32 if needed */ +- /* PERIODICLISTBASE: offset 0x14 */ +- u32 frame_list; /* points to periodic list */ +- /* ASYNCLISTADDR: offset 0x18 */ +- u32 async_next; /* address of next async queue head */ +- +- u32 reserved1; +- /* PORTSC: offset 0x20 */ +- u32 port_status; +-/* 31:23 reserved */ +-#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +-#define PORT_RESET (1<<8) /* reset port */ +-#define PORT_SUSPEND (1<<7) /* suspend port */ +-#define PORT_RESUME (1<<6) /* resume it */ +-#define PORT_PEC (1<<3) /* port enable change */ +-#define PORT_PE (1<<2) /* port enable */ +-#define PORT_CSC (1<<1) /* connect status change */ +-#define PORT_CONNECT (1<<0) /* device connected */ +-#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) +- u32 reserved2[19]; +- +- /* OTGCSR: offet 0x70 */ +- u32 otgcsr; +-#define OTGCSR_HOST_SPD_TYP (3 << 22) +-#define OTGCSR_A_BUS_DROP (1 << 5) +-#define OTGCSR_A_BUS_REQ (1 << 4) +- +- /* OTGISR: offset 0x74 */ +- u32 otgisr; +-#define OTGISR_OVC (1 << 10) +- +- u32 reserved3[15]; +- +- /* GMIR: offset 0xB4 */ +- u32 gmir; +-#define GMIR_INT_POLARITY (1 << 3) /*Active High*/ +-#define GMIR_MHC_INT (1 << 2) +-#define GMIR_MOTG_INT (1 << 1) +-#define GMIR_MDEV_INT (1 << 0) +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma) +- +-/* +- * EHCI Specification 0.95 Section 3.5 +- * QTD: describe data transfer components (buffer, direction, ...) +- * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". +- * +- * These are associated only with "QH" (Queue Head) structures, +- * used with control, bulk, and interrupt transfers. +- */ +-struct fotg210_qtd { +- /* first part defined by EHCI spec */ +- __hc32 hw_next; /* see EHCI 3.5.1 */ +- __hc32 hw_alt_next; /* see EHCI 3.5.2 */ +- __hc32 hw_token; /* see EHCI 3.5.3 */ +-#define QTD_TOGGLE (1 << 31) /* data toggle */ +-#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +-#define QTD_IOC (1 << 15) /* interrupt on complete */ +-#define QTD_CERR(tok) (((tok)>>10) & 0x3) +-#define QTD_PID(tok) (((tok)>>8) & 0x3) +-#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +-#define QTD_STS_HALT (1 << 6) /* halted on error */ +-#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +-#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +-#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +-#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +-#define QTD_STS_STS (1 << 1) /* split transaction state */ +-#define QTD_STS_PING (1 << 0) /* issue PING? */ +- +-#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE) +-#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT) +-#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS) +- +- __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ +- __hc32 hw_buf_hi[5]; /* Appendix B */ +- +- /* the rest is HCD-private */ +- dma_addr_t qtd_dma; /* qtd address */ +- struct list_head qtd_list; /* sw qtd list */ +- struct urb *urb; /* qtd's urb */ +- size_t length; /* length of buffer */ +-} __aligned(32); +- +-/* mask NakCnt+T in qh->hw_alt_next */ +-#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f) +- +-#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) +- +-/*-------------------------------------------------------------------------*/ +- +-/* type tag from {qh,itd,fstn}->hw_next */ +-#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1)) +- +-/* +- * Now the following defines are not converted using the +- * cpu_to_le32() macro anymore, since we have to support +- * "dynamic" switching between be and le support, so that the driver +- * can be used on one system with SoC EHCI controller using big-endian +- * descriptors as well as a normal little-endian PCI EHCI controller. +- */ +-/* values for that type tag */ +-#define Q_TYPE_ITD (0 << 1) +-#define Q_TYPE_QH (1 << 1) +-#define Q_TYPE_SITD (2 << 1) +-#define Q_TYPE_FSTN (3 << 1) +- +-/* next async queue entry, or pointer to interrupt/periodic QH */ +-#define QH_NEXT(fotg210, dma) \ +- (cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH)) +- +-/* for periodic/async schedules and qtd lists, mark end of list */ +-#define FOTG210_LIST_END(fotg210) \ +- cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */ +- +-/* +- * Entries in periodic shadow table are pointers to one of four kinds +- * of data structure. That's dictated by the hardware; a type tag is +- * encoded in the low bits of the hardware's periodic schedule. Use +- * Q_NEXT_TYPE to get the tag. +- * +- * For entries in the async schedule, the type tag always says "qh". +- */ +-union fotg210_shadow { +- struct fotg210_qh *qh; /* Q_TYPE_QH */ +- struct fotg210_itd *itd; /* Q_TYPE_ITD */ +- struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */ +- __hc32 *hw_next; /* (all types) */ +- void *ptr; +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.95 Section 3.6 +- * QH: describes control/bulk/interrupt endpoints +- * See Fig 3-7 "Queue Head Structure Layout". +- * +- * These appear in both the async and (for interrupt) periodic schedules. +- */ +- +-/* first part defined by EHCI spec */ +-struct fotg210_qh_hw { +- __hc32 hw_next; /* see EHCI 3.6.1 */ +- __hc32 hw_info1; /* see EHCI 3.6.2 */ +-#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +-#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +-#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +-#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +-#define QH_LOW_SPEED (1 << 12) +-#define QH_FULL_SPEED (0 << 12) +-#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ +- __hc32 hw_info2; /* see EHCI 3.6.2 */ +-#define QH_SMASK 0x000000ff +-#define QH_CMASK 0x0000ff00 +-#define QH_HUBADDR 0x007f0000 +-#define QH_HUBPORT 0x3f800000 +-#define QH_MULT 0xc0000000 +- __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ +- +- /* qtd overlay (hardware parts of a struct fotg210_qtd) */ +- __hc32 hw_qtd_next; +- __hc32 hw_alt_next; +- __hc32 hw_token; +- __hc32 hw_buf[5]; +- __hc32 hw_buf_hi[5]; +-} __aligned(32); +- +-struct fotg210_qh { +- struct fotg210_qh_hw *hw; /* Must come first */ +- /* the rest is HCD-private */ +- dma_addr_t qh_dma; /* address of qh */ +- union fotg210_shadow qh_next; /* ptr to qh; or periodic */ +- struct list_head qtd_list; /* sw qtd list */ +- struct list_head intr_node; /* list of intr QHs */ +- struct fotg210_qtd *dummy; +- struct fotg210_qh *unlink_next; /* next on unlink list */ +- +- unsigned unlink_cycle; +- +- u8 needs_rescan; /* Dequeue during giveback */ +- u8 qh_state; +-#define QH_STATE_LINKED 1 /* HC sees this */ +-#define QH_STATE_UNLINK 2 /* HC may still see this */ +-#define QH_STATE_IDLE 3 /* HC doesn't see this */ +-#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ +-#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ +- +- u8 xacterrs; /* XactErr retry counter */ +-#define QH_XACTERR_MAX 32 /* XactErr retry limit */ +- +- /* periodic schedule info */ +- u8 usecs; /* intr bandwidth */ +- u8 gap_uf; /* uframes split/csplit gap */ +- u8 c_usecs; /* ... split completion bw */ +- u16 tt_usecs; /* tt downstream bandwidth */ +- unsigned short period; /* polling interval */ +- unsigned short start; /* where polling starts */ +-#define NO_FRAME ((unsigned short)~0) /* pick new start */ +- +- struct usb_device *dev; /* access to TT */ +- unsigned is_out:1; /* bulk or intr OUT */ +- unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* description of one iso transaction (up to 3 KB data if highspeed) */ +-struct fotg210_iso_packet { +- /* These will be copied to iTD when scheduling */ +- u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ +- __hc32 transaction; /* itd->hw_transaction[i] |= */ +- u8 cross; /* buf crosses pages */ +- /* for full speed OUT splits */ +- u32 buf1; +-}; +- +-/* temporary schedule data for packets from iso urbs (both speeds) +- * each packet is one logical usb transaction to the device (not TT), +- * beginning at stream->next_uframe +- */ +-struct fotg210_iso_sched { +- struct list_head td_list; +- unsigned span; +- struct fotg210_iso_packet packet[]; +-}; +- +-/* +- * fotg210_iso_stream - groups all (s)itds for this endpoint. +- * acts like a qh would, if EHCI had them for ISO. +- */ +-struct fotg210_iso_stream { +- /* first field matches fotg210_hq, but is NULL */ +- struct fotg210_qh_hw *hw; +- +- u8 bEndpointAddress; +- u8 highspeed; +- struct list_head td_list; /* queued itds */ +- struct list_head free_list; /* list of unused itds */ +- struct usb_device *udev; +- struct usb_host_endpoint *ep; +- +- /* output of (re)scheduling */ +- int next_uframe; +- __hc32 splits; +- +- /* the rest is derived from the endpoint descriptor, +- * trusting urb->interval == f(epdesc->bInterval) and +- * including the extra info for hw_bufp[0..2] +- */ +- u8 usecs, c_usecs; +- u16 interval; +- u16 tt_usecs; +- u16 maxp; +- u16 raw_mask; +- unsigned bandwidth; +- +- /* This is used to initialize iTD's hw_bufp fields */ +- __hc32 buf0; +- __hc32 buf1; +- __hc32 buf2; +- +- /* this is used to initialize sITD's tt info */ +- __hc32 address; +-}; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.95 Section 3.3 +- * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" +- * +- * Schedule records for high speed iso xfers +- */ +-struct fotg210_itd { +- /* first part defined by EHCI spec */ +- __hc32 hw_next; /* see EHCI 3.3.1 */ +- __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ +-#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +-#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +-#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */ +-#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +-#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) +-#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */ +- +-#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE) +- +- __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ +- __hc32 hw_bufp_hi[7]; /* Appendix B */ +- +- /* the rest is HCD-private */ +- dma_addr_t itd_dma; /* for this itd */ +- union fotg210_shadow itd_next; /* ptr to periodic q entry */ +- +- struct urb *urb; +- struct fotg210_iso_stream *stream; /* endpoint's queue */ +- struct list_head itd_list; /* list of stream's itds */ +- +- /* any/all hw_transactions here may be used by that urb */ +- unsigned frame; /* where scheduled */ +- unsigned pg; +- unsigned index[8]; /* in urb->iso_frame_desc */ +-} __aligned(32); +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * EHCI Specification 0.96 Section 3.7 +- * Periodic Frame Span Traversal Node (FSTN) +- * +- * Manages split interrupt transactions (using TT) that span frame boundaries +- * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN +- * makes the HC jump (back) to a QH to scan for fs/ls QH completions until +- * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. +- */ +-struct fotg210_fstn { +- __hc32 hw_next; /* any periodic q entry */ +- __hc32 hw_prev; /* qh or FOTG210_LIST_END */ +- +- /* the rest is HCD-private */ +- dma_addr_t fstn_dma; +- union fotg210_shadow fstn_next; /* ptr to periodic q entry */ +-} __aligned(32); +- +-/*-------------------------------------------------------------------------*/ +- +-/* Prepare the PORTSC wakeup flags during controller suspend/resume */ +- +-#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ +- fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) +- +-#define fotg210_prepare_ports_for_controller_resume(fotg210) \ +- fotg210_adjust_port_wakeup_flags(fotg210, false, false) +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * Some EHCI controllers have a Transaction Translator built into the +- * root hub. This is a non-standard feature. Each controller will need +- * to add code to the following inline functions, and call them as +- * needed (mostly in root hub code). +- */ +- +-static inline unsigned int +-fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +-{ +- return (readl(&fotg210->regs->otgcsr) +- & OTGCSR_HOST_SPD_TYP) >> 22; +-} +- +-/* Returns the speed of a device attached to a port on the root hub. */ +-static inline unsigned int +-fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc) +-{ +- switch (fotg210_get_speed(fotg210, portsc)) { +- case 0: +- return 0; +- case 1: +- return USB_PORT_STAT_LOW_SPEED; +- case 2: +- default: +- return USB_PORT_STAT_HIGH_SPEED; +- } +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#define fotg210_has_fsl_portno_bug(e) (0) +- +-/* +- * While most USB host controllers implement their registers in +- * little-endian format, a minority (celleb companion chip) implement +- * them in big endian format. +- * +- * This attempts to support either format at compile time without a +- * runtime penalty, or both formats with the additional overhead +- * of checking a flag bit. +- * +- */ +- +-#define fotg210_big_endian_mmio(e) 0 +-#define fotg210_big_endian_capbase(e) 0 +- +-static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210, +- __u32 __iomem *regs) +-{ +- return readl(regs); +-} +- +-static inline void fotg210_writel(const struct fotg210_hcd *fotg210, +- const unsigned int val, __u32 __iomem *regs) +-{ +- writel(val, regs); +-} +- +-/* cpu to fotg210 */ +-static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x) +-{ +- return cpu_to_le32(x); +-} +- +-/* fotg210 to cpu */ +-static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x) +-{ +- return le32_to_cpu(x); +-} +- +-static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210, +- const __hc32 *x) +-{ +- return le32_to_cpup(x); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) +-{ +- return fotg210_readl(fotg210, &fotg210->regs->frame_index); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-#endif /* __LINUX_FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch b/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch new file mode 100644 index 00000000000..5c7b4ff9c79 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0003-usb-fotg210-Compile-into-one-module.patch @@ -0,0 +1,332 @@ +From 0dbc77a99267a5efef0603a4b49ac02ece6a3f23 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:07 +0200 +Subject: [PATCH 03/29] usb: fotg210: Compile into one module + +It is since ages perfectly possible to compile both of these +modules into the same kernel, which makes no sense since it +is one piece of hardware. + +Compile one module named "fotg210.ko" for both HCD and UDC +drivers by collecting the init calls into a fotg210-core.c +file and start to centralize things handling one and the same +piece of hardware. + +Stub out the initcalls if one or the other part of the driver +was not selected. + +Tested by compiling one or the other or both of the drivers +into the kernel and as modules. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-2-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -12,7 +12,7 @@ config USB_FOTG210 + if USB_FOTG210 + + config USB_FOTG210_HCD +- tristate "Faraday FOTG210 USB Host Controller support" ++ bool "Faraday FOTG210 USB Host Controller support" + depends on USB + help + Faraday FOTG210 is an OTG controller which can be configured as +@@ -24,7 +24,7 @@ config USB_FOTG210_HCD + + config USB_FOTG210_UDC + depends on USB_GADGET +- tristate "Faraday FOTG210 USB Peripheral Controller support" ++ bool "Faraday FOTG210 USB Peripheral Controller support" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver suppports +--- a/drivers/usb/fotg210/Makefile ++++ b/drivers/usb/fotg210/Makefile +@@ -1,3 +1,10 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o +-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ++ ++# This setup links the different object files into one single ++# module so we don't have to EXPORT() a lot of internal symbols ++# or create unnecessary submodules. ++fotg210-objs-y += fotg210-core.o ++fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o ++fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o ++fotg210-objs := $(fotg210-objs-y) ++obj-$(CONFIG_USB_FOTG210) += fotg210.o +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Central probing code for the FOTG210 dual role driver ++ * We register one driver for the hardware and then we decide ++ * whether to proceed with probing the host or the peripheral ++ * driver. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "fotg210.h" ++ ++static int fotg210_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { ++ ret = fotg210_hcd_probe(pdev); ++ if (ret) ++ return ret; ++ } ++ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ ret = fotg210_udc_probe(pdev); ++ ++ return ret; ++} ++ ++static int fotg210_remove(struct platform_device *pdev) ++{ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_remove(pdev); ++ if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ fotg210_udc_remove(pdev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg210" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, fotg210_of_match); ++#endif ++ ++static struct platform_driver fotg210_driver = { ++ .driver = { ++ .name = "fotg210", ++ .of_match_table = of_match_ptr(fotg210_of_match), ++ }, ++ .probe = fotg210_probe, ++ .remove = fotg210_remove, ++}; ++ ++static int __init fotg210_init(void) ++{ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_init(); ++ return platform_driver_register(&fotg210_driver); ++} ++module_init(fotg210_init); ++ ++static void __exit fotg210_cleanup(void) ++{ ++ platform_driver_unregister(&fotg210_driver); ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ fotg210_hcd_cleanup(); ++} ++module_exit(fotg210_cleanup); ++ ++MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("FOTG210 Dual Role Controller Driver"); +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -39,8 +39,8 @@ + #include + #include + +-#define DRIVER_AUTHOR "Yuan-Hsin Chen" +-#define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" ++#include "fotg210.h" ++ + static const char hcd_name[] = "fotg210_hcd"; + + #undef FOTG210_URB_TRACE +@@ -5490,9 +5490,6 @@ static int fotg210_get_frame(struct usb_ + * functions and in order to facilitate role switching we cannot + * give the fotg210 driver exclusive access to those. + */ +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_LICENSE("GPL"); + + static const struct hc_driver fotg210_fotg210_hc_driver = { + .description = hcd_name, +@@ -5560,7 +5557,7 @@ static void fotg210_init(struct fotg210_ + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +-static int fotg210_hcd_probe(struct platform_device *pdev) ++int fotg210_hcd_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; +@@ -5652,7 +5649,7 @@ fail_create_hcd: + * @dev: USB Host Controller being removed + * + */ +-static int fotg210_hcd_remove(struct platform_device *pdev) ++int fotg210_hcd_remove(struct platform_device *pdev) + { + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +@@ -5668,27 +5665,8 @@ static int fotg210_hcd_remove(struct pla + return 0; + } + +-#ifdef CONFIG_OF +-static const struct of_device_id fotg210_of_match[] = { +- { .compatible = "faraday,fotg210" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, fotg210_of_match); +-#endif +- +-static struct platform_driver fotg210_hcd_driver = { +- .driver = { +- .name = "fotg210-hcd", +- .of_match_table = of_match_ptr(fotg210_of_match), +- }, +- .probe = fotg210_hcd_probe, +- .remove = fotg210_hcd_remove, +-}; +- +-static int __init fotg210_hcd_init(void) ++int __init fotg210_hcd_init(void) + { +- int retval = 0; +- + if (usb_disabled()) + return -ENODEV; + +@@ -5704,24 +5682,11 @@ static int __init fotg210_hcd_init(void) + + fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); + +- retval = platform_driver_register(&fotg210_hcd_driver); +- if (retval < 0) +- goto clean; +- return retval; +- +-clean: +- debugfs_remove(fotg210_debug_root); +- fotg210_debug_root = NULL; +- +- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +- return retval; ++ return 0; + } +-module_init(fotg210_hcd_init); + +-static void __exit fotg210_hcd_cleanup(void) ++void __exit fotg210_hcd_cleanup(void) + { +- platform_driver_unregister(&fotg210_hcd_driver); + debugfs_remove(fotg210_debug_root); + clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); + } +-module_exit(fotg210_hcd_cleanup); +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "fotg210.h" + #include "fotg210-udc.h" + + #define DRIVER_DESC "FOTG210 USB Device Controller Driver" +@@ -1081,7 +1082,7 @@ static const struct usb_gadget_ops fotg2 + .udc_stop = fotg210_udc_stop, + }; + +-static int fotg210_udc_remove(struct platform_device *pdev) ++int fotg210_udc_remove(struct platform_device *pdev) + { + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + int i; +@@ -1098,7 +1099,7 @@ static int fotg210_udc_remove(struct pla + return 0; + } + +-static int fotg210_udc_probe(struct platform_device *pdev) ++int fotg210_udc_probe(struct platform_device *pdev) + { + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; +@@ -1223,17 +1224,3 @@ err_alloc: + err: + return ret; + } +- +-static struct platform_driver fotg210_driver = { +- .driver = { +- .name = udc_name, +- }, +- .probe = fotg210_udc_probe, +- .remove = fotg210_udc_remove, +-}; +- +-module_platform_driver(fotg210_driver); +- +-MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang "); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION(DRIVER_DESC); +--- /dev/null ++++ b/drivers/usb/fotg210/fotg210.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __FOTG210_H ++#define __FOTG210_H ++ ++#ifdef CONFIG_USB_FOTG210_HCD ++int fotg210_hcd_probe(struct platform_device *pdev); ++int fotg210_hcd_remove(struct platform_device *pdev); ++int fotg210_hcd_init(void); ++void fotg210_hcd_cleanup(void); ++#else ++static inline int fotg210_hcd_probe(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_hcd_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_hcd_init(void) ++{ ++ return 0; ++} ++static inline void fotg210_hcd_cleanup(void) ++{ ++} ++#endif ++ ++#ifdef CONFIG_USB_FOTG210_UDC ++int fotg210_udc_probe(struct platform_device *pdev); ++int fotg210_udc_remove(struct platform_device *pdev); ++#else ++static inline int fotg210_udc_probe(struct platform_device *pdev) ++{ ++ return 0; ++} ++static inline int fotg210_udc_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++#endif ++ ++#endif /* __FOTG210_H */ diff --git a/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch b/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch new file mode 100644 index 00000000000..6a19a0aa4d9 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0004-usb-fotg210-Select-subdriver-by-mode.patch @@ -0,0 +1,68 @@ +From 7c0b661926097e935f2711857596fc2277b2304a Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 23 Oct 2022 16:47:08 +0200 +Subject: [PATCH 04/29] usb: fotg210: Select subdriver by mode + +Check which mode the hardware is in, and selecte the peripheral +driver if the hardware is in explicit peripheral mode, otherwise +select host mode. + +This should solve the immediate problem that both subdrivers +can get probed. + +Cc: Fabian Vogt +Cc: Yuan-Hsin Chen +Cc: Felipe Balbi +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221023144708.3596563-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -10,30 +10,37 @@ + #include + #include + #include ++#include + + #include "fotg210.h" + + static int fotg210_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; ++ enum usb_dr_mode mode; + int ret; + +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) { +- ret = fotg210_hcd_probe(pdev); +- if (ret) +- return ret; +- } +- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ mode = usb_get_dr_mode(dev); ++ ++ if (mode == USB_DR_MODE_PERIPHERAL) + ret = fotg210_udc_probe(pdev); ++ else ++ ret = fotg210_hcd_probe(pdev); + + return ret; + } + + static int fotg210_remove(struct platform_device *pdev) + { +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) +- fotg210_hcd_remove(pdev); +- if (IS_ENABLED(CONFIG_USB_FOTG210_UDC)) ++ struct device *dev = &pdev->dev; ++ enum usb_dr_mode mode; ++ ++ mode = usb_get_dr_mode(dev); ++ ++ if (mode == USB_DR_MODE_PERIPHERAL) + fotg210_udc_remove(pdev); ++ else ++ fotg210_hcd_remove(pdev); + + return 0; + } diff --git a/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch b/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch new file mode 100644 index 00000000000..daf8d856117 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0005-usb-fotg2-add-Gemini-specific-handling.patch @@ -0,0 +1,135 @@ +From f7f6c8aca91093e2f886ec97910b1a7d9a69bf9b Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 9 Nov 2022 21:05:54 +0100 +Subject: [PATCH 05/29] usb: fotg2: add Gemini-specific handling + +The Cortina Systems Gemini has bolted on a PHY inside the +silicon that can be handled by six bits in a MISC register in +the system controller. + +If we are running on Gemini, look up a syscon regmap through +a phandle and enable VBUS and optionally the Mini-B connector. + +If the device is flagged as "wakeup-source" using the standard +DT bindings, we also enable this in the global controller for +respective port. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221109200554.1957185-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -5,6 +5,7 @@ config USB_FOTG210 + depends on USB || USB_GADGET + depends on HAS_DMA && HAS_IOMEM + default ARCH_GEMINI ++ select MFD_SYSCON + help + Faraday FOTG210 is a dual-mode USB controller that can act + in both host controller and peripheral controller mode. +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -5,15 +5,86 @@ + * whether to proceed with probing the host or the peripheral + * driver. + */ ++#include + #include ++#include + #include + #include + #include ++#include + #include + #include + + #include "fotg210.h" + ++/* ++ * Gemini-specific initialization function, only executed on the ++ * Gemini SoC using the global misc control register. ++ * ++ * The gemini USB blocks are connected to either Mini-A (host mode) or ++ * Mini-B (peripheral mode) plugs. There is no role switch support on the ++ * Gemini SoC, just either-or. ++ */ ++#define GEMINI_GLOBAL_MISC_CTRL 0x30 ++#define GEMINI_MISC_USB0_WAKEUP BIT(14) ++#define GEMINI_MISC_USB1_WAKEUP BIT(15) ++#define GEMINI_MISC_USB0_VBUS_ON BIT(22) ++#define GEMINI_MISC_USB1_VBUS_ON BIT(23) ++#define GEMINI_MISC_USB0_MINI_B BIT(29) ++#define GEMINI_MISC_USB1_MINI_B BIT(30) ++ ++static int fotg210_gemini_init(struct device *dev, struct resource *res, ++ enum usb_dr_mode mode) ++{ ++ struct device_node *np = dev->of_node; ++ struct regmap *map; ++ bool wakeup; ++ u32 mask, val; ++ int ret; ++ ++ map = syscon_regmap_lookup_by_phandle(np, "syscon"); ++ if (IS_ERR(map)) { ++ dev_err(dev, "no syscon\n"); ++ return PTR_ERR(map); ++ } ++ wakeup = of_property_read_bool(np, "wakeup-source"); ++ ++ /* ++ * Figure out if this is USB0 or USB1 by simply checking the ++ * physical base address. ++ */ ++ mask = 0; ++ if (res->start == 0x69000000) { ++ mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | ++ GEMINI_MISC_USB1_WAKEUP; ++ if (mode == USB_DR_MODE_HOST) ++ val = GEMINI_MISC_USB1_VBUS_ON; ++ else ++ val = GEMINI_MISC_USB1_MINI_B; ++ if (wakeup) ++ val |= GEMINI_MISC_USB1_WAKEUP; ++ } else { ++ mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | ++ GEMINI_MISC_USB0_WAKEUP; ++ if (mode == USB_DR_MODE_HOST) ++ val = GEMINI_MISC_USB0_VBUS_ON; ++ else ++ val = GEMINI_MISC_USB0_MINI_B; ++ if (wakeup) ++ val |= GEMINI_MISC_USB0_WAKEUP; ++ } ++ ++ ret = regmap_update_bits(map, GEMINI_GLOBAL_MISC_CTRL, mask, val); ++ if (ret) { ++ dev_err(dev, "failed to initialize Gemini PHY\n"); ++ return ret; ++ } ++ ++ dev_info(dev, "initialized Gemini PHY in %s mode\n", ++ (mode == USB_DR_MODE_HOST) ? "host" : "gadget"); ++ return 0; ++} ++ + static int fotg210_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -22,6 +93,15 @@ static int fotg210_probe(struct platform + + mode = usb_get_dr_mode(dev); + ++ if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ret = fotg210_gemini_init(dev, res, mode); ++ if (ret) ++ return ret; ++ } ++ + if (mode == USB_DR_MODE_PERIPHERAL) + ret = fotg210_udc_probe(pdev); + else diff --git a/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch b/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch new file mode 100644 index 00000000000..bd3a42415a4 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0006-usb-fotg210-Fix-Kconfig-for-USB-host-modules.patch @@ -0,0 +1,51 @@ +From 6e002d41889bc52213a26ff91338d340505e0336 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Fri, 11 Nov 2022 15:48:21 +0100 +Subject: [PATCH 06/29] usb: fotg210: Fix Kconfig for USB host modules + +The kernel robot reports a link failure when activating the +FOTG210 host subdriver with =y on a system where the USB host +core is a module (CONFIG_USB=m). + +This is a bit of special case, so mimic the Kconfig incantations +from DWC3: let the subdrivers for host or peripheral depend +on the host or gadget support being =y or the same as the +FOTG210 core itself. + +This should ensure that either: + +- The host (CONFIG_USB) or gadget (CONFIG_GADGET) is compiled + in and then the FOTG210 can be either module or compiled + in. + +- The host or gadget is modular, and then the FOTG210 module + must be a module too, or we cannot resolve the symbols + at link time. + +Reported-by: kernel test robot +Link: https://lore.kernel.org/linux-usb/202211112132.0BUPGKCd-lkp@intel.com/ +Cc: Arnd Bergmann +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221111144821.113665-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -14,7 +14,7 @@ if USB_FOTG210 + + config USB_FOTG210_HCD + bool "Faraday FOTG210 USB Host Controller support" +- depends on USB ++ depends on USB=y || USB=USB_FOTG210 + help + Faraday FOTG210 is an OTG controller which can be configured as + an USB2.0 host. It is designed to meet USB2.0 EHCI specification +@@ -24,7 +24,7 @@ config USB_FOTG210_HCD + module will be called fotg210-hcd. + + config USB_FOTG210_UDC +- depends on USB_GADGET ++ depends on USB_GADGET=y || USB_GADGET=USB_FOTG210 + bool "Faraday FOTG210 USB Peripheral Controller support" + help + Faraday USB2.0 OTG controller which can be configured as diff --git a/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch b/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch new file mode 100644 index 00000000000..6afef0d8200 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0007-usb-USB_FOTG210-should-depend-on-ARCH_GEMINI.patch @@ -0,0 +1,26 @@ +From 466b10510add46afd21ca19505b29d35ad853370 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Mon, 21 Nov 2022 16:22:19 +0100 +Subject: [PATCH 07/29] usb: USB_FOTG210 should depend on ARCH_GEMINI + +The Faraday Technology FOTG210 USB2 Dual Role Controller is only present +on Cortina Systems Gemini SoCs. Hence add a dependency on ARCH_GEMINI, +to prevent asking the user about its drivers when configuring a kernel +without Cortina Systems Gemini SoC support. + +Fixes: 1dd33a9f1b95ab59 ("usb: fotg210: Collect pieces of dual mode controller") +Signed-off-by: Geert Uytterhoeven +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/a989b3b798ecaf3b45f35160e30e605636d66a77.1669044086.git.geert+renesas@glider.be +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/Kconfig ++++ b/drivers/usb/fotg210/Kconfig +@@ -4,6 +4,7 @@ config USB_FOTG210 + tristate "Faraday FOTG210 USB2 Dual Role controller" + depends on USB || USB_GADGET + depends on HAS_DMA && HAS_IOMEM ++ depends on ARCH_GEMINI || COMPILE_TEST + default ARCH_GEMINI + select MFD_SYSCON + help diff --git a/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch b/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch new file mode 100644 index 00000000000..2a595e885d1 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0008-fotg210-udc-Use-dev-pointer-in-probe-and-dev_message.patch @@ -0,0 +1,61 @@ +From 27cd321a365fecac857e41ad1681062994142e4a Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:51:58 +0100 +Subject: [PATCH 08/29] fotg210-udc: Use dev pointer in probe and dev_messages + +Add a local struct device *dev pointer and use dev_err() +etc to report status. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-1-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1104,6 +1104,7 @@ int fotg210_udc_probe(struct platform_de + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; ++ struct device *dev = &pdev->dev; + int ret = 0; + int i; + +@@ -1135,7 +1136,7 @@ int fotg210_udc_probe(struct platform_de + + fotg210->reg = ioremap(res->start, resource_size(res)); + if (fotg210->reg == NULL) { +- pr_err("ioremap error.\n"); ++ dev_err(dev, "ioremap error\n"); + goto err_alloc; + } + +@@ -1146,8 +1147,8 @@ int fotg210_udc_probe(struct platform_de + fotg210->gadget.ops = &fotg210_gadget_ops; + + fotg210->gadget.max_speed = USB_SPEED_HIGH; +- fotg210->gadget.dev.parent = &pdev->dev; +- fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ fotg210->gadget.dev.parent = dev; ++ fotg210->gadget.dev.dma_mask = dev->dma_mask; + fotg210->gadget.name = udc_name; + + INIT_LIST_HEAD(&fotg210->gadget.ep_list); +@@ -1195,15 +1196,15 @@ int fotg210_udc_probe(struct platform_de + ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { +- pr_err("request_irq error (%d)\n", ret); ++ dev_err(dev, "request_irq error (%d)\n", ret); + goto err_req; + } + +- ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); ++ ret = usb_add_gadget_udc(dev, &fotg210->gadget); + if (ret) + goto err_add_udc; + +- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); ++ dev_info(dev, "version %s\n", DRIVER_VERSION); + + return 0; + diff --git a/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch new file mode 100644 index 00000000000..498875c5356 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch @@ -0,0 +1,158 @@ +From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:51:59 +0100 +Subject: [PATCH 09/29] fotg210-udc: Support optional external PHY + +This adds support for an optional external PHY to the FOTG210 +UDC driver. + +Tested with the GPIO VBUS PHY driver on the Gemini SoC. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,6 +15,8 @@ + #include + #include + #include ++#include ++#include + + #include "fotg210.h" + #include "fotg210-udc.h" +@@ -1022,10 +1024,18 @@ static int fotg210_udc_start(struct usb_ + { + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + u32 value; ++ int ret; + + /* hook up the driver */ + fotg210->driver = driver; + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) { ++ ret = otg_set_peripheral(fotg210->phy->otg, ++ &fotg210->gadget); ++ if (ret) ++ dev_err(fotg210->dev, "can't bind to phy\n"); ++ } ++ + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; +@@ -1067,6 +1077,9 @@ static int fotg210_udc_stop(struct usb_g + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + unsigned long flags; + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ return otg_set_peripheral(fotg210->phy->otg, NULL); ++ + spin_lock_irqsave(&fotg210->lock, flags); + + fotg210_init(fotg210); +@@ -1082,12 +1095,50 @@ static const struct usb_gadget_ops fotg2 + .udc_stop = fotg210_udc_stop, + }; + ++/** ++ * fotg210_phy_event - Called by phy upon VBus event ++ * @nb: notifier block ++ * @action: phy action, is vbus connect or disconnect ++ * @data: the usb_gadget structure in fotg210 ++ * ++ * Called by the USB Phy when a cable connect or disconnect is sensed. ++ * ++ * Returns NOTIFY_OK or NOTIFY_DONE ++ */ ++static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, ++ void *data) ++{ ++ struct usb_gadget *gadget = data; ++ ++ if (!gadget) ++ return NOTIFY_DONE; ++ ++ switch (action) { ++ case USB_EVENT_VBUS: ++ usb_gadget_vbus_connect(gadget); ++ return NOTIFY_OK; ++ case USB_EVENT_NONE: ++ usb_gadget_vbus_disconnect(gadget); ++ return NOTIFY_OK; ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++static struct notifier_block fotg210_phy_notifier = { ++ .notifier_call = fotg210_phy_event, ++}; ++ + int fotg210_udc_remove(struct platform_device *pdev) + { + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + int i; + + usb_del_gadget_udc(&fotg210->gadget); ++ if (!IS_ERR_OR_NULL(fotg210->phy)) { ++ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); ++ usb_put_phy(fotg210->phy); ++ } + iounmap(fotg210->reg); + free_irq(platform_get_irq(pdev, 0), fotg210); + +@@ -1127,6 +1178,22 @@ int fotg210_udc_probe(struct platform_de + if (fotg210 == NULL) + goto err; + ++ fotg210->dev = dev; ++ ++ fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); ++ if (IS_ERR(fotg210->phy)) { ++ ret = PTR_ERR(fotg210->phy); ++ if (ret == -EPROBE_DEFER) ++ goto err; ++ dev_info(dev, "no PHY found\n"); ++ fotg210->phy = NULL; ++ } else { ++ ret = usb_phy_init(fotg210->phy); ++ if (ret) ++ goto err; ++ dev_info(dev, "found and initialized PHY\n"); ++ } ++ + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (_ep[i] == NULL) +@@ -1200,6 +1267,9 @@ int fotg210_udc_probe(struct platform_de + goto err_req; + } + ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ usb_register_notifier(fotg210->phy, &fotg210_phy_notifier); ++ + ret = usb_add_gadget_udc(dev, &fotg210->gadget); + if (ret) + goto err_add_udc; +@@ -1209,6 +1279,8 @@ int fotg210_udc_probe(struct platform_de + return 0; + + err_add_udc: ++ if (!IS_ERR_OR_NULL(fotg210->phy)) ++ usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); + free_irq(ires->start, fotg210); + + err_req: +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -234,6 +234,8 @@ struct fotg210_udc { + + unsigned long irq_trigger; + ++ struct device *dev; ++ struct usb_phy *phy; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + diff --git a/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch b/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch new file mode 100644 index 00000000000..8da3de3b476 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0010-fotg210-udc-Handle-PCLK.patch @@ -0,0 +1,90 @@ +From 772ea3ec2b9363b45ef9a4768ea205f758c3debc Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:52:00 +0100 +Subject: [PATCH 10/29] fotg210-udc: Handle PCLK + +This adds optional handling of the peripheral clock PCLK. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -1145,6 +1146,10 @@ int fotg210_udc_remove(struct platform_d + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); ++ ++ if (!IS_ERR(fotg210->pclk)) ++ clk_disable_unprepare(fotg210->pclk); ++ + kfree(fotg210); + + return 0; +@@ -1180,17 +1185,34 @@ int fotg210_udc_probe(struct platform_de + + fotg210->dev = dev; + ++ /* It's OK not to supply this clock */ ++ fotg210->pclk = devm_clk_get(dev, "PCLK"); ++ if (!IS_ERR(fotg210->pclk)) { ++ ret = clk_prepare_enable(fotg210->pclk); ++ if (ret) { ++ dev_err(dev, "failed to enable PCLK\n"); ++ return ret; ++ } ++ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { ++ /* ++ * Percolate deferrals, for anything else, ++ * just live without the clocking. ++ */ ++ ret = -EPROBE_DEFER; ++ goto err; ++ } ++ + fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +- goto err; ++ goto err_pclk; + dev_info(dev, "no PHY found\n"); + fotg210->phy = NULL; + } else { + ret = usb_phy_init(fotg210->phy); + if (ret) +- goto err; ++ goto err_pclk; + dev_info(dev, "found and initialized PHY\n"); + } + +@@ -1292,6 +1314,10 @@ err_map: + err_alloc: + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); ++err_pclk: ++ if (!IS_ERR(fotg210->pclk)) ++ clk_disable_unprepare(fotg210->pclk); ++ + kfree(fotg210); + + err: +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -231,6 +231,7 @@ struct fotg210_ep { + struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; ++ struct clk *pclk; + + unsigned long irq_trigger; + diff --git a/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch b/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch new file mode 100644 index 00000000000..9544de7cb07 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0011-fotg210-udc-Get-IRQ-using-platform_get_irq.patch @@ -0,0 +1,69 @@ +From eda686d41e298a9d16708d2ec8d12d8e682dd7ca Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 14 Nov 2022 12:52:01 +0100 +Subject: [PATCH 11/29] fotg210-udc: Get IRQ using platform_get_irq() + +The platform_get_irq() is necessary to use to get dynamic +IRQ resolution when instantiating the device from the +device tree. IRQs are not passed as resources in that +case. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221114115201.302887-4-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1157,10 +1157,11 @@ int fotg210_udc_remove(struct platform_d + + int fotg210_udc_probe(struct platform_device *pdev) + { +- struct resource *res, *ires; ++ struct resource *res; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + struct device *dev = &pdev->dev; ++ int irq; + int ret = 0; + int i; + +@@ -1170,9 +1171,9 @@ int fotg210_udc_probe(struct platform_de + return -ENODEV; + } + +- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!ires) { +- pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ pr_err("could not get irq\n"); + return -ENODEV; + } + +@@ -1202,7 +1203,7 @@ int fotg210_udc_probe(struct platform_de + goto err; + } + +- fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); ++ fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +@@ -1282,7 +1283,7 @@ int fotg210_udc_probe(struct platform_de + + fotg210_disable_unplug(fotg210); + +- ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, ++ ret = request_irq(irq, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { + dev_err(dev, "request_irq error (%d)\n", ret); +@@ -1303,7 +1304,7 @@ int fotg210_udc_probe(struct platform_de + err_add_udc: + if (!IS_ERR_OR_NULL(fotg210->phy)) + usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier); +- free_irq(ires->start, fotg210); ++ free_irq(irq, fotg210); + + err_req: + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); diff --git a/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch b/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch new file mode 100644 index 00000000000..8c33c50b2cb --- /dev/null +++ b/target/linux/gemini/patches-6.1/0012-usb-fotg210-udc-Remove-a-useless-assignment.patch @@ -0,0 +1,39 @@ +From 7889a2f0256c55e0184dffd0001d0782f9e4cb83 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Mon, 14 Nov 2022 21:38:04 +0100 +Subject: [PATCH 12/29] usb: fotg210-udc: Remove a useless assignment + +There is no need to use an intermediate array for these memory allocations, +so, axe it. + +While at it, turn a '== NULL' into a shorter '!' when testing memory +allocation failure. + +Signed-off-by: Christophe JAILLET +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/deab9696fc4000499470e7ccbca7c36fca17bd4e.1668458274.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1159,7 +1159,6 @@ int fotg210_udc_probe(struct platform_de + { + struct resource *res; + struct fotg210_udc *fotg210 = NULL; +- struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + struct device *dev = &pdev->dev; + int irq; + int ret = 0; +@@ -1218,10 +1217,9 @@ int fotg210_udc_probe(struct platform_de + } + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { +- _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); +- if (_ep[i] == NULL) ++ fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); ++ if (!fotg210->ep[i]) + goto err_alloc; +- fotg210->ep[i] = _ep[i]; + } + + fotg210->reg = ioremap(res->start, resource_size(res)); diff --git a/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch b/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch new file mode 100644 index 00000000000..178135662f5 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0013-usb-fotg210-udc-fix-potential-memory-leak-in-fotg210.patch @@ -0,0 +1,58 @@ +From 7b95ade85ac18eec63e81ac58a482b3e88361ffd Mon Sep 17 00:00:00 2001 +From: Yi Yang +Date: Fri, 2 Dec 2022 09:21:26 +0800 +Subject: [PATCH 13/29] usb: fotg210-udc: fix potential memory leak in + fotg210_udc_probe() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In fotg210_udc_probe(), if devm_clk_get() or clk_prepare_enable() +fails, 'fotg210' will not be freed, which will lead to a memory leak. +Fix it by moving kfree() to a proper location. + +In addition,we can use "return -ENOMEM" instead of "goto err" +to simplify the code. + +Fixes: 718a38d092ec ("fotg210-udc: Handle PCLK") +Reviewed-by: Andrzej Pietrasiewicz +Reviewed-by: Linus Walleij +Signed-off-by: Yi Yang +Link: https://lore.kernel.org/r/20221202012126.246953-1-yiyang13@huawei.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1176,12 +1176,10 @@ int fotg210_udc_probe(struct platform_de + return -ENODEV; + } + +- ret = -ENOMEM; +- + /* initialize udc */ + fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + if (fotg210 == NULL) +- goto err; ++ return -ENOMEM; + + fotg210->dev = dev; + +@@ -1191,7 +1189,7 @@ int fotg210_udc_probe(struct platform_de + ret = clk_prepare_enable(fotg210->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); +- return ret; ++ goto err; + } + } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { + /* +@@ -1317,8 +1315,7 @@ err_pclk: + if (!IS_ERR(fotg210->pclk)) + clk_disable_unprepare(fotg210->pclk); + +- kfree(fotg210); +- + err: ++ kfree(fotg210); + return ret; + } diff --git a/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch b/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch new file mode 100644 index 00000000000..acdf1796f3b --- /dev/null +++ b/target/linux/gemini/patches-6.1/0014-usb-fotg210-fix-OTG-only-build.patch @@ -0,0 +1,39 @@ +From d8eed400495029ba551704ff0fae1dad87332291 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 15 Dec 2022 17:57:20 +0100 +Subject: [PATCH 14/29] usb: fotg210: fix OTG-only build + +The fotg210 module combines the HCD and OTG drivers, which then +fails to build when only the USB gadget support is enabled +in the kernel but host support is not: + +aarch64-linux-ld: drivers/usb/fotg210/fotg210-core.o: in function `fotg210_init': +fotg210-core.c:(.init.text+0xc): undefined reference to `usb_disabled' + +Move the check for usb_disabled() after the check for the HCD module, +and let the OTG driver still be probed in this configuration. + +A nicer approach might be to have the common portion built as a +library module, with the two platform other files registering +their own platform_driver instances separately. + +Fixes: ddacd6ef44ca ("usb: fotg210: Fix Kconfig for USB host modules") +Reviewed-by: Linus Walleij +Signed-off-by: Arnd Bergmann +Link: https://lore.kernel.org/r/20221215165728.2062984-1-arnd@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -144,10 +144,7 @@ static struct platform_driver fotg210_dr + + static int __init fotg210_init(void) + { +- if (usb_disabled()) +- return -ENODEV; +- +- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD)) ++ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) && !usb_disabled()) + fotg210_hcd_init(); + return platform_driver_register(&fotg210_driver); + } diff --git a/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch b/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch new file mode 100644 index 00000000000..a9bbca58b4a --- /dev/null +++ b/target/linux/gemini/patches-6.1/0015-usb-fotg210-udc-fix-error-return-code-in-fotg210_udc.patch @@ -0,0 +1,28 @@ +From eaaa85d907fe27852dd960b2bc5d7bcf11bc3ebd Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Fri, 30 Dec 2022 14:54:27 +0800 +Subject: [PATCH 15/29] usb: fotg210-udc: fix error return code in + fotg210_udc_probe() + +After commit 5f217ccd520f ("fotg210-udc: Support optional external PHY"), +the error code is re-assigned to 0 in fotg210_udc_probe(), if allocate or +map memory fails after the assignment, it can't return an error code. Set +the error code to -ENOMEM to fix this problem. + +Fixes: 5f217ccd520f ("fotg210-udc: Support optional external PHY") +Signed-off-by: Yang Yingliang +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20221230065427.944586-1-yangyingliang@huawei.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1214,6 +1214,8 @@ int fotg210_udc_probe(struct platform_de + dev_info(dev, "found and initialized PHY\n"); + } + ++ ret = -ENOMEM; ++ + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (!fotg210->ep[i]) diff --git a/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch b/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch new file mode 100644 index 00000000000..6ff6d28ad3f --- /dev/null +++ b/target/linux/gemini/patches-6.1/0016-usb-fotg210-List-different-variants.patch @@ -0,0 +1,25 @@ +From 407577548b2fcd41cc72ee05df1f05a430ed30a0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:16 +0100 +Subject: [PATCH 16/29] usb: fotg210: List different variants + +There are at least two variants of the FOTG: FOTG200 and +FOTG210. Handle them in this driver and let's add +more quirks as we go along. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-2-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -127,7 +127,9 @@ static int fotg210_remove(struct platfor + + #ifdef CONFIG_OF + static const struct of_device_id fotg210_of_match[] = { ++ { .compatible = "faraday,fotg200" }, + { .compatible = "faraday,fotg210" }, ++ /* TODO: can we also handle FUSB220? */ + {}, + }; + MODULE_DEVICE_TABLE(of, fotg210_of_match); diff --git a/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch b/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch new file mode 100644 index 00000000000..7dbd511ecbe --- /dev/null +++ b/target/linux/gemini/patches-6.1/0017-usb-fotg210-Acquire-memory-resource-in-core.patch @@ -0,0 +1,245 @@ +From fa735ad1afeb5791d5562617b9bbed74574d3e81 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:17 +0100 +Subject: [PATCH 17/29] usb: fotg210: Acquire memory resource in core + +The subdrivers are obtaining and mapping the memory resource +separately. Create a common state container for the shared +resources and start populating this by acquiring the IO +memory resource and remap it and pass this to the subdrivers +for host and peripheral. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-3-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -33,9 +33,10 @@ + #define GEMINI_MISC_USB0_MINI_B BIT(29) + #define GEMINI_MISC_USB1_MINI_B BIT(30) + +-static int fotg210_gemini_init(struct device *dev, struct resource *res, ++static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res, + enum usb_dr_mode mode) + { ++ struct device *dev = fotg->dev; + struct device_node *np = dev->of_node; + struct regmap *map; + bool wakeup; +@@ -47,6 +48,7 @@ static int fotg210_gemini_init(struct de + dev_err(dev, "no syscon\n"); + return PTR_ERR(map); + } ++ fotg->map = map; + wakeup = of_property_read_bool(np, "wakeup-source"); + + /* +@@ -55,6 +57,7 @@ static int fotg210_gemini_init(struct de + */ + mask = 0; + if (res->start == 0x69000000) { ++ fotg->port = GEMINI_PORT_1; + mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B | + GEMINI_MISC_USB1_WAKEUP; + if (mode == USB_DR_MODE_HOST) +@@ -64,6 +67,7 @@ static int fotg210_gemini_init(struct de + if (wakeup) + val |= GEMINI_MISC_USB1_WAKEUP; + } else { ++ fotg->port = GEMINI_PORT_0; + mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B | + GEMINI_MISC_USB0_WAKEUP; + if (mode == USB_DR_MODE_HOST) +@@ -89,23 +93,34 @@ static int fotg210_probe(struct platform + { + struct device *dev = &pdev->dev; + enum usb_dr_mode mode; ++ struct fotg210 *fotg; + int ret; + ++ fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); ++ if (!fotg) ++ return -ENOMEM; ++ fotg->dev = dev; ++ ++ fotg->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!fotg->res) ++ return -ENODEV; ++ ++ fotg->base = devm_ioremap_resource(dev, fotg->res); ++ if (!fotg->base) ++ return -ENOMEM; ++ + mode = usb_get_dr_mode(dev); + + if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { +- struct resource *res; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- ret = fotg210_gemini_init(dev, res, mode); ++ ret = fotg210_gemini_init(fotg, fotg->res, mode); + if (ret) + return ret; + } + + if (mode == USB_DR_MODE_PERIPHERAL) +- ret = fotg210_udc_probe(pdev); ++ ret = fotg210_udc_probe(pdev, fotg); + else +- ret = fotg210_hcd_probe(pdev); ++ ret = fotg210_hcd_probe(pdev, fotg); + + return ret; + } +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -5557,11 +5557,10 @@ static void fotg210_init(struct fotg210_ + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +-int fotg210_hcd_probe(struct platform_device *pdev) ++int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg) + { + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; +- struct resource *res; + int irq; + int retval; + struct fotg210_hcd *fotg210; +@@ -5585,18 +5584,14 @@ int fotg210_hcd_probe(struct platform_de + + hcd->has_tt = 1; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- hcd->regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(hcd->regs)) { +- retval = PTR_ERR(hcd->regs); +- goto failed_put_hcd; +- } ++ hcd->regs = fotg->base; + +- hcd->rsrc_start = res->start; +- hcd->rsrc_len = resource_size(res); ++ hcd->rsrc_start = fotg->res->start; ++ hcd->rsrc_len = resource_size(fotg->res); + + fotg210 = hcd_to_fotg210(hcd); + ++ fotg210->fotg = fotg; + fotg210->caps = hcd->regs; + + /* It's OK not to supply this clock */ +--- a/drivers/usb/fotg210/fotg210-hcd.h ++++ b/drivers/usb/fotg210/fotg210-hcd.h +@@ -182,6 +182,7 @@ struct fotg210_hcd { /* one per contro + # define INCR(x) do {} while (0) + #endif + ++ struct fotg210 *fotg; /* Overarching FOTG210 device */ + /* silicon clock */ + struct clk *pclk; + }; +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1155,21 +1155,14 @@ int fotg210_udc_remove(struct platform_d + return 0; + } + +-int fotg210_udc_probe(struct platform_device *pdev) ++int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg) + { +- struct resource *res; + struct fotg210_udc *fotg210 = NULL; + struct device *dev = &pdev->dev; + int irq; + int ret = 0; + int i; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- pr_err("platform_get_resource error.\n"); +- return -ENODEV; +- } +- + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("could not get irq\n"); +@@ -1182,6 +1175,7 @@ int fotg210_udc_probe(struct platform_de + return -ENOMEM; + + fotg210->dev = dev; ++ fotg210->fotg = fotg; + + /* It's OK not to supply this clock */ + fotg210->pclk = devm_clk_get(dev, "PCLK"); +@@ -1222,11 +1216,7 @@ int fotg210_udc_probe(struct platform_de + goto err_alloc; + } + +- fotg210->reg = ioremap(res->start, resource_size(res)); +- if (fotg210->reg == NULL) { +- dev_err(dev, "ioremap error\n"); +- goto err_alloc; +- } ++ fotg210->reg = fotg->base; + + spin_lock_init(&fotg210->lock); + +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -236,6 +236,7 @@ struct fotg210_udc { + unsigned long irq_trigger; + + struct device *dev; ++ struct fotg210 *fotg; + struct usb_phy *phy; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -2,13 +2,28 @@ + #ifndef __FOTG210_H + #define __FOTG210_H + ++enum gemini_port { ++ GEMINI_PORT_NONE = 0, ++ GEMINI_PORT_0, ++ GEMINI_PORT_1, ++}; ++ ++struct fotg210 { ++ struct device *dev; ++ struct resource *res; ++ void __iomem *base; ++ struct regmap *map; ++ enum gemini_port port; ++}; ++ + #ifdef CONFIG_USB_FOTG210_HCD +-int fotg210_hcd_probe(struct platform_device *pdev); ++int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_hcd_remove(struct platform_device *pdev); + int fotg210_hcd_init(void); + void fotg210_hcd_cleanup(void); + #else +-static inline int fotg210_hcd_probe(struct platform_device *pdev) ++static inline int fotg210_hcd_probe(struct platform_device *pdev, ++ struct fotg210 *fotg) + { + return 0; + } +@@ -26,10 +41,11 @@ static inline void fotg210_hcd_cleanup(v + #endif + + #ifdef CONFIG_USB_FOTG210_UDC +-int fotg210_udc_probe(struct platform_device *pdev); ++int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_udc_remove(struct platform_device *pdev); + #else +-static inline int fotg210_udc_probe(struct platform_device *pdev) ++static inline int fotg210_udc_probe(struct platform_device *pdev, ++ struct fotg210 *fotg) + { + return 0; + } diff --git a/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch b/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch new file mode 100644 index 00000000000..9894f4dc66d --- /dev/null +++ b/target/linux/gemini/patches-6.1/0018-usb-fotg210-Move-clock-handling-to-core.patch @@ -0,0 +1,196 @@ +From fb8e1e8dbc47e7aff7624b47adaa0a84d2983802 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:18 +0100 +Subject: [PATCH 18/29] usb: fotg210: Move clock handling to core + +Grab the optional silicon block clock, prepare and enable it in +the core before proceeding to prepare the host or peripheral +driver. This saves duplicate code and also uses the simple +devm_clk_get_optional_enabled() to do everything we really +want to do. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-4-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -6,6 +6,7 @@ + * driver. + */ + #include ++#include + #include + #include + #include +@@ -109,6 +110,10 @@ static int fotg210_probe(struct platform + if (!fotg->base) + return -ENOMEM; + ++ fotg->pclk = devm_clk_get_optional_enabled(dev, "PCLK"); ++ if (IS_ERR(fotg->pclk)) ++ return PTR_ERR(fotg->pclk); ++ + mode = usb_get_dr_mode(dev); + + if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) { +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -33,7 +33,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -5594,44 +5593,22 @@ int fotg210_hcd_probe(struct platform_de + fotg210->fotg = fotg; + fotg210->caps = hcd->regs; + +- /* It's OK not to supply this clock */ +- fotg210->pclk = clk_get(dev, "PCLK"); +- if (!IS_ERR(fotg210->pclk)) { +- retval = clk_prepare_enable(fotg210->pclk); +- if (retval) { +- dev_err(dev, "failed to enable PCLK\n"); +- goto failed_put_hcd; +- } +- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { +- /* +- * Percolate deferrals, for anything else, +- * just live without the clocking. +- */ +- retval = PTR_ERR(fotg210->pclk); +- goto failed_dis_clk; +- } +- + retval = fotg210_setup(hcd); + if (retval) +- goto failed_dis_clk; ++ goto failed_put_hcd; + + fotg210_init(fotg210); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) { + dev_err(dev, "failed to add hcd with err %d\n", retval); +- goto failed_dis_clk; ++ goto failed_put_hcd; + } + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(pdev, hcd); + + return retval; + +-failed_dis_clk: +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } + failed_put_hcd: + usb_put_hcd(hcd); + fail_create_hcd: +@@ -5647,12 +5624,6 @@ fail_create_hcd: + int fotg210_hcd_remove(struct platform_device *pdev) + { + struct usb_hcd *hcd = platform_get_drvdata(pdev); +- struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); +- +- if (!IS_ERR(fotg210->pclk)) { +- clk_disable_unprepare(fotg210->pclk); +- clk_put(fotg210->pclk); +- } + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -1147,9 +1146,6 @@ int fotg210_udc_remove(struct platform_d + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); + +- if (!IS_ERR(fotg210->pclk)) +- clk_disable_unprepare(fotg210->pclk); +- + kfree(fotg210); + + return 0; +@@ -1177,34 +1173,17 @@ int fotg210_udc_probe(struct platform_de + fotg210->dev = dev; + fotg210->fotg = fotg; + +- /* It's OK not to supply this clock */ +- fotg210->pclk = devm_clk_get(dev, "PCLK"); +- if (!IS_ERR(fotg210->pclk)) { +- ret = clk_prepare_enable(fotg210->pclk); +- if (ret) { +- dev_err(dev, "failed to enable PCLK\n"); +- goto err; +- } +- } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) { +- /* +- * Percolate deferrals, for anything else, +- * just live without the clocking. +- */ +- ret = -EPROBE_DEFER; +- goto err; +- } +- + fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + if (IS_ERR(fotg210->phy)) { + ret = PTR_ERR(fotg210->phy); + if (ret == -EPROBE_DEFER) +- goto err_pclk; ++ goto err_free; + dev_info(dev, "no PHY found\n"); + fotg210->phy = NULL; + } else { + ret = usb_phy_init(fotg210->phy); + if (ret) +- goto err_pclk; ++ goto err_free; + dev_info(dev, "found and initialized PHY\n"); + } + +@@ -1303,11 +1282,8 @@ err_map: + err_alloc: + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) + kfree(fotg210->ep[i]); +-err_pclk: +- if (!IS_ERR(fotg210->pclk)) +- clk_disable_unprepare(fotg210->pclk); + +-err: ++err_free: + kfree(fotg210); + return ret; + } +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -231,7 +231,6 @@ struct fotg210_ep { + struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; +- struct clk *pclk; + + unsigned long irq_trigger; + +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -12,6 +12,7 @@ struct fotg210 { + struct device *dev; + struct resource *res; + void __iomem *base; ++ struct clk *pclk; + struct regmap *map; + enum gemini_port port; + }; diff --git a/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch b/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch new file mode 100644 index 00000000000..892b0d31af0 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0019-usb-fotg210-Check-role-register-in-core.patch @@ -0,0 +1,54 @@ +From b1b07abb598211de3ce7f52abdf8dcb24384341e Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:19 +0100 +Subject: [PATCH 19/29] usb: fotg210: Check role register in core + +Read the role register and check that we are in host/peripheral +mode and issue warnings if we're not in the right role when +probing respective driver. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-5-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -18,6 +18,11 @@ + + #include "fotg210.h" + ++/* Role Register 0x80 */ ++#define FOTG210_RR 0x80 ++#define FOTG210_RR_ID BIT(21) /* 1 = B-device, 0 = A-device */ ++#define FOTG210_RR_CROLE BIT(20) /* 1 = device, 0 = host */ ++ + /* + * Gemini-specific initialization function, only executed on the + * Gemini SoC using the global misc control register. +@@ -95,6 +100,7 @@ static int fotg210_probe(struct platform + struct device *dev = &pdev->dev; + enum usb_dr_mode mode; + struct fotg210 *fotg; ++ u32 val; + int ret; + + fotg = devm_kzalloc(dev, sizeof(*fotg), GFP_KERNEL); +@@ -122,10 +128,16 @@ static int fotg210_probe(struct platform + return ret; + } + +- if (mode == USB_DR_MODE_PERIPHERAL) ++ val = readl(fotg->base + FOTG210_RR); ++ if (mode == USB_DR_MODE_PERIPHERAL) { ++ if (!(val & FOTG210_RR_CROLE)) ++ dev_err(dev, "block not in device role\n"); + ret = fotg210_udc_probe(pdev, fotg); +- else ++ } else { ++ if (val & FOTG210_RR_CROLE) ++ dev_err(dev, "block not in host role\n"); + ret = fotg210_hcd_probe(pdev, fotg); ++ } + + return ret; + } diff --git a/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch b/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch new file mode 100644 index 00000000000..20f8f943501 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0020-usb-fotg210-udc-Assign-of_node-and-speed-on-start.patch @@ -0,0 +1,34 @@ +From d7c2b0b6da75b86cf5ddbcd51a74d74e19bbf178 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:20 +0100 +Subject: [PATCH 20/29] usb: fotg210-udc: Assign of_node and speed on start + +Follow the example set by other drivers to assign of_node +and speed to the driver when binding, also print bound +info akin to other UDC drivers. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-6-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1028,6 +1028,10 @@ static int fotg210_udc_start(struct usb_ + + /* hook up the driver */ + fotg210->driver = driver; ++ fotg210->gadget.dev.of_node = fotg210->dev->of_node; ++ fotg210->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ dev_info(fotg210->dev, "bound driver %s\n", driver->driver.name); + + if (!IS_ERR_OR_NULL(fotg210->phy)) { + ret = otg_set_peripheral(fotg210->phy->otg, +@@ -1084,6 +1088,7 @@ static int fotg210_udc_stop(struct usb_g + + fotg210_init(fotg210); + fotg210->driver = NULL; ++ fotg210->gadget.speed = USB_SPEED_UNKNOWN; + + spin_unlock_irqrestore(&fotg210->lock, flags); + diff --git a/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch b/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch new file mode 100644 index 00000000000..d98561f0d4c --- /dev/null +++ b/target/linux/gemini/patches-6.1/0021-usb-fotg210-udc-Implement-VBUS-session.patch @@ -0,0 +1,96 @@ +From 2fbbfb2c556944945639b17b13fcb1e05272b646 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 18 Jan 2023 08:09:21 +0100 +Subject: [PATCH 21/29] usb: fotg210-udc: Implement VBUS session + +Implement VBUS session handling for FOTG210. This is +mainly used by the UDC driver which needs to call down to +the FOTG210 core and enable/disable VBUS, as this needs to be +handled outside of the HCD and UDC drivers, by platform +specific glue code. + +The Gemini has a special bit in a system register to turn +VBUS on and off so we implement this in the FOTG210 core. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230103-gemini-fotg210-usb-v2-7-100388af9810@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-core.c ++++ b/drivers/usb/fotg210/fotg210-core.c +@@ -95,6 +95,35 @@ static int fotg210_gemini_init(struct fo + return 0; + } + ++/** ++ * fotg210_vbus() - Called by gadget driver to enable/disable VBUS ++ * @enable: true to enable VBUS, false to disable VBUS ++ */ ++void fotg210_vbus(struct fotg210 *fotg, bool enable) ++{ ++ u32 mask; ++ u32 val; ++ int ret; ++ ++ switch (fotg->port) { ++ case GEMINI_PORT_0: ++ mask = GEMINI_MISC_USB0_VBUS_ON; ++ val = enable ? GEMINI_MISC_USB0_VBUS_ON : 0; ++ break; ++ case GEMINI_PORT_1: ++ mask = GEMINI_MISC_USB1_VBUS_ON; ++ val = enable ? GEMINI_MISC_USB1_VBUS_ON : 0; ++ break; ++ default: ++ return; ++ } ++ ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val); ++ if (ret) ++ dev_err(fotg->dev, "failed to %s VBUS\n", ++ enable ? "enable" : "disable"); ++ dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable"); ++} ++ + static int fotg210_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -1095,9 +1095,26 @@ static int fotg210_udc_stop(struct usb_g + return 0; + } + ++/** ++ * fotg210_vbus_session - Called by external transceiver to enable/disable udc ++ * @_gadget: usb gadget ++ * @is_active: 0 if should disable UDC VBUS, 1 if should enable ++ * ++ * Returns 0 ++ */ ++static int fotg210_vbus_session(struct usb_gadget *g, int is_active) ++{ ++ struct fotg210_udc *fotg210 = gadget_to_fotg210(g); ++ ++ /* Call down to core integration layer to drive or disable VBUS */ ++ fotg210_vbus(fotg210->fotg, is_active); ++ return 0; ++} ++ + static const struct usb_gadget_ops fotg210_gadget_ops = { + .udc_start = fotg210_udc_start, + .udc_stop = fotg210_udc_stop, ++ .vbus_session = fotg210_vbus_session, + }; + + /** +--- a/drivers/usb/fotg210/fotg210.h ++++ b/drivers/usb/fotg210/fotg210.h +@@ -17,6 +17,8 @@ struct fotg210 { + enum gemini_port port; + }; + ++void fotg210_vbus(struct fotg210 *fotg, bool enable); ++ + #ifdef CONFIG_USB_FOTG210_HCD + int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg); + int fotg210_hcd_remove(struct platform_device *pdev); diff --git a/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch b/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch new file mode 100644 index 00000000000..fc5831eb23b --- /dev/null +++ b/target/linux/gemini/patches-6.1/0022-fotg210-udc-Introduce-and-use-a-fotg210_ack_int-func.patch @@ -0,0 +1,134 @@ +From f011d1eab23f4c063c5441c0d5a22898adf9145c Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Mon, 23 Jan 2023 08:35:07 +0100 +Subject: [PATCH 22/29] fotg210-udc: Introduce and use a fotg210_ack_int + function + +This is in preparation of support for devices where interrupts are acked +differently. + +Signed-off-by: Fabian Vogt +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073508.2350402-3-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -28,6 +28,14 @@ static const char udc_name[] = "fotg210_ + static const char * const fotg210_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4"}; + ++static void fotg210_ack_int(struct fotg210_udc *fotg210, u32 offset, u32 mask) ++{ ++ u32 value = ioread32(fotg210->reg + offset); ++ ++ value &= ~mask; ++ iowrite32(value, fotg210->reg + offset); ++} ++ + static void fotg210_disable_fifo_int(struct fotg210_ep *ep) + { + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); +@@ -303,8 +311,7 @@ static void fotg210_wait_dma_done(struct + goto dma_reset; + } while (!(value & DISGR2_DMA_CMPLT)); + +- value &= ~DISGR2_DMA_CMPLT; +- iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); ++ fotg210_ack_int(ep->fotg210, FOTG210_DISGR2, DISGR2_DMA_CMPLT); + return; + + dma_reset: +@@ -844,14 +851,6 @@ static void fotg210_ep0in(struct fotg210 + } + } + +-static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +-{ +- u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); +- +- value &= ~DISGR0_CX_COMABT_INT; +- iowrite32(value, fotg210->reg + FOTG210_DISGR0); +-} +- + static void fotg210_in_fifo_handler(struct fotg210_ep *ep) + { + struct fotg210_request *req = list_entry(ep->queue.next, +@@ -893,60 +892,43 @@ static irqreturn_t fotg210_irq(int irq, + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); +- u32 value; + + int_grp2 &= ~int_msk2; + + if (int_grp2 & DISGR2_USBRST_INT) { + usb_gadget_udc_reset(&fotg210->gadget, + fotg210->driver); +- value = ioread32(reg); +- value &= ~DISGR2_USBRST_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_USBRST_INT); + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_SUSP_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_SUSP_INT); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_RESM_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RESM_INT); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ERR_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ERR_INT); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { +- value = ioread32(reg); +- value &= ~DISGR2_ISO_SEQ_ABORT_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_ISO_SEQ_ABORT_INT); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_TX0BYTE_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_TX0BYTE_INT); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + fotg210_clear_rx0byte(fotg210); +- value = ioread32(reg); +- value &= ~DISGR2_RX0BYTE_INT; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_RX0BYTE_INT); + pr_info("fotg210 received 0 byte\n"); + } + if (int_grp2 & DISGR2_DMA_ERROR) { +- value = ioread32(reg); +- value &= ~DISGR2_DMA_ERROR; +- iowrite32(value, reg); ++ fotg210_ack_int(fotg210, FOTG210_DISGR2, DISGR2_DMA_ERROR); + } + } + +@@ -960,7 +942,7 @@ static irqreturn_t fotg210_irq(int irq, + + /* the highest priority in this source register */ + if (int_grp0 & DISGR0_CX_COMABT_INT) { +- fotg210_clear_comabt_int(fotg210); ++ fotg210_ack_int(fotg210, FOTG210_DISGR0, DISGR0_CX_COMABT_INT); + pr_info("fotg210 CX command abort\n"); + } + diff --git a/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch b/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch new file mode 100644 index 00000000000..fde17a48b3d --- /dev/null +++ b/target/linux/gemini/patches-6.1/0023-fotg210-udc-Improve-device-initialization.patch @@ -0,0 +1,62 @@ +From 367747c7813cecf19b46ef7134691f903ab76dc9 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Mon, 23 Jan 2023 08:35:08 +0100 +Subject: [PATCH 23/29] fotg210-udc: Improve device initialization + +Reset the device explicitly to get into a known state and also set the chip +enable bit. Additionally, mask interrupts which aren't handled. + +Signed-off-by: Fabian Vogt +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073508.2350402-4-linus.walleij@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-udc.c ++++ b/drivers/usb/fotg210/fotg210-udc.c +@@ -7,6 +7,7 @@ + * Author : Yuan-Hsin Chen + */ + ++#include + #include + #include + #include +@@ -1022,6 +1023,11 @@ static int fotg210_udc_start(struct usb_ + dev_err(fotg210->dev, "can't bind to phy\n"); + } + ++ /* chip enable */ ++ value = ioread32(fotg210->reg + FOTG210_DMCR); ++ value |= DMCR_CHIP_EN; ++ iowrite32(value, fotg210->reg + FOTG210_DMCR); ++ + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; +@@ -1038,6 +1044,15 @@ static void fotg210_init(struct fotg210_ + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + fotg210->reg + FOTG210_GMIR); + ++ /* mask interrupts for groups other than 0-2 */ ++ iowrite32(~(DMIGR_MINT_G0 | DMIGR_MINT_G1 | DMIGR_MINT_G2), ++ fotg210->reg + FOTG210_DMIGR); ++ ++ /* udc software reset */ ++ iowrite32(DMCR_SFRST, fotg210->reg + FOTG210_DMCR); ++ /* Better wait a bit, but without a datasheet, no idea how long. */ ++ usleep_range(100, 200); ++ + /* disable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value &= ~DMCR_GLINT_EN; +--- a/drivers/usb/fotg210/fotg210-udc.h ++++ b/drivers/usb/fotg210/fotg210-udc.h +@@ -58,6 +58,8 @@ + + /* Device Mask of Interrupt Group Register (0x130) */ + #define FOTG210_DMIGR 0x130 ++#define DMIGR_MINT_G2 (1 << 2) ++#define DMIGR_MINT_G1 (1 << 1) + #define DMIGR_MINT_G0 (1 << 0) + + /* Device Mask of Interrupt Source Group 0(0x134) */ diff --git a/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch b/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch new file mode 100644 index 00000000000..680836110a0 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0024-usb-fotg210-hcd-use-sysfs_emit-to-instead-of-scnprin.patch @@ -0,0 +1,32 @@ +From 482830a70408a5d30af264b3d6706f818c78b2b2 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 20 Jan 2023 17:44:33 +0200 +Subject: [PATCH 24/29] usb: fotg210-hcd: use sysfs_emit() to instead of + scnprintf() + +Follow the advice of the Documentation/filesystems/sysfs.rst and show() +should only use sysfs_emit() or sysfs_emit_at() when formatting the +value to be returned to user space. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230120154437.22025-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- +--- a/drivers/usb/fotg210/fotg210-hcd.c ++++ b/drivers/usb/fotg210/fotg210-hcd.c +@@ -4686,14 +4686,11 @@ static ssize_t uframe_periodic_max_show( + struct device_attribute *attr, char *buf) + { + struct fotg210_hcd *fotg210; +- int n; + + fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); +- n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); +- return n; ++ return sysfs_emit(buf, "%d\n", fotg210->uframe_periodic_max); + } + +- + static ssize_t uframe_periodic_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + { diff --git a/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch b/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch new file mode 100644 index 00000000000..1e031f1d4ff --- /dev/null +++ b/target/linux/gemini/patches-6.1/0025-ARM-dts-gemini-Push-down-flash-address-size-cells.patch @@ -0,0 +1,62 @@ +From 6b84aa39a063eec883d410a9893cec70fce56163 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:28 +0100 +Subject: [PATCH 25/29] ARM: dts: gemini: Push down flash address/size cells + +The platforms not defining any OF partions complain like +this: + +../arch/arm/boot/dts/gemini.dtsi:19.25-28.5: Warning + (avoid_unnecessary_addr_size): /soc/flash@30000000: unnecessary + #address-cells/#size-cells without "ranges" or child "reg" property + +Get rid of this by only defining the address-cells and +size-cells where it is actually used by OF partitions. + +Link: https://lore.kernel.org/r/20221204190230.3345590-1-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts ++++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts +@@ -164,6 +164,8 @@ + compatible = "cortina,gemini-flash", "jedec-flash"; + status = "okay"; + reg = <0x30000000 0x00080000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + /* + * This "RedBoot" is the Storlink derivative. +--- a/arch/arm/boot/dts/gemini-wbd111.dts ++++ b/arch/arm/boot/dts/gemini-wbd111.dts +@@ -86,6 +86,8 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + partition@0 { + label = "RedBoot"; +--- a/arch/arm/boot/dts/gemini-wbd222.dts ++++ b/arch/arm/boot/dts/gemini-wbd222.dts +@@ -90,6 +90,8 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; ++ #address-cells = <1>; ++ #size-cells = <1>; + + partition@0 { + label = "RedBoot"; +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -22,8 +22,6 @@ + pinctrl-names = "default"; + pinctrl-0 = <&pflash_default_pins>; + bank-width = <2>; +- #address-cells = <1>; +- #size-cells = <1>; + status = "disabled"; + }; + diff --git a/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch new file mode 100644 index 00000000000..1aff23ed1ba --- /dev/null +++ b/target/linux/gemini/patches-6.1/0026-ARM-dts-gemini-wbd111-Use-RedBoot-partion-parser.patch @@ -0,0 +1,54 @@ +From 0e733f5af628210f372585e431504a7024e7b571 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:29 +0100 +Subject: [PATCH 26/29] ARM: dts: gemini: wbd111: Use RedBoot partion parser + +This is clearly a RedBoot partitioned device with 0x20000 +sized erase blocks. + +Link: https://lore.kernel.org/r/20221204190230.3345590-2-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-wbd111.dts ++++ b/arch/arm/boot/dts/gemini-wbd111.dts +@@ -86,36 +86,11 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; +- #address-cells = <1>; +- #size-cells = <1>; + +- partition@0 { +- label = "RedBoot"; +- reg = <0x00000000 0x00020000>; +- read-only; +- }; +- partition@20000 { +- label = "kernel"; +- reg = <0x00020000 0x00100000>; +- }; +- partition@120000 { +- label = "rootfs"; +- reg = <0x00120000 0x006a0000>; +- }; +- partition@7c0000 { +- label = "VCTL"; +- reg = <0x007c0000 0x00010000>; +- read-only; +- }; +- partition@7d0000 { +- label = "cfg"; +- reg = <0x007d0000 0x00010000>; +- read-only; +- }; +- partition@7e0000 { +- label = "FIS"; +- reg = <0x007e0000 0x00010000>; +- read-only; ++ partitions { ++ compatible = "redboot-fis"; ++ /* Eraseblock at 0x7e0000 */ ++ fis-index-block = <0x3f>; + }; + }; + diff --git a/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch b/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch new file mode 100644 index 00000000000..8cafeaa0dfe --- /dev/null +++ b/target/linux/gemini/patches-6.1/0027-ARM-dts-gemini-wbd222-Use-RedBoot-partion-parser.patch @@ -0,0 +1,54 @@ +From 8558e2e1110a5daa4ac9e1c5b5c15e1651a8fb94 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 4 Dec 2022 20:02:30 +0100 +Subject: [PATCH 27/29] ARM: dts: gemini: wbd222: Use RedBoot partion parser + +This is clearly a RedBoot partitioned device with 0x20000 +sized erase blocks. + +Link: https://lore.kernel.org/r/20221204190230.3345590-3-linus.walleij@linaro.org +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-wbd222.dts ++++ b/arch/arm/boot/dts/gemini-wbd222.dts +@@ -90,36 +90,11 @@ + status = "okay"; + /* 8MB of flash */ + reg = <0x30000000 0x00800000>; +- #address-cells = <1>; +- #size-cells = <1>; + +- partition@0 { +- label = "RedBoot"; +- reg = <0x00000000 0x00020000>; +- read-only; +- }; +- partition@20000 { +- label = "kernel"; +- reg = <0x00020000 0x00100000>; +- }; +- partition@120000 { +- label = "rootfs"; +- reg = <0x00120000 0x006a0000>; +- }; +- partition@7c0000 { +- label = "VCTL"; +- reg = <0x007c0000 0x00010000>; +- read-only; +- }; +- partition@7d0000 { +- label = "cfg"; +- reg = <0x007d0000 0x00010000>; +- read-only; +- }; +- partition@7e0000 { +- label = "FIS"; +- reg = <0x007e0000 0x00010000>; +- read-only; ++ partitions { ++ compatible = "redboot-fis"; ++ /* Eraseblock at 0x7e0000 */ ++ fis-index-block = <0x3f>; + }; + }; + diff --git a/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch b/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch new file mode 100644 index 00000000000..fb93b70a313 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0028-ARM-dts-gemini-Fix-USB-block-version.patch @@ -0,0 +1,31 @@ +From d5c01ce4a1016507c69682894cf6b66301abca3d Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 23 Jan 2023 08:39:15 +0100 +Subject: [PATCH 28/29] ARM: dts: gemini: Fix USB block version + +The FOTG version in the Gemini is the FOTG200, fix this +up. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073916.2350839-1-linus.walleij@linaro.org +--- +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -439,7 +439,7 @@ + }; + + usb0: usb@68000000 { +- compatible = "cortina,gemini-usb", "faraday,fotg210"; ++ compatible = "cortina,gemini-usb", "faraday,fotg200"; + reg = <0x68000000 0x1000>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; + resets = <&syscon GEMINI_RESET_USB0>; +@@ -460,7 +460,7 @@ + }; + + usb1: usb@69000000 { +- compatible = "cortina,gemini-usb", "faraday,fotg210"; ++ compatible = "cortina,gemini-usb", "faraday,fotg200"; + reg = <0x69000000 0x1000>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + resets = <&syscon GEMINI_RESET_USB1>; diff --git a/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch b/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch new file mode 100644 index 00000000000..667878170b7 --- /dev/null +++ b/target/linux/gemini/patches-6.1/0029-ARM-dts-gemini-Enable-DNS313-FOTG210-as-periph.patch @@ -0,0 +1,54 @@ +From 296184694ae7a4e388603c95499e98d30b21cc09 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 23 Jan 2023 08:39:16 +0100 +Subject: [PATCH 29/29] ARM: dts: gemini: Enable DNS313 FOTG210 as periph + +Add the GPIO-based VBUS phy, and enable the FOTG210 +USB1 block for use as peripheral. + +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20230123073916.2350839-2-linus.walleij@linaro.org +--- +--- a/arch/arm/boot/dts/gemini-dlink-dns-313.dts ++++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts +@@ -80,6 +80,15 @@ + #cooling-cells = <2>; + }; + ++ /* ++ * This is the type B USB connector on the device, ++ * a GPIO-controlled USB VBUS detect ++ */ ++ usb1_phy: phy { ++ compatible = "gpio-usb-b-connector", "usb-b-connector"; ++ #phy-cells = <0>; ++ vbus-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; ++ }; + + /* Global Mixed-Mode Technology G751 mounted on GPIO I2C */ + i2c { +@@ -302,5 +311,13 @@ + ide@63000000 { + status = "okay"; + }; ++ ++ usb@69000000 { ++ status = "okay"; ++ dr_mode = "peripheral"; ++ usb-phy = <&usb1_phy>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_default_pins>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/gemini.dtsi ++++ b/arch/arm/boot/dts/gemini.dtsi +@@ -455,6 +455,8 @@ + */ + pinctrl-names = "default"; + pinctrl-0 = <&usb_default_pins>; ++ /* Default to host mode */ ++ dr_mode = "host"; + syscon = <&syscon>; + status = "disabled"; + }; diff --git a/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch b/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch new file mode 100644 index 00000000000..99e0d2731da --- /dev/null +++ b/target/linux/gemini/patches-6.1/300-ARM-dts-Augment-DIR-685-partition-table-for-OpenWrt.patch @@ -0,0 +1,34 @@ +From 36ee838bf83c01cff7cb47c7b07be278d2950ac0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Mon, 11 Mar 2019 15:44:29 +0100 +Subject: [PATCH 2/2] ARM: dts: Augment DIR-685 partition table for OpenWrt + +Rename the firmware partition so that the firmware MTD +splitter will do its job, drop the rootfs arguments as +the MTD splitter will set this up automatically. + +Signed-off-by: Linus Walleij +--- +--- a/arch/arm/boot/dts/gemini-dlink-dir-685.dts ++++ b/arch/arm/boot/dts/gemini-dlink-dir-685.dts +@@ -20,7 +20,7 @@ + }; + + chosen { +- bootargs = "console=ttyS0,19200n8 root=/dev/sda1 rw rootwait consoleblank=300"; ++ bootargs = "console=ttyS0,19200n8 consoleblank=300"; + stdout-path = "uart0:19200n8"; + }; + +@@ -317,9 +317,9 @@ + * this is called "upgrade" on the vendor system. + */ + partition@40000 { +- label = "upgrade"; ++ compatible = "wrg"; ++ label = "firmware"; + reg = <0x00040000 0x01f40000>; +- read-only; + }; + /* RGDB, Residental Gateway Database? */ + partition@1f80000 { diff --git a/target/linux/generic/backport-6.1/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch b/target/linux/generic/backport-6.1/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch index d63b03e6e87..9d21f8de22f 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch @@ -126,11 +126,9 @@ Signed-off-by: T.J. Mercier mm/workingset.c | 4 ++-- 4 files changed, 24 insertions(+), 24 deletions(-) -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index e8ed225d8f7ca..f63968bd7de59 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -178,7 +178,7 @@ static inline void lru_gen_update_size(struct lruvec *lruvec, struct folio *foli +@@ -178,7 +178,7 @@ static inline void lru_gen_update_size(s int zone = folio_zonenum(folio); int delta = folio_nr_pages(folio); enum lru_list lru = type * LRU_INACTIVE_FILE; @@ -139,7 +137,7 @@ index e8ed225d8f7ca..f63968bd7de59 100644 VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS); VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS); -@@ -224,7 +224,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, +@@ -224,7 +224,7 @@ static inline bool lru_gen_add_folio(str int gen = folio_lru_gen(folio); int type = folio_is_file_lru(folio); int zone = folio_zonenum(folio); @@ -148,8 +146,6 @@ index e8ed225d8f7ca..f63968bd7de59 100644 VM_WARN_ON_ONCE_FOLIO(gen != -1, folio); -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index 5f74891556f33..bd3e4689f72dc 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -404,7 +404,7 @@ enum { @@ -179,11 +175,9 @@ index 5f74891556f33..bd3e4689f72dc 100644 /* to concurrently iterate lru_gen_mm_list */ struct lru_gen_mm_state mm_state; #endif -diff --git a/mm/vmscan.c b/mm/vmscan.c -index d18296109aa7e..27142caf284c1 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3190,7 +3190,7 @@ static int get_nr_gens(struct lruvec *lruvec, int type) +@@ -3190,7 +3190,7 @@ static int get_nr_gens(struct lruvec *lr static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) { @@ -201,7 +195,7 @@ index d18296109aa7e..27142caf284c1 100644 int hist = lru_hist_from_seq(lrugen->min_seq[type]); pos->refaulted = lrugen->avg_refaulted[type][tier] + -@@ -3611,7 +3611,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain, +@@ -3611,7 +3611,7 @@ static void read_ctrl_pos(struct lruvec static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover) { int hist, tier; @@ -210,7 +204,7 @@ index d18296109aa7e..27142caf284c1 100644 bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1; unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1; -@@ -3688,7 +3688,7 @@ static int folio_update_gen(struct folio *folio, int gen) +@@ -3688,7 +3688,7 @@ static int folio_update_gen(struct folio static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclaiming) { int type = folio_is_file_lru(folio); @@ -219,7 +213,7 @@ index d18296109aa7e..27142caf284c1 100644 int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); unsigned long new_flags, old_flags = READ_ONCE(folio->flags); -@@ -3733,7 +3733,7 @@ static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio, +@@ -3733,7 +3733,7 @@ static void update_batch_size(struct lru static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk) { int gen, type, zone; @@ -228,7 +222,7 @@ index d18296109aa7e..27142caf284c1 100644 walk->batched = 0; -@@ -4250,7 +4250,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) +@@ -4250,7 +4250,7 @@ static bool inc_min_seq(struct lruvec *l { int zone; int remaining = MAX_LRU_BATCH; @@ -237,7 +231,7 @@ index d18296109aa7e..27142caf284c1 100644 int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); if (type == LRU_GEN_ANON && !can_swap) -@@ -4286,7 +4286,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) +@@ -4286,7 +4286,7 @@ static bool try_to_inc_min_seq(struct lr { int gen, type, zone; bool success = false; @@ -246,7 +240,7 @@ index d18296109aa7e..27142caf284c1 100644 DEFINE_MIN_SEQ(lruvec); VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); -@@ -4307,7 +4307,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) +@@ -4307,7 +4307,7 @@ next: ; } @@ -255,7 +249,7 @@ index d18296109aa7e..27142caf284c1 100644 if (can_swap) { min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]); min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]); -@@ -4329,7 +4329,7 @@ static void inc_max_seq(struct lruvec *lruvec, bool can_swap, bool force_scan) +@@ -4329,7 +4329,7 @@ static void inc_max_seq(struct lruvec *l { int prev, next; int type, zone; @@ -264,7 +258,7 @@ index d18296109aa7e..27142caf284c1 100644 spin_lock_irq(&lruvec->lru_lock); -@@ -4387,7 +4387,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4387,7 +4387,7 @@ static bool try_to_inc_max_seq(struct lr bool success; struct lru_gen_mm_walk *walk; struct mm_struct *mm = NULL; @@ -273,7 +267,7 @@ index d18296109aa7e..27142caf284c1 100644 VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq)); -@@ -4452,7 +4452,7 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsig +@@ -4452,7 +4452,7 @@ static bool should_run_aging(struct lruv unsigned long old = 0; unsigned long young = 0; unsigned long total = 0; @@ -282,7 +276,7 @@ index d18296109aa7e..27142caf284c1 100644 struct mem_cgroup *memcg = lruvec_memcg(lruvec); for (type = !can_swap; type < ANON_AND_FILE; type++) { -@@ -4737,7 +4737,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, int tier_idx) +@@ -4737,7 +4737,7 @@ static bool sort_folio(struct lruvec *lr int delta = folio_nr_pages(folio); int refs = folio_lru_refs(folio); int tier = lru_tier_from_refs(refs); @@ -291,7 +285,7 @@ index d18296109aa7e..27142caf284c1 100644 VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio); -@@ -4837,7 +4837,7 @@ static int scan_folios(struct lruvec *lruvec, struct scan_control *sc, +@@ -4837,7 +4837,7 @@ static int scan_folios(struct lruvec *lr int scanned = 0; int isolated = 0; int remaining = MAX_LRU_BATCH; @@ -300,7 +294,7 @@ index d18296109aa7e..27142caf284c1 100644 struct mem_cgroup *memcg = lruvec_memcg(lruvec); VM_WARN_ON_ONCE(!list_empty(list)); -@@ -5237,7 +5237,7 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5237,7 +5237,7 @@ done: static bool __maybe_unused state_is_valid(struct lruvec *lruvec) { @@ -309,7 +303,7 @@ index d18296109aa7e..27142caf284c1 100644 if (lrugen->enabled) { enum lru_list lru; -@@ -5519,7 +5519,7 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, +@@ -5519,7 +5519,7 @@ static void lru_gen_seq_show_full(struct int i; int type, tier; int hist = lru_hist_from_seq(seq); @@ -318,7 +312,7 @@ index d18296109aa7e..27142caf284c1 100644 for (tier = 0; tier < MAX_NR_TIERS; tier++) { seq_printf(m, " %10d", tier); -@@ -5569,7 +5569,7 @@ static int lru_gen_seq_show(struct seq_file *m, void *v) +@@ -5569,7 +5569,7 @@ static int lru_gen_seq_show(struct seq_f unsigned long seq; bool full = !debugfs_real_fops(m->file)->write; struct lruvec *lruvec = v; @@ -327,7 +321,7 @@ index d18296109aa7e..27142caf284c1 100644 int nid = lruvec_pgdat(lruvec)->node_id; struct mem_cgroup *memcg = lruvec_memcg(lruvec); DEFINE_MAX_SEQ(lruvec); -@@ -5823,7 +5823,7 @@ void lru_gen_init_lruvec(struct lruvec *lruvec) +@@ -5823,7 +5823,7 @@ void lru_gen_init_lruvec(struct lruvec * { int i; int gen, type, zone; @@ -336,11 +330,9 @@ index d18296109aa7e..27142caf284c1 100644 lrugen->max_seq = MIN_NR_GENS + 1; lrugen->enabled = lru_gen_enabled(); -diff --git a/mm/workingset.c b/mm/workingset.c -index ae7e984b23c6b..688aaa73f64e8 100644 --- a/mm/workingset.c +++ b/mm/workingset.c -@@ -223,7 +223,7 @@ static void *lru_gen_eviction(struct folio *folio) +@@ -223,7 +223,7 @@ static void *lru_gen_eviction(struct fol unsigned long token; unsigned long min_seq; struct lruvec *lruvec; @@ -349,7 +341,7 @@ index ae7e984b23c6b..688aaa73f64e8 100644 int type = folio_is_file_lru(folio); int delta = folio_nr_pages(folio); int refs = folio_lru_refs(folio); -@@ -252,7 +252,7 @@ static void lru_gen_refault(struct folio *folio, void *shadow) +@@ -252,7 +252,7 @@ static void lru_gen_refault(struct folio unsigned long token; unsigned long min_seq; struct lruvec *lruvec; @@ -358,6 +350,3 @@ index ae7e984b23c6b..688aaa73f64e8 100644 struct mem_cgroup *memcg; struct pglist_data *pgdat; int type = folio_is_file_lru(folio); --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-02-UPSTREAM-mm-multi-gen-LRU-rename-lrugen-lists-to-lru.patch b/target/linux/generic/backport-6.1/020-v6.3-02-UPSTREAM-mm-multi-gen-LRU-rename-lrugen-lists-to-lru.patch index 65fb0144b1e..97ea33c528b 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-02-UPSTREAM-mm-multi-gen-LRU-rename-lrugen-lists-to-lru.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-02-UPSTREAM-mm-multi-gen-LRU-rename-lrugen-lists-to-lru.patch @@ -28,8 +28,6 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) -diff --git a/Documentation/mm/multigen_lru.rst b/Documentation/mm/multigen_lru.rst -index d7062c6a89464..d8f721f98868a 100644 --- a/Documentation/mm/multigen_lru.rst +++ b/Documentation/mm/multigen_lru.rst @@ -89,15 +89,15 @@ variables are monotonically increasing. @@ -51,7 +49,7 @@ index d7062c6a89464..d8f721f98868a 100644 contrast to moving across generations, which requires the LRU lock, moving across tiers only involves atomic operations on ``folio->flags`` and therefore has a negligible cost. A feedback loop -@@ -127,7 +127,7 @@ page mapped by this PTE to ``(max_seq%MAX_NR_GENS)+1``. +@@ -127,7 +127,7 @@ page mapped by this PTE to ``(max_seq%MA Eviction -------- The eviction consumes old generations. Given an ``lruvec``, it @@ -60,11 +58,9 @@ index d7062c6a89464..d8f721f98868a 100644 ``min_seq%MAX_NR_GENS`` becomes empty. To select a type and a tier to evict from, it first compares ``min_seq[]`` to select the older type. If both types are equally old, it selects the one whose first tier has -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index f63968bd7de59..da38e3d962e2f 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -256,9 +256,9 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, +@@ -256,9 +256,9 @@ static inline bool lru_gen_add_folio(str lru_gen_update_size(lruvec, folio, -1, gen); /* for folio_rotate_reclaimable() */ if (reclaiming) @@ -76,8 +72,6 @@ index f63968bd7de59..da38e3d962e2f 100644 return true; } -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index bd3e4689f72dc..02e4323744715 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -312,7 +312,7 @@ enum lruvec_flags { @@ -109,11 +103,9 @@ index bd3e4689f72dc..02e4323744715 100644 /* the multi-gen LRU sizes, eventually consistent */ long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; /* the exponential moving average of refaulted */ -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 27142caf284c1..b02fed912f742 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4258,7 +4258,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) +@@ -4258,7 +4258,7 @@ static bool inc_min_seq(struct lruvec *l /* prevent cold/hot inversion if force_scan is true */ for (zone = 0; zone < MAX_NR_ZONES; zone++) { @@ -122,7 +114,7 @@ index 27142caf284c1..b02fed912f742 100644 while (!list_empty(head)) { struct folio *folio = lru_to_folio(head); -@@ -4269,7 +4269,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) +@@ -4269,7 +4269,7 @@ static bool inc_min_seq(struct lruvec *l VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone, folio); new_gen = folio_inc_gen(lruvec, folio, false); @@ -131,7 +123,7 @@ index 27142caf284c1..b02fed912f742 100644 if (!--remaining) return false; -@@ -4297,7 +4297,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) +@@ -4297,7 +4297,7 @@ static bool try_to_inc_min_seq(struct lr gen = lru_gen_from_seq(min_seq[type]); for (zone = 0; zone < MAX_NR_ZONES; zone++) { @@ -140,7 +132,7 @@ index 27142caf284c1..b02fed912f742 100644 goto next; } -@@ -4762,7 +4762,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, int tier_idx) +@@ -4762,7 +4762,7 @@ static bool sort_folio(struct lruvec *lr /* promoted */ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { @@ -149,7 +141,7 @@ index 27142caf284c1..b02fed912f742 100644 return true; } -@@ -4771,7 +4771,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, int tier_idx) +@@ -4771,7 +4771,7 @@ static bool sort_folio(struct lruvec *lr int hist = lru_hist_from_seq(lrugen->min_seq[type]); gen = folio_inc_gen(lruvec, folio, false); @@ -158,7 +150,7 @@ index 27142caf284c1..b02fed912f742 100644 WRITE_ONCE(lrugen->protected[hist][type][tier - 1], lrugen->protected[hist][type][tier - 1] + delta); -@@ -4783,7 +4783,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, int tier_idx) +@@ -4783,7 +4783,7 @@ static bool sort_folio(struct lruvec *lr if (folio_test_locked(folio) || folio_test_writeback(folio) || (type == LRU_GEN_FILE && folio_test_dirty(folio))) { gen = folio_inc_gen(lruvec, folio, true); @@ -167,7 +159,7 @@ index 27142caf284c1..b02fed912f742 100644 return true; } -@@ -4850,7 +4850,7 @@ static int scan_folios(struct lruvec *lruvec, struct scan_control *sc, +@@ -4850,7 +4850,7 @@ static int scan_folios(struct lruvec *lr for (zone = sc->reclaim_idx; zone >= 0; zone--) { LIST_HEAD(moved); int skipped = 0; @@ -176,7 +168,7 @@ index 27142caf284c1..b02fed912f742 100644 while (!list_empty(head)) { struct folio *folio = lru_to_folio(head); -@@ -5250,7 +5250,7 @@ static bool __maybe_unused state_is_valid(struct lruvec *lruvec) +@@ -5250,7 +5250,7 @@ static bool __maybe_unused state_is_vali int gen, type, zone; for_each_gen_type_zone(gen, type, zone) { @@ -185,7 +177,7 @@ index 27142caf284c1..b02fed912f742 100644 return false; } } -@@ -5295,7 +5295,7 @@ static bool drain_evictable(struct lruvec *lruvec) +@@ -5295,7 +5295,7 @@ static bool drain_evictable(struct lruve int remaining = MAX_LRU_BATCH; for_each_gen_type_zone(gen, type, zone) { @@ -194,7 +186,7 @@ index 27142caf284c1..b02fed912f742 100644 while (!list_empty(head)) { bool success; -@@ -5832,7 +5832,7 @@ void lru_gen_init_lruvec(struct lruvec *lruvec) +@@ -5832,7 +5832,7 @@ void lru_gen_init_lruvec(struct lruvec * lrugen->timestamps[i] = jiffies; for_each_gen_type_zone(gen, type, zone) @@ -203,6 +195,3 @@ index 27142caf284c1..b02fed912f742 100644 lruvec->mm_state.seq = MIN_NR_GENS; init_waitqueue_head(&lruvec->mm_state.wait); --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch b/target/linux/generic/backport-6.1/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch index 6d764bb2b44..a5e4ad557f8 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch @@ -40,11 +40,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 81 +++++++++++++++-------------------------------------- 1 file changed, 23 insertions(+), 58 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index b02fed912f742..991961180b320 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -448,6 +448,11 @@ static bool cgroup_reclaim(struct scan_control *sc) +@@ -448,6 +448,11 @@ static bool cgroup_reclaim(struct scan_c return sc->target_mem_cgroup; } @@ -56,7 +54,7 @@ index b02fed912f742..991961180b320 100644 /** * writeback_throttling_sane - is the usual dirty throttling mechanism available? * @sc: scan_control in question -@@ -498,6 +503,11 @@ static bool cgroup_reclaim(struct scan_control *sc) +@@ -498,6 +503,11 @@ static bool cgroup_reclaim(struct scan_c return false; } @@ -68,7 +66,7 @@ index b02fed912f742..991961180b320 100644 static bool writeback_throttling_sane(struct scan_control *sc) { return true; -@@ -4993,8 +5003,7 @@ static int isolate_folios(struct lruvec *lruvec, struct scan_control *sc, int sw +@@ -4993,8 +5003,7 @@ static int isolate_folios(struct lruvec return scanned; } @@ -78,7 +76,7 @@ index b02fed912f742..991961180b320 100644 { int type; int scanned; -@@ -5083,9 +5092,6 @@ static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swap +@@ -5083,9 +5092,6 @@ retry: goto retry; } @@ -88,7 +86,7 @@ index b02fed912f742..991961180b320 100644 return scanned; } -@@ -5124,67 +5130,26 @@ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control * +@@ -5124,67 +5130,26 @@ done: return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0; } @@ -165,7 +163,7 @@ index b02fed912f742..991961180b320 100644 lru_add_drain(); -@@ -5208,7 +5173,7 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5208,7 +5173,7 @@ static void lru_gen_shrink_lruvec(struct if (!nr_to_scan) goto done; @@ -174,7 +172,7 @@ index b02fed912f742..991961180b320 100644 if (!delta) goto done; -@@ -5216,7 +5181,7 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5216,7 +5181,7 @@ static void lru_gen_shrink_lruvec(struct if (scanned >= nr_to_scan) break; @@ -183,7 +181,7 @@ index b02fed912f742..991961180b320 100644 break; cond_resched(); -@@ -5666,7 +5631,7 @@ static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_co +@@ -5666,7 +5631,7 @@ static int run_eviction(struct lruvec *l if (sc->nr_reclaimed >= nr_to_reclaim) return 0; @@ -192,6 +190,3 @@ index b02fed912f742..991961180b320 100644 return 0; cond_resched(); --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch b/target/linux/generic/backport-6.1/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch index c3f534a9e0c..87ed87a52d0 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch @@ -42,8 +42,6 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 126 ++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 67 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 991961180b320..5a2e83e673232 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -136,7 +136,6 @@ struct scan_control { @@ -54,7 +52,7 @@ index 991961180b320..5a2e83e673232 100644 unsigned long last_reclaimed; #endif -@@ -4455,7 +4454,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4455,7 +4454,7 @@ done: return true; } @@ -63,7 +61,7 @@ index 991961180b320..5a2e83e673232 100644 struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan) { int gen, type, zone; -@@ -4464,6 +4463,13 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsig +@@ -4464,6 +4463,13 @@ static bool should_run_aging(struct lruv unsigned long total = 0; struct lru_gen_folio *lrugen = &lruvec->lrugen; struct mem_cgroup *memcg = lruvec_memcg(lruvec); @@ -77,7 +75,7 @@ index 991961180b320..5a2e83e673232 100644 for (type = !can_swap; type < ANON_AND_FILE; type++) { unsigned long seq; -@@ -4492,8 +4498,6 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsig +@@ -4492,8 +4498,6 @@ static bool should_run_aging(struct lruv * stalls when the number of generations reaches MIN_NR_GENS. Hence, the * ideal number of generations is MIN_NR_GENS+1. */ @@ -86,7 +84,7 @@ index 991961180b320..5a2e83e673232 100644 if (min_seq[!can_swap] + MIN_NR_GENS < max_seq) return false; -@@ -4512,40 +4516,54 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsig +@@ -4512,40 +4516,54 @@ static bool should_run_aging(struct lruv return false; } @@ -162,7 +160,7 @@ index 991961180b320..5a2e83e673232 100644 } /* to protect the working set of the last N jiffies */ -@@ -4554,46 +4572,32 @@ static unsigned long lru_gen_min_ttl __read_mostly; +@@ -4554,46 +4572,32 @@ static unsigned long lru_gen_min_ttl __r static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) { struct mem_cgroup *memcg; @@ -216,7 +214,7 @@ index 991961180b320..5a2e83e673232 100644 */ if (mutex_trylock(&oom_lock)) { struct oom_control oc = { -@@ -5101,33 +5105,27 @@ static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swap +@@ -5101,33 +5105,27 @@ retry: * reclaim. */ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, @@ -256,7 +254,7 @@ index 991961180b320..5a2e83e673232 100644 } static unsigned long get_nr_to_reclaim(struct scan_control *sc) -@@ -5146,9 +5144,7 @@ static unsigned long get_nr_to_reclaim(struct scan_control *sc) +@@ -5146,9 +5144,7 @@ static unsigned long get_nr_to_reclaim(s static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) { struct blk_plug plug; @@ -266,7 +264,7 @@ index 991961180b320..5a2e83e673232 100644 unsigned long nr_to_reclaim = get_nr_to_reclaim(sc); lru_add_drain(); -@@ -5169,13 +5165,13 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5169,13 +5165,13 @@ static void lru_gen_shrink_lruvec(struct else swappiness = 0; @@ -283,7 +281,7 @@ index 991961180b320..5a2e83e673232 100644 scanned += delta; if (scanned >= nr_to_scan) -@@ -5187,10 +5183,6 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5187,10 +5183,6 @@ static void lru_gen_shrink_lruvec(struct cond_resched(); } @@ -294,6 +292,3 @@ index 991961180b320..5a2e83e673232 100644 clear_mm_walk(); blk_finish_plug(&plug); --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch b/target/linux/generic/backport-6.1/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch index d6bfe2a91c2..857072296bf 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch @@ -24,11 +24,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 124 ++++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 62 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 5a2e83e673232..0c47952714b26 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4454,68 +4454,6 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4454,68 +4454,6 @@ done: return true; } @@ -97,7 +95,7 @@ index 5a2e83e673232..0c47952714b26 100644 static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc) { int gen, type, zone; -@@ -5099,6 +5037,68 @@ static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swap +@@ -5099,6 +5037,68 @@ retry: return scanned; } @@ -166,6 +164,3 @@ index 5a2e83e673232..0c47952714b26 100644 /* * For future optimizations: * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch b/target/linux/generic/backport-6.1/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch index 14f28820ec0..7a4d175128d 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch @@ -74,11 +74,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 374 +++++++++++++++++++++++++++++++++---- 6 files changed, 500 insertions(+), 35 deletions(-) -diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h -index e039763029563..82d28b052a9e5 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h -@@ -790,6 +790,11 @@ static inline void obj_cgroup_put(struct obj_cgroup *objcg) +@@ -790,6 +790,11 @@ static inline void obj_cgroup_put(struct percpu_ref_put(&objcg->refcnt); } @@ -90,7 +88,7 @@ index e039763029563..82d28b052a9e5 100644 static inline void mem_cgroup_put(struct mem_cgroup *memcg) { if (memcg) -@@ -1290,6 +1295,11 @@ static inline void obj_cgroup_put(struct obj_cgroup *objcg) +@@ -1290,6 +1295,11 @@ static inline void obj_cgroup_put(struct { } @@ -102,11 +100,9 @@ index e039763029563..82d28b052a9e5 100644 static inline void mem_cgroup_put(struct mem_cgroup *memcg) { } -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index da38e3d962e2f..c1fd3922dc5dd 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -122,6 +122,18 @@ static inline bool lru_gen_in_fault(void) +@@ -122,6 +122,18 @@ static inline bool lru_gen_in_fault(void return current->in_lru_fault; } @@ -125,7 +121,7 @@ index da38e3d962e2f..c1fd3922dc5dd 100644 static inline int lru_gen_from_seq(unsigned long seq) { return seq % MAX_NR_GENS; -@@ -297,6 +309,11 @@ static inline bool lru_gen_in_fault(void) +@@ -297,6 +309,11 @@ static inline bool lru_gen_in_fault(void return false; } @@ -137,8 +133,6 @@ index da38e3d962e2f..c1fd3922dc5dd 100644 static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming) { return false; -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index 02e4323744715..66e067a635682 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -7,6 +7,7 @@ @@ -180,7 +174,7 @@ index 02e4323744715..66e067a635682 100644 }; enum { -@@ -479,12 +497,87 @@ void lru_gen_init_lruvec(struct lruvec *lruvec); +@@ -479,12 +497,87 @@ void lru_gen_init_lruvec(struct lruvec * void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); #ifdef CONFIG_MEMCG @@ -269,7 +263,7 @@ index 02e4323744715..66e067a635682 100644 static inline void lru_gen_init_lruvec(struct lruvec *lruvec) { } -@@ -494,6 +587,7 @@ static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +@@ -494,6 +587,7 @@ static inline void lru_gen_look_around(s } #ifdef CONFIG_MEMCG @@ -277,7 +271,7 @@ index 02e4323744715..66e067a635682 100644 static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) { } -@@ -501,7 +595,24 @@ static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) +@@ -501,7 +595,24 @@ static inline void lru_gen_init_memcg(st static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg) { } @@ -312,11 +306,9 @@ index 02e4323744715..66e067a635682 100644 #endif CACHELINE_PADDING(_pad2_); -diff --git a/mm/memcontrol.c b/mm/memcontrol.c -index 3e8f1ad0fe9db..7815d556e38cc 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c -@@ -477,6 +477,16 @@ static void mem_cgroup_update_tree(struct mem_cgroup *memcg, int nid) +@@ -477,6 +477,16 @@ static void mem_cgroup_update_tree(struc struct mem_cgroup_per_node *mz; struct mem_cgroup_tree_per_node *mctz; @@ -333,7 +325,7 @@ index 3e8f1ad0fe9db..7815d556e38cc 100644 mctz = soft_limit_tree.rb_tree_per_node[nid]; if (!mctz) return; -@@ -3522,6 +3532,9 @@ unsigned long mem_cgroup_soft_limit_reclaim(pg_data_t *pgdat, int order, +@@ -3522,6 +3532,9 @@ unsigned long mem_cgroup_soft_limit_recl struct mem_cgroup_tree_per_node *mctz; unsigned long excess; @@ -343,7 +335,7 @@ index 3e8f1ad0fe9db..7815d556e38cc 100644 if (order > 0) return 0; -@@ -5382,6 +5395,7 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) +@@ -5382,6 +5395,7 @@ static int mem_cgroup_css_online(struct if (unlikely(mem_cgroup_is_root(memcg))) queue_delayed_work(system_unbound_wq, &stats_flush_dwork, 2UL*HZ); @@ -351,7 +343,7 @@ index 3e8f1ad0fe9db..7815d556e38cc 100644 return 0; offline_kmem: memcg_offline_kmem(memcg); -@@ -5413,6 +5427,7 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) +@@ -5413,6 +5427,7 @@ static void mem_cgroup_css_offline(struc memcg_offline_kmem(memcg); reparent_shrinker_deferred(memcg); wb_memcg_offline(memcg); @@ -359,7 +351,7 @@ index 3e8f1ad0fe9db..7815d556e38cc 100644 drain_all_stock(memcg); -@@ -5424,6 +5439,7 @@ static void mem_cgroup_css_released(struct cgroup_subsys_state *css) +@@ -5424,6 +5439,7 @@ static void mem_cgroup_css_released(stru struct mem_cgroup *memcg = mem_cgroup_from_css(css); invalidate_reclaim_iterators(memcg); @@ -367,11 +359,9 @@ index 3e8f1ad0fe9db..7815d556e38cc 100644 } static void mem_cgroup_css_free(struct cgroup_subsys_state *css) -diff --git a/mm/page_alloc.c b/mm/page_alloc.c -index 69668817fed37..473057b81a9df 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c -@@ -7957,6 +7957,7 @@ static void __init free_area_init_node(int nid) +@@ -7957,6 +7957,7 @@ static void __init free_area_init_node(i pgdat_set_deferred_range(pgdat); free_area_init_core(pgdat); @@ -379,8 +369,6 @@ index 69668817fed37..473057b81a9df 100644 } static void __init free_area_init_memoryless_node(int nid) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 0c47952714b26..65eb28448f216 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -54,6 +54,8 @@ @@ -404,7 +392,7 @@ index 0c47952714b26..65eb28448f216 100644 /* Allocation order */ s8 order; -@@ -3160,6 +3157,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS); +@@ -3160,6 +3157,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_ca for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++) @@ -414,7 +402,7 @@ index 0c47952714b26..65eb28448f216 100644 static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid) { struct pglist_data *pgdat = NODE_DATA(nid); -@@ -4440,8 +4440,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4440,8 +4440,7 @@ done: if (sc->priority <= DEF_PRIORITY - 2) wait_event_killable(lruvec->mm_state.wait, max_seq < READ_ONCE(lrugen->max_seq)); @@ -424,7 +412,7 @@ index 0c47952714b26..65eb28448f216 100644 } VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq)); -@@ -4514,8 +4513,6 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) +@@ -4514,8 +4513,6 @@ static void lru_gen_age_node(struct pgli VM_WARN_ON_ONCE(!current_is_kswapd()); @@ -433,7 +421,7 @@ index 0c47952714b26..65eb28448f216 100644 /* check the order to exclude compaction-induced reclaim */ if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY) return; -@@ -5104,8 +5101,7 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, +@@ -5104,8 +5101,7 @@ static bool should_run_aging(struct lruv * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg * reclaim. */ @@ -443,7 +431,7 @@ index 0c47952714b26..65eb28448f216 100644 { unsigned long nr_to_scan; struct mem_cgroup *memcg = lruvec_memcg(lruvec); -@@ -5122,10 +5118,8 @@ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control * +@@ -5122,10 +5118,8 @@ static unsigned long get_nr_to_scan(stru if (sc->priority == DEF_PRIORITY) return nr_to_scan; @@ -455,7 +443,7 @@ index 0c47952714b26..65eb28448f216 100644 } static unsigned long get_nr_to_reclaim(struct scan_control *sc) -@@ -5134,29 +5128,18 @@ static unsigned long get_nr_to_reclaim(struct scan_control *sc) +@@ -5134,29 +5128,18 @@ static unsigned long get_nr_to_reclaim(s if (!global_reclaim(sc)) return -1; @@ -487,7 +475,7 @@ index 0c47952714b26..65eb28448f216 100644 if (sc->may_swap) swappiness = get_swappiness(lruvec, sc); -@@ -5166,7 +5149,7 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5166,7 +5149,7 @@ static void lru_gen_shrink_lruvec(struct swappiness = 0; nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); @@ -496,7 +484,7 @@ index 0c47952714b26..65eb28448f216 100644 break; delta = evict_folios(lruvec, sc, swappiness); -@@ -5183,11 +5166,252 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5183,10 +5166,251 @@ static void lru_gen_shrink_lruvec(struct cond_resched(); } @@ -621,11 +609,11 @@ index 0c47952714b26..65eb28448f216 100644 + if (try_to_shrink_lruvec(lruvec, sc)) + lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG); + - clear_mm_walk(); - - blk_finish_plug(&plug); - } - ++ clear_mm_walk(); ++ ++ blk_finish_plug(&plug); ++} ++ +#else /* !CONFIG_MEMCG */ + +static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc) @@ -691,9 +679,9 @@ index 0c47952714b26..65eb28448f216 100644 + if (current_is_kswapd()) + sc->nr_reclaimed += reclaimed; + -+ clear_mm_walk(); -+ -+ blk_finish_plug(&plug); + clear_mm_walk(); + + blk_finish_plug(&plug); + + /* kswapd should never fail */ + pgdat->kswapd_failures = 0; @@ -743,13 +731,12 @@ index 0c47952714b26..65eb28448f216 100644 + WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1); + + spin_unlock(&pgdat->memcg_lru.lock); -+} + } +#endif -+ + /****************************************************************************** * state change - ******************************************************************************/ -@@ -5644,11 +5868,11 @@ static int run_cmd(char cmd, int memcg_id, int nid, unsigned long seq, +@@ -5644,11 +5868,11 @@ static int run_cmd(char cmd, int memcg_i if (!mem_cgroup_disabled()) { rcu_read_lock(); @@ -764,7 +751,7 @@ index 0c47952714b26..65eb28448f216 100644 rcu_read_unlock(); if (!memcg) -@@ -5796,6 +6020,19 @@ void lru_gen_init_lruvec(struct lruvec *lruvec) +@@ -5796,6 +6020,19 @@ void lru_gen_init_lruvec(struct lruvec * } #ifdef CONFIG_MEMCG @@ -784,7 +771,7 @@ index 0c47952714b26..65eb28448f216 100644 void lru_gen_init_memcg(struct mem_cgroup *memcg) { INIT_LIST_HEAD(&memcg->mm_list.fifo); -@@ -5819,7 +6056,69 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) +@@ -5819,7 +6056,69 @@ void lru_gen_exit_memcg(struct mem_cgrou } } } @@ -855,7 +842,7 @@ index 0c47952714b26..65eb28448f216 100644 static int __init init_lru_gen(void) { -@@ -5846,6 +6145,10 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5846,6 +6145,10 @@ static void lru_gen_shrink_lruvec(struct { } @@ -866,7 +853,7 @@ index 0c47952714b26..65eb28448f216 100644 #endif /* CONFIG_LRU_GEN */ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) -@@ -5859,7 +6162,7 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) +@@ -5859,7 +6162,7 @@ static void shrink_lruvec(struct lruvec bool proportional_reclaim; struct blk_plug plug; @@ -875,7 +862,7 @@ index 0c47952714b26..65eb28448f216 100644 lru_gen_shrink_lruvec(lruvec, sc); return; } -@@ -6102,6 +6405,11 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc) +@@ -6102,6 +6405,11 @@ static void shrink_node(pg_data_t *pgdat struct lruvec *target_lruvec; bool reclaimable = false; @@ -887,6 +874,3 @@ index 0c47952714b26..65eb28448f216 100644 target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); again: --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch b/target/linux/generic/backport-6.1/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch index 9aaf247a098..29430636ff4 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch @@ -39,11 +39,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 55 +++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 65eb28448f216..0a0e1250ffc87 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3185,6 +3185,9 @@ static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) +@@ -3185,6 +3185,9 @@ static int get_swappiness(struct lruvec struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); @@ -53,7 +51,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 if (!can_demote(pgdat->node_id, sc) && mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH) return 0; -@@ -4223,7 +4226,7 @@ static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct lru_gen_ +@@ -4223,7 +4226,7 @@ static void walk_mm(struct lruvec *lruve } while (err == -EAGAIN); } @@ -62,7 +60,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 { struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk; -@@ -4231,7 +4234,7 @@ static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat) +@@ -4231,7 +4234,7 @@ static struct lru_gen_mm_walk *set_mm_wa VM_WARN_ON_ONCE(walk); walk = &pgdat->mm_walk; @@ -71,7 +69,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 VM_WARN_ON_ONCE(current_is_kswapd()); walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN); -@@ -4417,7 +4420,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4417,7 +4420,7 @@ static bool try_to_inc_max_seq(struct lr goto done; } @@ -80,7 +78,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 if (!walk) { success = iterate_mm_list_nowalk(lruvec, max_seq); goto done; -@@ -4486,8 +4489,6 @@ static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc +@@ -4486,8 +4489,6 @@ static bool lruvec_is_reclaimable(struct struct mem_cgroup *memcg = lruvec_memcg(lruvec); DEFINE_MIN_SEQ(lruvec); @@ -89,7 +87,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 /* see the comment on lru_gen_folio */ gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]); birth = READ_ONCE(lruvec->lrugen.timestamps[gen]); -@@ -4743,12 +4744,8 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca +@@ -4743,12 +4744,8 @@ static bool isolate_folio(struct lruvec { bool success; @@ -103,7 +101,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 (folio_test_dirty(folio) || (folio_test_anon(folio) && !folio_test_swapcache(folio)))) return false; -@@ -4845,9 +4842,8 @@ static int scan_folios(struct lruvec *lruvec, struct scan_control *sc, +@@ -4845,9 +4842,8 @@ static int scan_folios(struct lruvec *lr __count_vm_events(PGSCAN_ANON + type, isolated); /* @@ -115,7 +113,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 */ return isolated || !remaining ? scanned : 0; } -@@ -5107,8 +5103,7 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, bool +@@ -5107,8 +5103,7 @@ static long get_nr_to_scan(struct lruvec struct mem_cgroup *memcg = lruvec_memcg(lruvec); DEFINE_MAX_SEQ(lruvec); @@ -125,7 +123,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 return 0; if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan)) -@@ -5136,17 +5131,14 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) +@@ -5136,17 +5131,14 @@ static bool try_to_shrink_lruvec(struct long nr_to_scan; unsigned long scanned = 0; unsigned long nr_to_reclaim = get_nr_to_reclaim(sc); @@ -148,7 +146,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); if (nr_to_scan <= 0) -@@ -5277,12 +5269,13 @@ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc +@@ -5277,12 +5269,13 @@ static void lru_gen_shrink_lruvec(struct struct blk_plug plug; VM_WARN_ON_ONCE(global_reclaim(sc)); @@ -163,7 +161,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 if (try_to_shrink_lruvec(lruvec, sc)) lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG); -@@ -5338,11 +5331,19 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control * +@@ -5338,11 +5331,19 @@ static void lru_gen_shrink_node(struct p VM_WARN_ON_ONCE(!global_reclaim(sc)); @@ -184,7 +182,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 set_initial_priority(pgdat, sc); -@@ -5360,7 +5361,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control * +@@ -5360,7 +5361,7 @@ static void lru_gen_shrink_node(struct p clear_mm_walk(); blk_finish_plug(&plug); @@ -193,7 +191,7 @@ index 65eb28448f216..0a0e1250ffc87 100644 /* kswapd should never fail */ pgdat->kswapd_failures = 0; } -@@ -5932,7 +5933,7 @@ static ssize_t lru_gen_seq_write(struct file *file, const char __user *src, +@@ -5932,7 +5933,7 @@ static ssize_t lru_gen_seq_write(struct set_task_reclaim_state(current, &sc.reclaim_state); flags = memalloc_noreclaim_save(); blk_start_plug(&plug); @@ -202,6 +200,3 @@ index 65eb28448f216..0a0e1250ffc87 100644 err = -ENOMEM; goto done; } --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch b/target/linux/generic/backport-6.1/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch index adf85b616f4..d7d98ca6c59 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch @@ -25,11 +25,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 0a0e1250ffc87..aa9746f2bc80b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4415,7 +4415,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4415,7 +4415,7 @@ static bool try_to_inc_max_seq(struct lr * handful of PTEs. Spreading the work out over a period of time usually * is less efficient, but it avoids bursty page faults. */ @@ -38,6 +36,3 @@ index 0a0e1250ffc87..aa9746f2bc80b 100644 success = iterate_mm_list_nowalk(lruvec, max_seq); goto done; } --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch b/target/linux/generic/backport-6.1/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch index 2cf14d78166..1f91cb9f54f 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch @@ -27,11 +27,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index aa9746f2bc80b..49da02f841c81 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -5206,18 +5206,20 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) +@@ -5206,18 +5206,20 @@ static int shrink_one(struct lruvec *lru static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc) { @@ -54,7 +52,7 @@ index aa9746f2bc80b..49da02f841c81 100644 gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq)); rcu_read_lock(); -@@ -5241,14 +5243,22 @@ static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc) +@@ -5241,14 +5243,22 @@ restart: op = shrink_one(lruvec, sc); @@ -80,7 +78,7 @@ index aa9746f2bc80b..49da02f841c81 100644 /* restart if raced with lru_gen_rotate_memcg() */ if (gen != get_nulls_value(pos)) goto restart; -@@ -5257,11 +5267,6 @@ static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc) +@@ -5257,11 +5267,6 @@ restart: bin = get_memcg_bin(bin + 1); if (bin != first_bin) goto restart; @@ -92,6 +90,3 @@ index aa9746f2bc80b..49da02f841c81 100644 } static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch b/target/linux/generic/backport-6.1/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch index 0a0b439e25f..b5051d71a47 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch @@ -60,11 +60,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 5 ++++- 4 files changed, 33 insertions(+), 29 deletions(-) -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index c1fd3922dc5dd..7bb2e5f94734c 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -595,4 +595,12 @@ pte_install_uffd_wp_if_needed(struct vm_area_struct *vma, unsigned long addr, +@@ -595,4 +595,12 @@ pte_install_uffd_wp_if_needed(struct vm_ #endif } @@ -77,11 +75,9 @@ index c1fd3922dc5dd..7bb2e5f94734c 100644 +} + #endif -diff --git a/mm/memory.c b/mm/memory.c -index 747b7ea30f890..c2f48f8003c2e 100644 --- a/mm/memory.c +++ b/mm/memory.c -@@ -1435,8 +1435,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, +@@ -1435,8 +1435,7 @@ again: force_flush = 1; set_page_dirty(page); } @@ -91,7 +87,7 @@ index 747b7ea30f890..c2f48f8003c2e 100644 mark_page_accessed(page); } rss[mm_counter(page)]--; -@@ -5170,8 +5169,8 @@ static inline void mm_account_fault(struct pt_regs *regs, +@@ -5170,8 +5169,8 @@ static inline void mm_account_fault(stru #ifdef CONFIG_LRU_GEN static void lru_gen_enter_fault(struct vm_area_struct *vma) { @@ -102,11 +98,9 @@ index 747b7ea30f890..c2f48f8003c2e 100644 } static void lru_gen_exit_fault(void) -diff --git a/mm/rmap.c b/mm/rmap.c -index 7da2d8d097d9b..825dac3caa1e5 100644 --- a/mm/rmap.c +++ b/mm/rmap.c -@@ -823,25 +823,14 @@ static bool folio_referenced_one(struct folio *folio, +@@ -823,25 +823,14 @@ static bool folio_referenced_one(struct } if (pvmw.pte) { @@ -135,7 +129,7 @@ index 7da2d8d097d9b..825dac3caa1e5 100644 } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { if (pmdp_clear_flush_young_notify(vma, address, pvmw.pmd)) -@@ -875,7 +864,20 @@ static bool invalid_folio_referenced_vma(struct vm_area_struct *vma, void *arg) +@@ -875,7 +864,20 @@ static bool invalid_folio_referenced_vma struct folio_referenced_arg *pra = arg; struct mem_cgroup *memcg = pra->memcg; @@ -157,7 +151,7 @@ index 7da2d8d097d9b..825dac3caa1e5 100644 return true; return false; -@@ -906,6 +908,7 @@ int folio_referenced(struct folio *folio, int is_locked, +@@ -906,6 +908,7 @@ int folio_referenced(struct folio *folio .arg = (void *)&pra, .anon_lock = folio_lock_anon_vma_read, .try_lock = true, @@ -165,7 +159,7 @@ index 7da2d8d097d9b..825dac3caa1e5 100644 }; *vm_flags = 0; -@@ -921,15 +924,6 @@ int folio_referenced(struct folio *folio, int is_locked, +@@ -921,15 +924,6 @@ int folio_referenced(struct folio *folio return 1; } @@ -181,11 +175,9 @@ index 7da2d8d097d9b..825dac3caa1e5 100644 rmap_walk(folio, &rwc); *vm_flags = pra.vm_flags; -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 49da02f841c81..596fed6ae0439 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3778,7 +3778,10 @@ static int should_skip_vma(unsigned long start, unsigned long end, struct mm_wal +@@ -3778,7 +3778,10 @@ static int should_skip_vma(unsigned long if (is_vm_hugetlb_page(vma)) return true; @@ -197,6 +189,3 @@ index 49da02f841c81..596fed6ae0439 100644 return true; if (vma == get_gate_vma(vma->vm_mm)) --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch b/target/linux/generic/backport-6.1/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch index 4c6fcecdac1..00e5b6e8d51 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch @@ -83,11 +83,9 @@ Signed-off-by: T.J. Mercier mm/fadvise.c | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) -diff --git a/include/linux/fs.h b/include/linux/fs.h -index f14ecbeab2a9d..97f9c41c1a43a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -166,6 +166,8 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, +@@ -166,6 +166,8 @@ typedef int (dio_iodone_t)(struct kiocb /* File supports DIRECT IO */ #define FMODE_CAN_ODIRECT ((__force fmode_t)0x400000) @@ -96,11 +94,9 @@ index f14ecbeab2a9d..97f9c41c1a43a 100644 /* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000) -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index 7bb2e5f94734c..9a8e2049333c0 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -600,6 +600,9 @@ static inline bool vma_has_recency(struct vm_area_struct *vma) +@@ -600,6 +600,9 @@ static inline bool vma_has_recency(struc if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ)) return false; @@ -110,11 +106,9 @@ index 7bb2e5f94734c..9a8e2049333c0 100644 return true; } -diff --git a/mm/fadvise.c b/mm/fadvise.c -index c76ee665355a4..2ba24d865bf5f 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c -@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, loff_t offset, loff_t len, int advice) +@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, l case POSIX_FADV_NORMAL: file->f_ra.ra_pages = bdi->ra_pages; spin_lock(&file->f_lock); @@ -123,7 +117,7 @@ index c76ee665355a4..2ba24d865bf5f 100644 spin_unlock(&file->f_lock); break; case POSIX_FADV_RANDOM: -@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, loff_t offset, loff_t len, int advice) +@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, l force_page_cache_readahead(mapping, file, start_index, nrpages); break; case POSIX_FADV_NOREUSE: @@ -133,6 +127,3 @@ index c76ee665355a4..2ba24d865bf5f 100644 break; case POSIX_FADV_DONTNEED: __filemap_fdatawrite_range(mapping, offset, endbyte, --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch b/target/linux/generic/backport-6.1/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch index 08804e00235..ca68b3a86aa 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch @@ -28,11 +28,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 4 ++++ 2 files changed, 19 insertions(+) -diff --git a/Documentation/mm/multigen_lru.rst b/Documentation/mm/multigen_lru.rst -index d8f721f98868a..6e1483e70fdca 100644 --- a/Documentation/mm/multigen_lru.rst +++ b/Documentation/mm/multigen_lru.rst -@@ -141,6 +141,21 @@ loop has detected outlying refaults from the tier this page is in. To +@@ -141,6 +141,21 @@ loop has detected outlying refaults from this end, the feedback loop uses the first tier as the baseline, for the reason stated earlier. @@ -54,11 +52,9 @@ index d8f721f98868a..6e1483e70fdca 100644 Summary ------- The multi-gen LRU can be disassembled into the following parts: -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 596fed6ae0439..ab0b8d3b9d88f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4459,6 +4459,10 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4459,6 +4459,10 @@ done: return true; } @@ -69,6 +65,3 @@ index 596fed6ae0439..ab0b8d3b9d88f 100644 static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc) { int gen, type, zone; --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch b/target/linux/generic/backport-6.1/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch index f80dedb4057..ca28cee2cb3 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch @@ -19,11 +19,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 4 ++++ 2 files changed, 18 insertions(+) -diff --git a/Documentation/mm/multigen_lru.rst b/Documentation/mm/multigen_lru.rst -index 6e1483e70fdca..bd988a142bc2f 100644 --- a/Documentation/mm/multigen_lru.rst +++ b/Documentation/mm/multigen_lru.rst -@@ -156,6 +156,20 @@ This time-based approach has the following advantages: +@@ -156,6 +156,20 @@ This time-based approach has the followi and memory sizes. 2. It is more reliable because it is directly wired to the OOM killer. @@ -44,11 +42,9 @@ index 6e1483e70fdca..bd988a142bc2f 100644 Summary ------- The multi-gen LRU can be disassembled into the following parts: -diff --git a/mm/vmscan.c b/mm/vmscan.c -index ab0b8d3b9d88f..8fa82630240d6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4553,6 +4553,10 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) +@@ -4553,6 +4553,10 @@ static void lru_gen_age_node(struct pgli } } @@ -59,6 +55,3 @@ index ab0b8d3b9d88f..8fa82630240d6 100644 /* * This function exploits spatial locality when shrink_folio_list() walks the * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch b/target/linux/generic/backport-6.1/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch index f98588c4d89..a7dfb5ffe79 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch @@ -20,11 +20,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 180 +++++++++++++++--------------- 2 files changed, 108 insertions(+), 88 deletions(-) -diff --git a/Documentation/mm/multigen_lru.rst b/Documentation/mm/multigen_lru.rst -index bd988a142bc2f..770b5d539856c 100644 --- a/Documentation/mm/multigen_lru.rst +++ b/Documentation/mm/multigen_lru.rst -@@ -170,6 +170,22 @@ promotes hot pages. If the scan was done cacheline efficiently, it +@@ -170,6 +170,22 @@ promotes hot pages. If the scan was done adds the PMD entry pointing to the PTE table to the Bloom filter. This forms a feedback loop between the eviction and the aging. @@ -47,15 +45,12 @@ index bd988a142bc2f..770b5d539856c 100644 Summary ------- The multi-gen LRU can be disassembled into the following parts: -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 8fa82630240d6..74b4f9d660b56 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3208,6 +3208,98 @@ static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) - get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS; +@@ -3209,6 +3209,98 @@ static bool __maybe_unused seq_is_valid( } -+/****************************************************************************** + /****************************************************************************** + * Bloom filters + ******************************************************************************/ + @@ -147,10 +142,11 @@ index 8fa82630240d6..74b4f9d660b56 100644 + WRITE_ONCE(lruvec->mm_state.filters[gen], filter); +} + - /****************************************************************************** ++/****************************************************************************** * mm_struct list ******************************************************************************/ -@@ -3333,94 +3425,6 @@ void lru_gen_migrate_mm(struct mm_struct *mm) + +@@ -3333,94 +3425,6 @@ void lru_gen_migrate_mm(struct mm_struct } #endif @@ -245,6 +241,3 @@ index 8fa82630240d6..74b4f9d660b56 100644 static void reset_mm_stats(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, bool last) { int i; --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch b/target/linux/generic/backport-6.1/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch index c26e28c3cad..735c91f7998 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch @@ -22,11 +22,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 250 +++++++++++++++++------------- 5 files changed, 178 insertions(+), 143 deletions(-) -diff --git a/Documentation/mm/multigen_lru.rst b/Documentation/mm/multigen_lru.rst -index 770b5d539856c..5f1f6ecbb79b9 100644 --- a/Documentation/mm/multigen_lru.rst +++ b/Documentation/mm/multigen_lru.rst -@@ -186,9 +186,40 @@ is false positive, the cost is an additional scan of a range of PTEs, +@@ -186,9 +186,40 @@ is false positive, the cost is an additi which may yield hot pages anyway. Parameters of the filter itself can control the false positive rate in the limit. @@ -68,11 +66,9 @@ index 770b5d539856c..5f1f6ecbb79b9 100644 * Generations * Rmap walks -diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h -index 9a8e2049333c0..5567f4850243b 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h -@@ -122,18 +122,6 @@ static inline bool lru_gen_in_fault(void) +@@ -122,18 +122,6 @@ static inline bool lru_gen_in_fault(void return current->in_lru_fault; } @@ -91,7 +87,7 @@ index 9a8e2049333c0..5567f4850243b 100644 static inline int lru_gen_from_seq(unsigned long seq) { return seq % MAX_NR_GENS; -@@ -309,11 +297,6 @@ static inline bool lru_gen_in_fault(void) +@@ -309,11 +297,6 @@ static inline bool lru_gen_in_fault(void return false; } @@ -103,8 +99,6 @@ index 9a8e2049333c0..5567f4850243b 100644 static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming) { return false; -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index 66e067a635682..403c7461e7a70 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -368,15 +368,6 @@ struct page_vma_mapped_walk; @@ -123,7 +117,7 @@ index 66e067a635682..403c7461e7a70 100644 #ifdef CONFIG_LRU_GEN enum { -@@ -557,7 +548,7 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg); +@@ -557,7 +548,7 @@ void lru_gen_exit_memcg(struct mem_cgrou void lru_gen_online_memcg(struct mem_cgroup *memcg); void lru_gen_offline_memcg(struct mem_cgroup *memcg); void lru_gen_release_memcg(struct mem_cgroup *memcg); @@ -132,7 +126,7 @@ index 66e067a635682..403c7461e7a70 100644 #else /* !CONFIG_MEMCG */ -@@ -608,7 +599,7 @@ static inline void lru_gen_release_memcg(struct mem_cgroup *memcg) +@@ -608,7 +599,7 @@ static inline void lru_gen_release_memcg { } @@ -141,11 +135,9 @@ index 66e067a635682..403c7461e7a70 100644 { } -diff --git a/mm/memcontrol.c b/mm/memcontrol.c -index 7815d556e38cc..5397aeb43986d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c -@@ -478,12 +478,8 @@ static void mem_cgroup_update_tree(struct mem_cgroup *memcg, int nid) +@@ -478,12 +478,8 @@ static void mem_cgroup_update_tree(struc struct mem_cgroup_tree_per_node *mctz; if (lru_gen_enabled()) { @@ -160,15 +152,12 @@ index 7815d556e38cc..5397aeb43986d 100644 return; } -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 74b4f9d660b56..ccde215c084ca 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4689,6 +4689,148 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - mem_cgroup_unlock_pages(); +@@ -4690,6 +4690,148 @@ void lru_gen_look_around(struct page_vma } -+/****************************************************************************** + /****************************************************************************** + * memcg LRU + ******************************************************************************/ + @@ -310,10 +299,11 @@ index 74b4f9d660b56..ccde215c084ca 100644 + +#endif + - /****************************************************************************** ++/****************************************************************************** * the eviction ******************************************************************************/ -@@ -5386,53 +5528,6 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control * + +@@ -5386,53 +5528,6 @@ done: pgdat->kswapd_failures = 0; } @@ -367,7 +357,7 @@ index 74b4f9d660b56..ccde215c084ca 100644 /****************************************************************************** * state change ******************************************************************************/ -@@ -6078,67 +6173,6 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) +@@ -6078,67 +6173,6 @@ void lru_gen_exit_memcg(struct mem_cgrou } } @@ -435,6 +425,3 @@ index 74b4f9d660b56..ccde215c084ca 100644 #endif /* CONFIG_MEMCG */ static int __init init_lru_gen(void) --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch b/target/linux/generic/backport-6.1/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch index 1f3f8ffe6da..94473755058 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch @@ -18,11 +18,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 5 +++++ 1 file changed, 5 insertions(+) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index ccde215c084ca..d5d6f8d94f58a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -6160,12 +6160,17 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) +@@ -6160,12 +6160,17 @@ void lru_gen_exit_memcg(struct mem_cgrou int i; int nid; @@ -40,6 +38,3 @@ index ccde215c084ca..d5d6f8d94f58a 100644 for (i = 0; i < NR_BLOOM_FILTERS; i++) { bitmap_free(lruvec->mm_state.filters[i]); lruvec->mm_state.filters[i] = NULL; --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch b/target/linux/generic/backport-6.1/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch index 02dd920e54c..2273977dc9c 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch @@ -17,11 +17,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index d5d6f8d94f58a..8f496c2e670a9 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3980,8 +3980,8 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, +@@ -3980,8 +3980,8 @@ restart: } #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) @@ -32,7 +30,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 { int i; pmd_t *pmd; -@@ -3994,18 +3994,19 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area +@@ -3994,18 +3994,19 @@ static void walk_pmd_range_locked(pud_t VM_WARN_ON_ONCE(pud_leaf(*pud)); /* try to batch at most 1+MIN_LRU_BATCH+1 entries */ @@ -56,7 +54,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 ptl = pmd_lockptr(args->mm, pmd); if (!spin_trylock(ptl)) -@@ -4016,15 +4017,16 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area +@@ -4016,15 +4017,16 @@ static void walk_pmd_range_locked(pud_t do { unsigned long pfn; struct folio *folio; @@ -76,7 +74,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 pmdp_test_and_clear_young(vma, addr, pmd + i); goto next; } -@@ -4053,12 +4055,11 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area +@@ -4053,12 +4055,11 @@ next: arch_leave_lazy_mmu_mode(); spin_unlock(ptl); done: @@ -92,7 +90,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 { } #endif -@@ -4071,9 +4072,9 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, +@@ -4071,9 +4072,9 @@ static void walk_pmd_range(pud_t *pud, u unsigned long next; unsigned long addr; struct vm_area_struct *vma; @@ -104,7 +102,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 VM_WARN_ON_ONCE(pud_leaf(*pud)); -@@ -4115,18 +4116,17 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, +@@ -4115,18 +4116,17 @@ restart: if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) continue; @@ -126,7 +124,7 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 } if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i)) -@@ -4143,7 +4143,7 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, +@@ -4143,7 +4143,7 @@ restart: update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i); } @@ -135,6 +133,3 @@ index d5d6f8d94f58a..8f496c2e670a9 100644 if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end)) goto restart; --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch b/target/linux/generic/backport-6.1/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch index caee6b096f1..1f071f0839c 100644 --- a/target/linux/generic/backport-6.1/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch +++ b/target/linux/generic/backport-6.1/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch @@ -24,11 +24,9 @@ Signed-off-by: T.J. Mercier mm/vmscan.c | 73 +++++++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 50 deletions(-) -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 8f496c2e670a9..f6ce7a1fd78a3 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4571,13 +4571,12 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) +@@ -4571,13 +4571,12 @@ static void lru_gen_age_node(struct pgli void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) { int i; @@ -44,7 +42,7 @@ index 8f496c2e670a9..f6ce7a1fd78a3 100644 struct folio *folio = pfn_folio(pvmw->pfn); struct mem_cgroup *memcg = folio_memcg(folio); struct pglist_data *pgdat = folio_pgdat(folio); -@@ -4594,25 +4593,28 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +@@ -4594,25 +4593,28 @@ void lru_gen_look_around(struct page_vma /* avoid taking the LRU lock under the PTL when possible */ walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL; @@ -81,7 +79,7 @@ index 8f496c2e670a9..f6ce7a1fd78a3 100644 for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) { unsigned long pfn; -@@ -4637,56 +4639,27 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +@@ -4637,56 +4639,27 @@ void lru_gen_look_around(struct page_vma !folio_test_swapcache(folio))) folio_mark_dirty(folio); @@ -148,6 +146,3 @@ index 8f496c2e670a9..f6ce7a1fd78a3 100644 } /****************************************************************************** --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch b/target/linux/generic/backport-6.1/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch index 4bf796c93d2..fc42a245f39 100644 --- a/target/linux/generic/backport-6.1/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch +++ b/target/linux/generic/backport-6.1/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch @@ -46,8 +46,6 @@ Signed-off-by: Andrew Morton mm/vmscan.c | 112 +++++++++++++++-------------------------- 2 files changed, 42 insertions(+), 78 deletions(-) -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index 403c7461e7a70..d62a5accf1be4 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -453,18 +453,14 @@ enum { @@ -71,11 +69,9 @@ index 403c7461e7a70..d62a5accf1be4 100644 }; struct lru_gen_mm_walk { -diff --git a/mm/vmscan.c b/mm/vmscan.c -index f6ce7a1fd78a3..851758303dbf4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -3371,18 +3371,13 @@ void lru_gen_del_mm(struct mm_struct *mm) +@@ -3371,18 +3371,13 @@ void lru_gen_del_mm(struct mm_struct *mm if (!lruvec) continue; @@ -99,7 +95,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 } list_del_init(&mm->lru_gen.list); -@@ -3478,68 +3473,54 @@ static bool iterate_mm_list(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, +@@ -3478,68 +3473,54 @@ static bool iterate_mm_list(struct lruve struct mm_struct **iter) { bool first = false; @@ -191,7 +187,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 if (*iter || last) reset_mm_stats(lruvec, walk, last); -@@ -3567,9 +3548,9 @@ static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long max_seq) +@@ -3567,9 +3548,9 @@ static bool iterate_mm_list_nowalk(struc VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq); @@ -204,7 +200,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 WRITE_ONCE(mm_state->seq, mm_state->seq + 1); reset_mm_stats(lruvec, NULL, true); success = true; -@@ -4172,10 +4153,6 @@ static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end, +@@ -4172,10 +4153,6 @@ restart: walk_pmd_range(&val, addr, next, args); @@ -215,7 +211,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 if (need_resched() || walk->batched >= MAX_LRU_BATCH) { end = (addr | ~PUD_MASK) + 1; goto done; -@@ -4208,8 +4185,14 @@ static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct lru_gen_ +@@ -4208,8 +4185,14 @@ static void walk_mm(struct lruvec *lruve walk->next_addr = FIRST_USER_ADDRESS; do { @@ -230,7 +226,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 /* folio_update_gen() requires stable folio_memcg() */ if (!mem_cgroup_trylock_pages(memcg)) break; -@@ -4442,25 +4425,12 @@ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +@@ -4442,25 +4425,12 @@ static bool try_to_inc_max_seq(struct lr success = iterate_mm_list(lruvec, walk, &mm); if (mm) walk_mm(lruvec, mm, walk); @@ -259,7 +255,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 } /****************************************************************************** -@@ -6105,7 +6075,6 @@ void lru_gen_init_lruvec(struct lruvec *lruvec) +@@ -6105,7 +6075,6 @@ void lru_gen_init_lruvec(struct lruvec * INIT_LIST_HEAD(&lrugen->folios[gen][type][zone]); lruvec->mm_state.seq = MIN_NR_GENS; @@ -267,7 +263,7 @@ index f6ce7a1fd78a3..851758303dbf4 100644 } #ifdef CONFIG_MEMCG -@@ -6138,7 +6107,6 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) +@@ -6138,7 +6107,6 @@ void lru_gen_exit_memcg(struct mem_cgrou for_each_node(nid) { struct lruvec *lruvec = get_lruvec(memcg, nid); @@ -275,6 +271,3 @@ index f6ce7a1fd78a3..851758303dbf4 100644 VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, sizeof(lruvec->lrugen.nr_pages))); --- -2.40.1 - diff --git a/target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch b/target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch new file mode 100644 index 00000000000..81c14a0557b --- /dev/null +++ b/target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch @@ -0,0 +1,48 @@ +From 9c5a170677c3c8facc83e931a57f4c99c0511ae0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:10:37 +0100 +Subject: [PATCH] net: phylink: add phylink_get_link_timer_ns() helper + +Add a helper to convert the PHY interface mode to the required link +timer setting as stated by the appropriate standard. Inappropriate +interface modes return an error. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + include/linux/phylink.h | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -614,6 +614,30 @@ int phylink_speed_up(struct phylink *pl) + + void phylink_set_port_modes(unsigned long *bits); + ++/** ++ * phylink_get_link_timer_ns - return the PCS link timer value ++ * @interface: link &typedef phy_interface_t mode ++ * ++ * Return the PCS link timer setting in nanoseconds for the PHY @interface ++ * mode, or -EINVAL if not appropriate. ++ */ ++static inline int phylink_get_link_timer_ns(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return 1600000; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return 10000000; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ + void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, + u16 bmsr, u16 lpa); + void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, diff --git a/target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch b/target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch new file mode 100644 index 00000000000..4d024b063ab --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch @@ -0,0 +1,32 @@ +From c3552d3f85f06cf4b4818bd84c4fcc09d8d45165 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:17:19 +0100 +Subject: [PATCH 01/13] net: dsa: mt7530: make some noise if register read + fails + +Simply returning the negative error value instead of the read value +doesn't seem like a good idea. Return 0 instead and add WARN_ON_ONCE(1) +so this kind of error will not go unnoticed. + +Suggested-by: Andrew Lunn +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -224,9 +224,10 @@ mt7530_mii_read(struct mt7530_priv *priv + /* MT7530 uses 31 as the pseudo port */ + ret = bus->write(bus, 0x1f, 0x1f, page); + if (ret < 0) { ++ WARN_ON_ONCE(1); + dev_err(&bus->dev, + "failed to read mt7530 register\n"); +- return ret; ++ return 0; + } + + lo = bus->read(bus, 0x1f, r); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch b/target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch new file mode 100644 index 00000000000..4f255abc5bc --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch @@ -0,0 +1,111 @@ +From b896355fc4988216d4f38582d07add9252a795ae Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:17:30 +0100 +Subject: [PATCH 02/13] net: dsa: mt7530: refactor SGMII PCS creation + +Instead of macro templates use a dedidated function and allocated +regmap_config when creating the regmaps for the pcs-mtk-lynxi +instances. +This is in preparation to switching to use unlocked regmap accessors +and have regmap's locking API handle locking for us. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 74 +++++++++++++++++++++++++++------------- + 1 file changed, 50 insertions(+), 24 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2950,26 +2950,56 @@ static const struct regmap_bus mt7531_re + .reg_update_bits = mt7530_regmap_update_bits, + }; + +-#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \ +- { \ +- .name = _name, \ +- .reg_bits = 16, \ +- .val_bits = 32, \ +- .reg_stride = 4, \ +- .reg_base = _reg_base, \ +- .max_register = 0x17c, \ ++static int ++mt7531_create_sgmii(struct mt7530_priv *priv) ++{ ++ struct regmap_config *mt7531_pcs_config[2]; ++ struct phylink_pcs *pcs; ++ struct regmap *regmap; ++ int i, ret = 0; ++ ++ for (i = 0; i < 2; i++) { ++ mt7531_pcs_config[i] = devm_kzalloc(priv->dev, ++ sizeof(struct regmap_config), ++ GFP_KERNEL); ++ if (!mt7531_pcs_config[i]) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ mt7531_pcs_config[i]->name = i ? "port6" : "port5"; ++ mt7531_pcs_config[i]->reg_bits = 16; ++ mt7531_pcs_config[i]->val_bits = 32; ++ mt7531_pcs_config[i]->reg_stride = 4; ++ mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); ++ mt7531_pcs_config[i]->max_register = 0x17c; ++ ++ regmap = devm_regmap_init(priv->dev, ++ &mt7531_regmap_bus, priv, ++ mt7531_pcs_config[i]); ++ if (IS_ERR(regmap)) { ++ ret = PTR_ERR(regmap); ++ break; ++ } ++ pcs = mtk_pcs_lynxi_create(priv->dev, regmap, ++ MT7531_PHYA_CTRL_SIGNAL3, 0); ++ if (!pcs) { ++ ret = -ENXIO; ++ break; ++ } ++ priv->ports[5 + i].sgmii_pcs = pcs; + } + +-static const struct regmap_config mt7531_pcs_config[] = { +- MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)), +- MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)), +-}; ++ if (ret && i) ++ mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); ++ ++ return ret; ++} + + static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; +- struct regmap *regmap; + int i, ret; + + /* Initialise the PCS devices */ +@@ -2991,15 +3021,11 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + +- if (priv->id == ID_MT7531) +- for (i = 0; i < 2; i++) { +- regmap = devm_regmap_init(ds->dev, +- &mt7531_regmap_bus, priv, +- &mt7531_pcs_config[i]); +- priv->ports[5 + i].sgmii_pcs = +- mtk_pcs_lynxi_create(ds->dev, regmap, +- MT7531_PHYA_CTRL_SIGNAL3, 0); +- } ++ if (priv->id == ID_MT7531) { ++ ret = mt7531_create_sgmii(priv); ++ if (ret && priv->irq) ++ mt7530_free_irq_common(priv); ++ } + + return ret; + } diff --git a/target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch b/target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch new file mode 100644 index 00000000000..77ac3f3f268 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch @@ -0,0 +1,74 @@ +From 33396408776385f3d2f6069646169a6b5b28e3b3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:17:40 +0100 +Subject: [PATCH 03/13] net: dsa: mt7530: use unlocked regmap accessors + +Instead of wrapping the locked register accessor functions, use the +unlocked variants and add locking wrapper functions to let regmap +handle the locking. + +This is a preparation towards being able to always use regmap to +access switch registers instead of open-coded accessor functions. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2923,7 +2923,7 @@ static int mt7530_regmap_read(void *cont + { + struct mt7530_priv *priv = context; + +- *val = mt7530_read(priv, reg); ++ *val = mt7530_mii_read(priv, reg); + return 0; + }; + +@@ -2931,23 +2931,25 @@ static int mt7530_regmap_write(void *con + { + struct mt7530_priv *priv = context; + +- mt7530_write(priv, reg, val); ++ mt7530_mii_write(priv, reg, val); + return 0; + }; + +-static int mt7530_regmap_update_bits(void *context, unsigned int reg, +- unsigned int mask, unsigned int val) ++static void ++mt7530_mdio_regmap_lock(void *mdio_lock) + { +- struct mt7530_priv *priv = context; ++ mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED); ++} + +- mt7530_rmw(priv, reg, mask, val); +- return 0; +-}; ++static void ++mt7530_mdio_regmap_unlock(void *mdio_lock) ++{ ++ mutex_unlock(mdio_lock); ++} + + static const struct regmap_bus mt7531_regmap_bus = { + .reg_write = mt7530_regmap_write, + .reg_read = mt7530_regmap_read, +- .reg_update_bits = mt7530_regmap_update_bits, + }; + + static int +@@ -2973,6 +2975,9 @@ mt7531_create_sgmii(struct mt7530_priv * + mt7531_pcs_config[i]->reg_stride = 4; + mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); + mt7531_pcs_config[i]->max_register = 0x17c; ++ mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock; ++ mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; ++ mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; + + regmap = devm_regmap_init(priv->dev, + &mt7531_regmap_bus, priv, diff --git a/target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch b/target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch new file mode 100644 index 00000000000..6e3e8b09b96 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch @@ -0,0 +1,224 @@ +From 743cba4345cb366248f9d375c6a9e50243dc0677 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:17:52 +0100 +Subject: [PATCH 04/13] net: dsa: mt7530: use regmap to access switch register + space + +Use regmap API to access the switch register space. + +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 99 ++++++++++++++++++++++++---------------- + drivers/net/dsa/mt7530.h | 2 + + 2 files changed, 62 insertions(+), 39 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -183,9 +183,9 @@ core_clear(struct mt7530_priv *priv, u32 + } + + static int +-mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) ++mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) + { +- struct mii_bus *bus = priv->bus; ++ struct mii_bus *bus = context; + u16 page, r, lo, hi; + int ret; + +@@ -197,24 +197,34 @@ mt7530_mii_write(struct mt7530_priv *pri + /* MT7530 uses 31 as the pseudo port */ + ret = bus->write(bus, 0x1f, 0x1f, page); + if (ret < 0) +- goto err; ++ return ret; + + ret = bus->write(bus, 0x1f, r, lo); + if (ret < 0) +- goto err; ++ return ret; + + ret = bus->write(bus, 0x1f, 0x10, hi); +-err: ++ return ret; ++} ++ ++static int ++mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ int ret; ++ ++ ret = regmap_write(priv->regmap, reg, val); ++ + if (ret < 0) +- dev_err(&bus->dev, ++ dev_err(priv->dev, + "failed to write mt7530 register\n"); ++ + return ret; + } + +-static u32 +-mt7530_mii_read(struct mt7530_priv *priv, u32 reg) ++static int ++mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) + { +- struct mii_bus *bus = priv->bus; ++ struct mii_bus *bus = context; + u16 page, r, lo, hi; + int ret; + +@@ -223,17 +233,32 @@ mt7530_mii_read(struct mt7530_priv *priv + + /* MT7530 uses 31 as the pseudo port */ + ret = bus->write(bus, 0x1f, 0x1f, page); +- if (ret < 0) { ++ if (ret < 0) ++ return ret; ++ ++ lo = bus->read(bus, 0x1f, r); ++ hi = bus->read(bus, 0x1f, 0x10); ++ ++ *val = (hi << 16) | (lo & 0xffff); ++ ++ return 0; ++} ++ ++static u32 ++mt7530_mii_read(struct mt7530_priv *priv, u32 reg) ++{ ++ int ret; ++ u32 val; ++ ++ ret = regmap_read(priv->regmap, reg, &val); ++ if (ret) { + WARN_ON_ONCE(1); +- dev_err(&bus->dev, ++ dev_err(priv->dev, + "failed to read mt7530 register\n"); + return 0; + } + +- lo = bus->read(bus, 0x1f, r); +- hi = bus->read(bus, 0x1f, 0x10); +- +- return (hi << 16) | (lo & 0xffff); ++ return val; + } + + static void +@@ -283,14 +308,10 @@ mt7530_rmw(struct mt7530_priv *priv, u32 + u32 mask, u32 set) + { + struct mii_bus *bus = priv->bus; +- u32 val; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- val = mt7530_mii_read(priv, reg); +- val &= ~mask; +- val |= set; +- mt7530_mii_write(priv, reg, val); ++ regmap_update_bits(priv->regmap, reg, mask, set); + + mutex_unlock(&bus->mdio_lock); + } +@@ -298,7 +319,7 @@ mt7530_rmw(struct mt7530_priv *priv, u32 + static void + mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val) + { +- mt7530_rmw(priv, reg, 0, val); ++ mt7530_rmw(priv, reg, val, val); + } + + static void +@@ -2919,22 +2940,6 @@ static const struct phylink_pcs_ops mt75 + .pcs_an_restart = mt7530_pcs_an_restart, + }; + +-static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) +-{ +- struct mt7530_priv *priv = context; +- +- *val = mt7530_mii_read(priv, reg); +- return 0; +-}; +- +-static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) +-{ +- struct mt7530_priv *priv = context; +- +- mt7530_mii_write(priv, reg, val); +- return 0; +-}; +- + static void + mt7530_mdio_regmap_lock(void *mdio_lock) + { +@@ -2947,7 +2952,7 @@ mt7530_mdio_regmap_unlock(void *mdio_loc + mutex_unlock(mdio_lock); + } + +-static const struct regmap_bus mt7531_regmap_bus = { ++static const struct regmap_bus mt7530_regmap_bus = { + .reg_write = mt7530_regmap_write, + .reg_read = mt7530_regmap_read, + }; +@@ -2980,7 +2985,7 @@ mt7531_create_sgmii(struct mt7530_priv * + mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; + + regmap = devm_regmap_init(priv->dev, +- &mt7531_regmap_bus, priv, ++ &mt7530_regmap_bus, priv->bus, + mt7531_pcs_config[i]); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); +@@ -3145,6 +3150,7 @@ MODULE_DEVICE_TABLE(of, mt7530_of_match) + static int + mt7530_probe(struct mdio_device *mdiodev) + { ++ static struct regmap_config *regmap_config; + struct mt7530_priv *priv; + struct device_node *dn; + +@@ -3224,6 +3230,21 @@ mt7530_probe(struct mdio_device *mdiodev + mutex_init(&priv->reg_mutex); + dev_set_drvdata(&mdiodev->dev, priv); + ++ regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), ++ GFP_KERNEL); ++ if (!regmap_config) ++ return -ENOMEM; ++ ++ regmap_config->reg_bits = 16; ++ regmap_config->val_bits = 32; ++ regmap_config->reg_stride = 4; ++ regmap_config->max_register = MT7530_CREV; ++ regmap_config->disable_locking = true; ++ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, ++ priv->bus, regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ + return dsa_register_switch(priv->ds); + } + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -747,6 +747,7 @@ struct mt753x_info { + * @dev: The device pointer + * @ds: The pointer to the dsa core structure + * @bus: The bus used for the device and built-in PHY ++ * @regmap: The regmap instance representing all switch registers + * @rstc: The pointer to reset control used by MCM + * @core_pwr: The power supplied into the core + * @io_pwr: The power supplied into the I/O +@@ -767,6 +768,7 @@ struct mt7530_priv { + struct device *dev; + struct dsa_switch *ds; + struct mii_bus *bus; ++ struct regmap *regmap; + struct reset_control *rstc; + struct regulator *core_pwr; + struct regulator *io_pwr; diff --git a/target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch b/target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch new file mode 100644 index 00000000000..a02702fd687 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch @@ -0,0 +1,54 @@ +From f3cf1d06e2aef644b426c23b4bb570780b1f8d47 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:18:04 +0100 +Subject: [PATCH 05/13] net: dsa: mt7530: move SGMII PCS creation to + mt7530_probe function + +Move creating the SGMII PCS from mt753x_setup() to the more appropriate +mt7530_probe() function. +This is done also in preparation of moving all functions related to +MDIO-connected MT753x switches to a separate module. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3031,12 +3031,6 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + +- if (priv->id == ID_MT7531) { +- ret = mt7531_create_sgmii(priv); +- if (ret && priv->irq) +- mt7530_free_irq_common(priv); +- } +- + return ret; + } + +@@ -3153,6 +3147,7 @@ mt7530_probe(struct mdio_device *mdiodev + static struct regmap_config *regmap_config; + struct mt7530_priv *priv; + struct device_node *dn; ++ int ret; + + dn = mdiodev->dev.of_node; + +@@ -3245,6 +3240,12 @@ mt7530_probe(struct mdio_device *mdiodev + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + ++ if (priv->id == ID_MT7531) { ++ ret = mt7531_create_sgmii(priv); ++ if (ret) ++ return ret; ++ } ++ + return dsa_register_switch(priv->ds); + } + diff --git a/target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch b/target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch new file mode 100644 index 00000000000..98122caf090 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch @@ -0,0 +1,273 @@ +From e4729ae7c095c0c87794bff47ea43e35d69de986 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:18:16 +0100 +Subject: [PATCH 06/13] net: dsa: mt7530: introduce mutex helpers + +As the MDIO bus lock only needs to be involved if actually operating +on an MDIO-connected switch we will need to skip locking for built-in +switches which are accessed via MMIO. +Create helper functions which simplify that upcoming change. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 73 ++++++++++++++++++++-------------------- + 1 file changed, 36 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -143,31 +143,40 @@ err: + } + + static void +-core_write(struct mt7530_priv *priv, u32 reg, u32 val) ++mt7530_mutex_lock(struct mt7530_priv *priv) + { +- struct mii_bus *bus = priv->bus; ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++} + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++static void ++mt7530_mutex_unlock(struct mt7530_priv *priv) ++{ ++ mutex_unlock(&priv->bus->mdio_lock); ++} ++ ++static void ++core_write(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ mt7530_mutex_lock(priv); + + core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + } + + static void + core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) + { +- struct mii_bus *bus = priv->bus; + u32 val; + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2); + val &= ~mask; + val |= set; + core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + } + + static void +@@ -264,13 +273,11 @@ mt7530_mii_read(struct mt7530_priv *priv + static void + mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + { +- struct mii_bus *bus = priv->bus; +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + mt7530_mii_write(priv, reg, val); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + } + + static u32 +@@ -282,14 +289,13 @@ _mt7530_unlocked_read(struct mt7530_dumm + static u32 + _mt7530_read(struct mt7530_dummy_poll *p) + { +- struct mii_bus *bus = p->priv->bus; + u32 val; + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(p->priv); + + val = mt7530_mii_read(p->priv, p->reg); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(p->priv); + + return val; + } +@@ -307,13 +313,11 @@ static void + mt7530_rmw(struct mt7530_priv *priv, u32 reg, + u32 mask, u32 set) + { +- struct mii_bus *bus = priv->bus; +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + regmap_update_bits(priv->regmap, reg, mask, set); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + } + + static void +@@ -645,14 +649,13 @@ static int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +- struct mii_bus *bus = priv->bus; + struct mt7530_dummy_poll p; + u32 reg, val; + int ret; + + INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); +@@ -685,7 +688,7 @@ mt7531_ind_c45_phy_read(struct mt7530_pr + + ret = val & MT7531_MDIO_RW_DATA_MASK; + out: +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + return ret; + } +@@ -694,14 +697,13 @@ static int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u32 data) + { +- struct mii_bus *bus = priv->bus; + struct mt7530_dummy_poll p; + u32 val, reg; + int ret; + + INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); +@@ -733,7 +735,7 @@ mt7531_ind_c45_phy_write(struct mt7530_p + } + + out: +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + return ret; + } +@@ -741,14 +743,13 @@ out: + static int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mii_bus *bus = priv->bus; + struct mt7530_dummy_poll p; + int ret; + u32 val; + + INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); +@@ -771,7 +772,7 @@ mt7531_ind_c22_phy_read(struct mt7530_pr + + ret = val & MT7531_MDIO_RW_DATA_MASK; + out: +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + return ret; + } +@@ -780,14 +781,13 @@ static int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mii_bus *bus = priv->bus; + struct mt7530_dummy_poll p; + int ret; + u32 reg; + + INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, + !(reg & MT7531_PHY_ACS_ST), 20, 100000); +@@ -809,7 +809,7 @@ mt7531_ind_c22_phy_write(struct mt7530_p + } + + out: +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + return ret; + } +@@ -1109,7 +1109,6 @@ static int + mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct mt7530_priv *priv = ds->priv; +- struct mii_bus *bus = priv->bus; + int length; + u32 val; + +@@ -1120,7 +1119,7 @@ mt7530_port_change_mtu(struct dsa_switch + if (!dsa_is_cpu_port(ds, port)) + return 0; + +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + + val = mt7530_mii_read(priv, MT7530_GMACCR); + val &= ~MAX_RX_PKT_LEN_MASK; +@@ -1141,7 +1140,7 @@ mt7530_port_change_mtu(struct dsa_switch + + mt7530_mii_write(priv, MT7530_GMACCR, val); + +- mutex_unlock(&bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + return 0; + } +@@ -1942,10 +1941,10 @@ mt7530_irq_thread_fn(int irq, void *dev_ + u32 val; + int p; + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + val = mt7530_mii_read(priv, MT7530_SYS_INT_STS); + mt7530_mii_write(priv, MT7530_SYS_INT_STS, val); +- mutex_unlock(&priv->bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + + for (p = 0; p < MT7530_NUM_PHYS; p++) { + if (BIT(p) & val) { +@@ -1981,7 +1980,7 @@ mt7530_irq_bus_lock(struct irq_data *d) + { + struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mt7530_mutex_lock(priv); + } + + static void +@@ -1990,7 +1989,7 @@ mt7530_irq_bus_sync_unlock(struct irq_da + struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); + + mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); +- mutex_unlock(&priv->bus->mdio_lock); ++ mt7530_mutex_unlock(priv); + } + + static struct irq_chip mt7530_irq_chip = { diff --git a/target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch b/target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch new file mode 100644 index 00000000000..5065d735232 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch @@ -0,0 +1,75 @@ +From 0d7ae94a0c581f86939bebec0b6ccd66e640d1d8 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:18:28 +0100 +Subject: [PATCH 07/13] net: dsa: mt7530: move p5_intf_modes() function to + mt7530.c + +In preparation of splitting mt7530.c into a driver for MDIO-connected +as well as MDIO-accessed built-in switches on one hand and MMIO-accessed +built-in switches move the p5_inft_modes() function from mt7530.h to +mt7530.c. The function is only needed there and will trigger a compiler +warning about a defined but unused function otherwise when including +mt7530.h in the to-be-introduced bus-specific drivers. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 18 ++++++++++++++++++ + drivers/net/dsa/mt7530.h | 18 ------------------ + 2 files changed, 18 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -950,6 +950,24 @@ mt7530_set_ageing_time(struct dsa_switch + return 0; + } + ++static const char *p5_intf_modes(unsigned int p5_interface) ++{ ++ switch (p5_interface) { ++ case P5_DISABLED: ++ return "DISABLED"; ++ case P5_INTF_SEL_PHY_P0: ++ return "PHY P0"; ++ case P5_INTF_SEL_PHY_P4: ++ return "PHY P4"; ++ case P5_INTF_SEL_GMAC5: ++ return "GMAC5"; ++ case P5_INTF_SEL_GMAC5_SGMII: ++ return "GMAC5_SGMII"; ++ default: ++ return "unknown"; ++ } ++} ++ + static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + { + struct mt7530_priv *priv = ds->priv; +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -682,24 +682,6 @@ enum p5_interface_select { + P5_INTF_SEL_GMAC5_SGMII, + }; + +-static const char *p5_intf_modes(unsigned int p5_interface) +-{ +- switch (p5_interface) { +- case P5_DISABLED: +- return "DISABLED"; +- case P5_INTF_SEL_PHY_P0: +- return "PHY P0"; +- case P5_INTF_SEL_PHY_P4: +- return "PHY P4"; +- case P5_INTF_SEL_GMAC5: +- return "GMAC5"; +- case P5_INTF_SEL_GMAC5_SGMII: +- return "GMAC5_SGMII"; +- default: +- return "unknown"; +- } +-} +- + struct mt7530_priv; + + struct mt753x_pcs { diff --git a/target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch b/target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch new file mode 100644 index 00000000000..761aa1d9797 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch @@ -0,0 +1,155 @@ +From 4d632005c90e253c000d0db73b7cdb9d8dc2e2dd Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:18:39 +0100 +Subject: [PATCH 08/13] net: dsa: mt7530: introduce mt7530_probe_common helper + function + +Move commonly used parts from mt7530_probe into new mt7530_probe_common +helper function which will be used by both, mt7530_probe and the +to-be-introduced mt7988_probe. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 98 ++++++++++++++++++++++------------------ + 1 file changed, 54 insertions(+), 44 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3159,44 +3159,21 @@ static const struct of_device_id mt7530_ + MODULE_DEVICE_TABLE(of, mt7530_of_match); + + static int +-mt7530_probe(struct mdio_device *mdiodev) ++mt7530_probe_common(struct mt7530_priv *priv) + { +- static struct regmap_config *regmap_config; +- struct mt7530_priv *priv; +- struct device_node *dn; +- int ret; ++ struct device *dev = priv->dev; + +- dn = mdiodev->dev.of_node; +- +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + +- priv->ds->dev = &mdiodev->dev; ++ priv->ds->dev = dev; + priv->ds->num_ports = MT7530_NUM_PORTS; + +- /* Use medatek,mcm property to distinguish hardware type that would +- * casues a little bit differences on power-on sequence. +- */ +- priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); +- if (priv->mcm) { +- dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); +- +- priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); +- if (IS_ERR(priv->rstc)) { +- dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); +- return PTR_ERR(priv->rstc); +- } +- } +- + /* Get the hardware identifier from the devicetree node. + * We will need it for some of the clock and regulator setup. + */ +- priv->info = of_device_get_match_data(&mdiodev->dev); ++ priv->info = of_device_get_match_data(dev); + if (!priv->info) + return -EINVAL; + +@@ -3210,23 +3187,53 @@ mt7530_probe(struct mdio_device *mdiodev + return -EINVAL; + + priv->id = priv->info->id; ++ priv->dev = dev; ++ priv->ds->priv = priv; ++ priv->ds->ops = &mt7530_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ dev_set_drvdata(dev, priv); + +- if (priv->id == ID_MT7530) { +- priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); +- if (IS_ERR(priv->core_pwr)) +- return PTR_ERR(priv->core_pwr); ++ return 0; ++} + +- priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); +- if (IS_ERR(priv->io_pwr)) +- return PTR_ERR(priv->io_pwr); +- } ++static int ++mt7530_probe(struct mdio_device *mdiodev) ++{ ++ static struct regmap_config *regmap_config; ++ struct mt7530_priv *priv; ++ struct device_node *dn; ++ int ret; ++ ++ dn = mdiodev->dev.of_node; ++ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; + +- /* Not MCM that indicates switch works as the remote standalone ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ ret = mt7530_probe_common(priv); ++ if (ret) ++ return ret; ++ ++ /* Use medatek,mcm property to distinguish hardware type that would ++ * cause a little bit differences on power-on sequence. ++ * Not MCM that indicates switch works as the remote standalone + * integrated circuit so the GPIO pin would be used to complete + * the reset, otherwise memory-mapped register accessing used + * through syscon provides in the case of MCM. + */ +- if (!priv->mcm) { ++ priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); ++ if (priv->mcm) { ++ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); ++ ++ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); ++ if (IS_ERR(priv->rstc)) { ++ dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->rstc); ++ } ++ } else { + priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { +@@ -3235,12 +3242,15 @@ mt7530_probe(struct mdio_device *mdiodev + } + } + +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- priv->ds->priv = priv; +- priv->ds->ops = &mt7530_switch_ops; +- mutex_init(&priv->reg_mutex); +- dev_set_drvdata(&mdiodev->dev, priv); ++ if (priv->id == ID_MT7530) { ++ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); ++ if (IS_ERR(priv->core_pwr)) ++ return PTR_ERR(priv->core_pwr); ++ ++ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); ++ if (IS_ERR(priv->io_pwr)) ++ return PTR_ERR(priv->io_pwr); ++ } + + regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), + GFP_KERNEL); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch b/target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch new file mode 100644 index 00000000000..d3c2a7e2c9b --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch @@ -0,0 +1,54 @@ +From 69b838d2629e6b82bcd9e0ab3c1c03f46e5e01d3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:18:50 +0100 +Subject: [PATCH 09/13] net: dsa: mt7530: introduce mt7530_remove_common helper + function + +Move commonly used parts from mt7530_remove into new +mt7530_remove_common helper function which will be used by both, +mt7530_remove and the to-be-introduced mt7988_remove. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3277,6 +3277,17 @@ mt7530_probe(struct mdio_device *mdiodev + } + + static void ++mt7530_remove_common(struct mt7530_priv *priv) ++{ ++ if (priv->irq) ++ mt7530_free_irq(priv); ++ ++ dsa_unregister_switch(priv->ds); ++ ++ mutex_destroy(&priv->reg_mutex); ++} ++ ++static void + mt7530_remove(struct mdio_device *mdiodev) + { + struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); +@@ -3295,15 +3306,10 @@ mt7530_remove(struct mdio_device *mdiode + dev_err(priv->dev, "Failed to disable io pwr: %d\n", + ret); + +- if (priv->irq) +- mt7530_free_irq(priv); +- +- dsa_unregister_switch(priv->ds); ++ mt7530_remove_common(priv); + + for (i = 0; i < 2; ++i) + mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); +- +- mutex_destroy(&priv->reg_mutex); + } + + static void mt7530_shutdown(struct mdio_device *mdiodev) diff --git a/target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch b/target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch new file mode 100644 index 00000000000..55378ca016b --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch @@ -0,0 +1,691 @@ +From 8eceed6dbd74067dbf4d8e39f14734f4d2f35176 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:19:13 +0100 +Subject: [PATCH 10/13] net: dsa: mt7530: introduce separate MDIO driver + +Split MT7530 switch driver into a common part and a part specific +for MDIO connected switches and multi-chip modules. +Move MDIO-specific functions to newly introduced mt7530-mdio.c while +keeping the common parts in mt7530.c. +Introduce new Kconfig symbol CONFIG_NET_DSA_MT7530_MDIO which is +implied by CONFIG_NET_DSA_MT7530. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + MAINTAINERS | 1 + + drivers/net/dsa/Kconfig | 16 +- + drivers/net/dsa/Makefile | 1 + + drivers/net/dsa/mt7530-mdio.c | 271 ++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.c | 264 +-------------------------------- + drivers/net/dsa/mt7530.h | 6 + + 6 files changed, 301 insertions(+), 258 deletions(-) + create mode 100644 drivers/net/dsa/mt7530-mdio.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -13060,6 +13060,7 @@ M: Landen Chao + L: netdev@vger.kernel.org + S: Maintained ++F: drivers/net/dsa/mt7530-mdio.c + F: drivers/net/dsa/mt7530.* + F: net/dsa/tag_mtk.c + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -37,10 +37,22 @@ config NET_DSA_MT7530 + tristate "MediaTek MT753x and MT7621 Ethernet switch support" + select NET_DSA_TAG_MTK + select MEDIATEK_GE_PHY ++ imply NET_DSA_MT7530_MDIO ++ help ++ This enables support for the MediaTek MT7530 and MT7531 Ethernet ++ switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT, ++ MT7621ST and MT7623AI SoCs, and built-in switch in MT7988 SoC are ++ supported as well. ++ ++config NET_DSA_MT7530_MDIO ++ tristate "MediaTek MT7530 MDIO interface driver" ++ depends on NET_DSA_MT7530 + select PCS_MTK_LYNXI + help +- This enables support for the MediaTek MT7530, MT7531, and MT7621 +- Ethernet switch chips. ++ This enables support for the MediaTek MT7530 and MT7531 switch ++ chips which are connected via MDIO, as well as multi-chip ++ module MT7530 which can be found in the MT7621AT, MT7621DAT, ++ MT7621ST and MT7623AI SoCs. + + config NET_DSA_MV88E6060 + tristate "Marvell 88E6060 ethernet switch chip support" +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -7,6 +7,7 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdi + endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o ++obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o + obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o +--- /dev/null ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -0,0 +1,271 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mt7530.h" ++ ++static int ++mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) ++{ ++ struct mii_bus *bus = context; ++ u16 page, r, lo, hi; ++ int ret; ++ ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; ++ lo = val & 0xffff; ++ hi = val >> 16; ++ ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) ++ return ret; ++ ++ ret = bus->write(bus, 0x1f, r, lo); ++ if (ret < 0) ++ return ret; ++ ++ ret = bus->write(bus, 0x1f, 0x10, hi); ++ return ret; ++} ++ ++static int ++mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) ++{ ++ struct mii_bus *bus = context; ++ u16 page, r, lo, hi; ++ int ret; ++ ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; ++ ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) ++ return ret; ++ ++ lo = bus->read(bus, 0x1f, r); ++ hi = bus->read(bus, 0x1f, 0x10); ++ ++ *val = (hi << 16) | (lo & 0xffff); ++ ++ return 0; ++} ++ ++static void ++mt7530_mdio_regmap_lock(void *mdio_lock) ++{ ++ mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED); ++} ++ ++static void ++mt7530_mdio_regmap_unlock(void *mdio_lock) ++{ ++ mutex_unlock(mdio_lock); ++} ++ ++static const struct regmap_bus mt7530_regmap_bus = { ++ .reg_write = mt7530_regmap_write, ++ .reg_read = mt7530_regmap_read, ++}; ++ ++static int ++mt7531_create_sgmii(struct mt7530_priv *priv) ++{ ++ struct regmap_config *mt7531_pcs_config[2]; ++ struct phylink_pcs *pcs; ++ struct regmap *regmap; ++ int i, ret = 0; ++ ++ for (i = 0; i < 2; i++) { ++ mt7531_pcs_config[i] = devm_kzalloc(priv->dev, ++ sizeof(struct regmap_config), ++ GFP_KERNEL); ++ if (!mt7531_pcs_config[i]) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ mt7531_pcs_config[i]->name = i ? "port6" : "port5"; ++ mt7531_pcs_config[i]->reg_bits = 16; ++ mt7531_pcs_config[i]->val_bits = 32; ++ mt7531_pcs_config[i]->reg_stride = 4; ++ mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); ++ mt7531_pcs_config[i]->max_register = 0x17c; ++ mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock; ++ mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; ++ mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; ++ ++ regmap = devm_regmap_init(priv->dev, ++ &mt7530_regmap_bus, priv->bus, ++ mt7531_pcs_config[i]); ++ if (IS_ERR(regmap)) { ++ ret = PTR_ERR(regmap); ++ break; ++ } ++ pcs = mtk_pcs_lynxi_create(priv->dev, regmap, ++ MT7531_PHYA_CTRL_SIGNAL3, 0); ++ if (!pcs) { ++ ret = -ENXIO; ++ break; ++ } ++ priv->ports[5 + i].sgmii_pcs = pcs; ++ } ++ ++ if (ret && i) ++ mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); ++ ++ return ret; ++} ++ ++static const struct of_device_id mt7530_of_match[] = { ++ { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, ++ { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, ++ { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mt7530_of_match); ++ ++static int ++mt7530_probe(struct mdio_device *mdiodev) ++{ ++ static struct regmap_config *regmap_config; ++ struct mt7530_priv *priv; ++ struct device_node *dn; ++ int ret; ++ ++ dn = mdiodev->dev.of_node; ++ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ ret = mt7530_probe_common(priv); ++ if (ret) ++ return ret; ++ ++ /* Use medatek,mcm property to distinguish hardware type that would ++ * cause a little bit differences on power-on sequence. ++ * Not MCM that indicates switch works as the remote standalone ++ * integrated circuit so the GPIO pin would be used to complete ++ * the reset, otherwise memory-mapped register accessing used ++ * through syscon provides in the case of MCM. ++ */ ++ priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); ++ if (priv->mcm) { ++ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); ++ ++ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); ++ if (IS_ERR(priv->rstc)) { ++ dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->rstc); ++ } ++ } else { ++ priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(priv->reset)) { ++ dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->reset); ++ } ++ } ++ ++ if (priv->id == ID_MT7530) { ++ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); ++ if (IS_ERR(priv->core_pwr)) ++ return PTR_ERR(priv->core_pwr); ++ ++ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); ++ if (IS_ERR(priv->io_pwr)) ++ return PTR_ERR(priv->io_pwr); ++ } ++ ++ regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), ++ GFP_KERNEL); ++ if (!regmap_config) ++ return -ENOMEM; ++ ++ regmap_config->reg_bits = 16; ++ regmap_config->val_bits = 32; ++ regmap_config->reg_stride = 4; ++ regmap_config->max_register = MT7530_CREV; ++ regmap_config->disable_locking = true; ++ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, ++ priv->bus, regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ if (priv->id == ID_MT7531) { ++ ret = mt7531_create_sgmii(priv); ++ if (ret) ++ return ret; ++ } ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void ++mt7530_remove(struct mdio_device *mdiodev) ++{ ++ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int ret = 0, i; ++ ++ if (!priv) ++ return; ++ ++ ret = regulator_disable(priv->core_pwr); ++ if (ret < 0) ++ dev_err(priv->dev, ++ "Failed to disable core power: %d\n", ret); ++ ++ ret = regulator_disable(priv->io_pwr); ++ if (ret < 0) ++ dev_err(priv->dev, "Failed to disable io pwr: %d\n", ++ ret); ++ ++ mt7530_remove_common(priv); ++ ++ for (i = 0; i < 2; ++i) ++ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); ++} ++ ++static void mt7530_shutdown(struct mdio_device *mdiodev) ++{ ++ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static struct mdio_driver mt7530_mdio_driver = { ++ .probe = mt7530_probe, ++ .remove = mt7530_remove, ++ .shutdown = mt7530_shutdown, ++ .mdiodrv.driver = { ++ .name = "mt7530-mdio", ++ .of_match_table = mt7530_of_match, ++ }, ++}; ++ ++mdio_module_driver(mt7530_mdio_driver); ++ ++MODULE_AUTHOR("Sean Wang "); ++MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -192,31 +191,6 @@ core_clear(struct mt7530_priv *priv, u32 + } + + static int +-mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) +-{ +- struct mii_bus *bus = context; +- u16 page, r, lo, hi; +- int ret; +- +- page = (reg >> 6) & 0x3ff; +- r = (reg >> 2) & 0xf; +- lo = val & 0xffff; +- hi = val >> 16; +- +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); +- if (ret < 0) +- return ret; +- +- ret = bus->write(bus, 0x1f, r, lo); +- if (ret < 0) +- return ret; +- +- ret = bus->write(bus, 0x1f, 0x10, hi); +- return ret; +-} +- +-static int + mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) + { + int ret; +@@ -230,29 +204,6 @@ mt7530_mii_write(struct mt7530_priv *pri + return ret; + } + +-static int +-mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) +-{ +- struct mii_bus *bus = context; +- u16 page, r, lo, hi; +- int ret; +- +- page = (reg >> 6) & 0x3ff; +- r = (reg >> 2) & 0xf; +- +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); +- if (ret < 0) +- return ret; +- +- lo = bus->read(bus, 0x1f, r); +- hi = bus->read(bus, 0x1f, 0x10); +- +- *val = (hi << 16) | (lo & 0xffff); +- +- return 0; +-} +- + static u32 + mt7530_mii_read(struct mt7530_priv *priv, u32 reg) + { +@@ -2957,72 +2908,6 @@ static const struct phylink_pcs_ops mt75 + .pcs_an_restart = mt7530_pcs_an_restart, + }; + +-static void +-mt7530_mdio_regmap_lock(void *mdio_lock) +-{ +- mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED); +-} +- +-static void +-mt7530_mdio_regmap_unlock(void *mdio_lock) +-{ +- mutex_unlock(mdio_lock); +-} +- +-static const struct regmap_bus mt7530_regmap_bus = { +- .reg_write = mt7530_regmap_write, +- .reg_read = mt7530_regmap_read, +-}; +- +-static int +-mt7531_create_sgmii(struct mt7530_priv *priv) +-{ +- struct regmap_config *mt7531_pcs_config[2]; +- struct phylink_pcs *pcs; +- struct regmap *regmap; +- int i, ret = 0; +- +- for (i = 0; i < 2; i++) { +- mt7531_pcs_config[i] = devm_kzalloc(priv->dev, +- sizeof(struct regmap_config), +- GFP_KERNEL); +- if (!mt7531_pcs_config[i]) { +- ret = -ENOMEM; +- break; +- } +- +- mt7531_pcs_config[i]->name = i ? "port6" : "port5"; +- mt7531_pcs_config[i]->reg_bits = 16; +- mt7531_pcs_config[i]->val_bits = 32; +- mt7531_pcs_config[i]->reg_stride = 4; +- mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); +- mt7531_pcs_config[i]->max_register = 0x17c; +- mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock; +- mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; +- mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; +- +- regmap = devm_regmap_init(priv->dev, +- &mt7530_regmap_bus, priv->bus, +- mt7531_pcs_config[i]); +- if (IS_ERR(regmap)) { +- ret = PTR_ERR(regmap); +- break; +- } +- pcs = mtk_pcs_lynxi_create(priv->dev, regmap, +- MT7531_PHYA_CTRL_SIGNAL3, 0); +- if (!pcs) { +- ret = -ENXIO; +- break; +- } +- priv->ports[5 + i].sgmii_pcs = pcs; +- } +- +- if (ret && i) +- mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); +- +- return ret; +-} +- + static int + mt753x_setup(struct dsa_switch *ds) + { +@@ -3081,7 +2966,7 @@ static int mt753x_set_mac_eee(struct dsa + return 0; + } + +-static const struct dsa_switch_ops mt7530_switch_ops = { ++const struct dsa_switch_ops mt7530_switch_ops = { + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt753x_setup, + .get_strings = mt7530_get_strings, +@@ -3115,8 +3000,9 @@ static const struct dsa_switch_ops mt753 + .get_mac_eee = mt753x_get_mac_eee, + .set_mac_eee = mt753x_set_mac_eee, + }; ++EXPORT_SYMBOL_GPL(mt7530_switch_ops); + +-static const struct mt753x_info mt753x_table[] = { ++const struct mt753x_info mt753x_table[] = { + [ID_MT7621] = { + .id = ID_MT7621, + .pcs_ops = &mt7530_pcs_ops, +@@ -3149,16 +3035,9 @@ static const struct mt753x_info mt753x_t + .mac_port_config = mt7531_mac_config, + }, + }; ++EXPORT_SYMBOL_GPL(mt753x_table); + +-static const struct of_device_id mt7530_of_match[] = { +- { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, +- { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, +- { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, mt7530_of_match); +- +-static int ++int + mt7530_probe_common(struct mt7530_priv *priv) + { + struct device *dev = priv->dev; +@@ -3195,88 +3074,9 @@ mt7530_probe_common(struct mt7530_priv * + + return 0; + } ++EXPORT_SYMBOL_GPL(mt7530_probe_common); + +-static int +-mt7530_probe(struct mdio_device *mdiodev) +-{ +- static struct regmap_config *regmap_config; +- struct mt7530_priv *priv; +- struct device_node *dn; +- int ret; +- +- dn = mdiodev->dev.of_node; +- +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- +- ret = mt7530_probe_common(priv); +- if (ret) +- return ret; +- +- /* Use medatek,mcm property to distinguish hardware type that would +- * cause a little bit differences on power-on sequence. +- * Not MCM that indicates switch works as the remote standalone +- * integrated circuit so the GPIO pin would be used to complete +- * the reset, otherwise memory-mapped register accessing used +- * through syscon provides in the case of MCM. +- */ +- priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); +- if (priv->mcm) { +- dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); +- +- priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); +- if (IS_ERR(priv->rstc)) { +- dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); +- return PTR_ERR(priv->rstc); +- } +- } else { +- priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", +- GPIOD_OUT_LOW); +- if (IS_ERR(priv->reset)) { +- dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); +- return PTR_ERR(priv->reset); +- } +- } +- +- if (priv->id == ID_MT7530) { +- priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); +- if (IS_ERR(priv->core_pwr)) +- return PTR_ERR(priv->core_pwr); +- +- priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); +- if (IS_ERR(priv->io_pwr)) +- return PTR_ERR(priv->io_pwr); +- } +- +- regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), +- GFP_KERNEL); +- if (!regmap_config) +- return -ENOMEM; +- +- regmap_config->reg_bits = 16; +- regmap_config->val_bits = 32; +- regmap_config->reg_stride = 4; +- regmap_config->max_register = MT7530_CREV; +- regmap_config->disable_locking = true; +- priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, +- priv->bus, regmap_config); +- if (IS_ERR(priv->regmap)) +- return PTR_ERR(priv->regmap); +- +- if (priv->id == ID_MT7531) { +- ret = mt7531_create_sgmii(priv); +- if (ret) +- return ret; +- } +- +- return dsa_register_switch(priv->ds); +-} +- +-static void ++void + mt7530_remove_common(struct mt7530_priv *priv) + { + if (priv->irq) +@@ -3286,55 +3086,7 @@ mt7530_remove_common(struct mt7530_priv + + mutex_destroy(&priv->reg_mutex); + } +- +-static void +-mt7530_remove(struct mdio_device *mdiodev) +-{ +- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int ret = 0, i; +- +- if (!priv) +- return; +- +- ret = regulator_disable(priv->core_pwr); +- if (ret < 0) +- dev_err(priv->dev, +- "Failed to disable core power: %d\n", ret); +- +- ret = regulator_disable(priv->io_pwr); +- if (ret < 0) +- dev_err(priv->dev, "Failed to disable io pwr: %d\n", +- ret); +- +- mt7530_remove_common(priv); +- +- for (i = 0; i < 2; ++i) +- mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); +-} +- +-static void mt7530_shutdown(struct mdio_device *mdiodev) +-{ +- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); +- +- if (!priv) +- return; +- +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-static struct mdio_driver mt7530_mdio_driver = { +- .probe = mt7530_probe, +- .remove = mt7530_remove, +- .shutdown = mt7530_shutdown, +- .mdiodrv.driver = { +- .name = "mt7530", +- .of_match_table = mt7530_of_match, +- }, +-}; +- +-mdio_module_driver(mt7530_mdio_driver); ++EXPORT_SYMBOL_GPL(mt7530_remove_common); + + MODULE_AUTHOR("Sean Wang "); + MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch"); +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -807,4 +807,10 @@ static inline void INIT_MT7530_DUMMY_POL + p->reg = reg; + } + ++int mt7530_probe_common(struct mt7530_priv *priv); ++void mt7530_remove_common(struct mt7530_priv *priv); ++ ++extern const struct dsa_switch_ops mt7530_switch_ops; ++extern const struct mt753x_info mt753x_table[]; ++ + #endif /* __MT7530_H */ diff --git a/target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch b/target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch new file mode 100644 index 00000000000..01011ed1a00 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch @@ -0,0 +1,47 @@ +From a52cadbf76593f8fcb2f4f62cb006e3f2a22ad06 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:19:28 +0100 +Subject: [PATCH 11/13] net: dsa: mt7530: skip locking if MDIO bus isn't + present + +As MT7530 and MT7531 internally use 32-bit wide registers, each access +to any register of the switch requires several operations on the MDIO +bus. Hence if there is congruent access, e.g. due to PCS or PHY +polling, this can mess up and interfere with another ongoing register +access sequence. + +However, the MDIO bus mutex is only relevant for MDIO-connected +switches. Prepare switches which have there registers directly mapped +into the SoCs register space via MMIO which do not require such +locking. There we can simply use regmap's default locking mechanism. + +Hence guard mutex operations to only be performed in case of MDIO +connected switches. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -144,13 +144,15 @@ err: + static void + mt7530_mutex_lock(struct mt7530_priv *priv) + { +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ if (priv->bus) ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); + } + + static void + mt7530_mutex_unlock(struct mt7530_priv *priv) + { +- mutex_unlock(&priv->bus->mdio_lock); ++ if (priv->bus) ++ mutex_unlock(&priv->bus->mdio_lock); + } + + static void diff --git a/target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch b/target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch new file mode 100644 index 00000000000..aeaf9f84678 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch @@ -0,0 +1,421 @@ +From b361015763fedea439f13b336b15ef7bdf1f7d4f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 02:19:40 +0100 +Subject: [PATCH 12/13] net: dsa: mt7530: introduce driver for MT7988 built-in + switch + +Add driver for the built-in Gigabit Ethernet switch which can be found +in the MediaTek MT7988 SoC. + +The switch shares most of its design with MT7530 and MT7531, but has +it's registers mapped into the SoCs register space rather than being +connected externally or internally via MDIO. + +Introduce a new platform driver to support that. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + MAINTAINERS | 2 + + drivers/net/dsa/Kconfig | 12 +++ + drivers/net/dsa/Makefile | 1 + + drivers/net/dsa/mt7530-mmio.c | 101 +++++++++++++++++++++++++ + drivers/net/dsa/mt7530.c | 137 +++++++++++++++++++++++++++++++++- + drivers/net/dsa/mt7530.h | 12 +-- + 6 files changed, 255 insertions(+), 10 deletions(-) + create mode 100644 drivers/net/dsa/mt7530-mmio.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -13058,9 +13058,11 @@ MEDIATEK SWITCH DRIVER + M: Sean Wang + M: Landen Chao + M: DENG Qingfang ++M: Daniel Golle + L: netdev@vger.kernel.org + S: Maintained + F: drivers/net/dsa/mt7530-mdio.c ++F: drivers/net/dsa/mt7530-mmio.c + F: drivers/net/dsa/mt7530.* + F: net/dsa/tag_mtk.c + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -38,6 +38,7 @@ config NET_DSA_MT7530 + select NET_DSA_TAG_MTK + select MEDIATEK_GE_PHY + imply NET_DSA_MT7530_MDIO ++ imply NET_DSA_MT7530_MMIO + help + This enables support for the MediaTek MT7530 and MT7531 Ethernet + switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT, +@@ -54,6 +55,17 @@ config NET_DSA_MT7530_MDIO + module MT7530 which can be found in the MT7621AT, MT7621DAT, + MT7621ST and MT7623AI SoCs. + ++config NET_DSA_MT7530_MMIO ++ tristate "MediaTek MT7530 MMIO interface driver" ++ depends on NET_DSA_MT7530 ++ depends on HAS_IOMEM ++ help ++ This enables support for the built-in Ethernet switch found ++ in the MediaTek MT7988 SoC. ++ The switch is a similar design as MT7531, but the switch registers ++ are directly mapped into the SoCs register space rather than being ++ accessible via MDIO. ++ + config NET_DSA_MV88E6060 + tristate "Marvell 88E6060 ethernet switch chip support" + select NET_DSA_TAG_TRAILER +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -8,6 +8,7 @@ endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o + obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o ++obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o + obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o +--- /dev/null ++++ b/drivers/net/dsa/mt7530-mmio.c +@@ -0,0 +1,101 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mt7530.h" ++ ++static const struct of_device_id mt7988_of_match[] = { ++ { .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mt7988_of_match); ++ ++static int ++mt7988_probe(struct platform_device *pdev) ++{ ++ static struct regmap_config *sw_regmap_config; ++ struct mt7530_priv *priv; ++ void __iomem *base_addr; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->bus = NULL; ++ priv->dev = &pdev->dev; ++ ++ ret = mt7530_probe_common(priv); ++ if (ret) ++ return ret; ++ ++ priv->rstc = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->rstc)) { ++ dev_err(&pdev->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(priv->rstc); ++ } ++ ++ base_addr = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base_addr)) { ++ dev_err(&pdev->dev, "cannot request I/O memory space\n"); ++ return -ENXIO; ++ } ++ ++ sw_regmap_config = devm_kzalloc(&pdev->dev, sizeof(*sw_regmap_config), GFP_KERNEL); ++ if (!sw_regmap_config) ++ return -ENOMEM; ++ ++ sw_regmap_config->name = "switch"; ++ sw_regmap_config->reg_bits = 16; ++ sw_regmap_config->val_bits = 32; ++ sw_regmap_config->reg_stride = 4; ++ sw_regmap_config->max_register = MT7530_CREV; ++ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base_addr, sw_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static int ++mt7988_remove(struct platform_device *pdev) ++{ ++ struct mt7530_priv *priv = platform_get_drvdata(pdev); ++ ++ if (priv) ++ mt7530_remove_common(priv); ++ ++ return 0; ++} ++ ++static void mt7988_shutdown(struct platform_device *pdev) ++{ ++ struct mt7530_priv *priv = platform_get_drvdata(pdev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&pdev->dev, NULL); ++} ++ ++static struct platform_driver mt7988_platform_driver = { ++ .probe = mt7988_probe, ++ .remove = mt7988_remove, ++ .shutdown = mt7988_shutdown, ++ .driver = { ++ .name = "mt7530-mmio", ++ .of_match_table = mt7988_of_match, ++ }, ++}; ++module_platform_driver(mt7988_platform_driver); ++ ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MMIO)"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1989,6 +1989,47 @@ static const struct irq_domain_ops mt753 + }; + + static void ++mt7988_irq_mask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable &= ~BIT(d->hwirq); ++ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); ++} ++ ++static void ++mt7988_irq_unmask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable |= BIT(d->hwirq); ++ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); ++} ++ ++static struct irq_chip mt7988_irq_chip = { ++ .name = KBUILD_MODNAME, ++ .irq_mask = mt7988_irq_mask, ++ .irq_unmask = mt7988_irq_unmask, ++}; ++ ++static int ++mt7988_irq_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_data(irq, domain->host_data); ++ irq_set_chip_and_handler(irq, &mt7988_irq_chip, handle_simple_irq); ++ irq_set_nested_thread(irq, true); ++ irq_set_noprobe(irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mt7988_irq_domain_ops = { ++ .map = mt7988_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static void + mt7530_setup_mdio_irq(struct mt7530_priv *priv) + { + struct dsa_switch *ds = priv->ds; +@@ -2022,8 +2063,15 @@ mt7530_setup_irq(struct mt7530_priv *pri + return priv->irq ? : -EINVAL; + } + +- priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, +- &mt7530_irq_domain_ops, priv); ++ if (priv->id == ID_MT7988) ++ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, ++ &mt7988_irq_domain_ops, ++ priv); ++ else ++ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, ++ &mt7530_irq_domain_ops, ++ priv); ++ + if (!priv->irq_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; +@@ -2520,6 +2568,25 @@ static void mt7531_mac_port_get_caps(str + } + } + ++static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ phy_interface_zero(config->supported_interfaces); ++ ++ switch (port) { ++ case 0 ... 4: /* Internal phy */ ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ break; ++ ++ case 6: ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10000FD; ++ } ++} ++ + static int + mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) + { +@@ -2596,6 +2663,17 @@ static bool mt753x_is_mac_port(u32 port) + } + + static int ++mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface) ++{ ++ if (dsa_is_cpu_port(ds, port) && ++ interface == PHY_INTERFACE_MODE_INTERNAL) ++ return 0; ++ ++ return -EINVAL; ++} ++ ++static int + mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) + { +@@ -2665,7 +2743,8 @@ mt753x_phylink_mac_config(struct dsa_swi + + switch (port) { + case 0 ... 4: /* Internal phy */ +- if (state->interface != PHY_INTERFACE_MODE_GMII) ++ if (state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; + case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ +@@ -2743,7 +2822,8 @@ static void mt753x_phylink_mac_link_up(s + /* MT753x MAC works in 1G full duplex mode for all up-clocked + * variants. + */ +- if (interface == PHY_INTERFACE_MODE_TRGMII || ++ if (interface == PHY_INTERFACE_MODE_INTERNAL || ++ interface == PHY_INTERFACE_MODE_TRGMII || + (phy_interface_mode_is_8023z(interface))) { + speed = SPEED_1000; + duplex = DUPLEX_FULL; +@@ -2823,6 +2903,21 @@ mt7531_cpu_port_config(struct dsa_switch + return 0; + } + ++static int ++mt7988_cpu_port_config(struct dsa_switch *ds, int port) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ mt7530_write(priv, MT7530_PMCR_P(port), ++ PMCR_CPU_PORT_SETTING(priv->id)); ++ ++ mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, ++ PHY_INTERFACE_MODE_INTERNAL, NULL, ++ SPEED_10000, DUPLEX_FULL, true, true); ++ ++ return 0; ++} ++ + static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) + { +@@ -2968,6 +3063,27 @@ static int mt753x_set_mac_eee(struct dsa + return 0; + } + ++static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface) ++{ ++ return 0; ++} ++ ++static int mt7988_setup(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ /* Reset the switch */ ++ reset_control_assert(priv->rstc); ++ usleep_range(20, 50); ++ reset_control_deassert(priv->rstc); ++ usleep_range(20, 50); ++ ++ /* Reset the switch PHYs */ ++ mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST); ++ ++ return mt7531_setup_common(ds); ++} ++ + const struct dsa_switch_ops mt7530_switch_ops = { + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt753x_setup, +@@ -3036,6 +3152,17 @@ const struct mt753x_info mt753x_table[] + .mac_port_get_caps = mt7531_mac_port_get_caps, + .mac_port_config = mt7531_mac_config, + }, ++ [ID_MT7988] = { ++ .id = ID_MT7988, ++ .pcs_ops = &mt7530_pcs_ops, ++ .sw_setup = mt7988_setup, ++ .phy_read = mt7531_ind_phy_read, ++ .phy_write = mt7531_ind_phy_write, ++ .pad_setup = mt7988_pad_setup, ++ .cpu_port_config = mt7988_cpu_port_config, ++ .mac_port_get_caps = mt7988_mac_port_get_caps, ++ .mac_port_config = mt7988_mac_config, ++ }, + }; + EXPORT_SYMBOL_GPL(mt753x_table); + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -18,6 +18,7 @@ enum mt753x_id { + ID_MT7530 = 0, + ID_MT7621 = 1, + ID_MT7531 = 2, ++ ID_MT7988 = 3, + }; + + #define NUM_TRGMII_CTRL 5 +@@ -54,11 +55,11 @@ enum mt753x_id { + #define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16) + #define MT7531_CPU_PMAP_MASK GENMASK(7, 0) + +-#define MT753X_MIRROR_REG(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_CFC : MT7530_MFC) +-#define MT753X_MIRROR_EN(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_EN : MIRROR_EN) +-#define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + + /* Registers for BPDU and PAE frame control*/ +@@ -295,9 +296,8 @@ enum mt7530_vlan_port_acc_frm { + MT7531_FORCE_DPX | \ + MT7531_FORCE_RX_FC | \ + MT7531_FORCE_TX_FC) +-#define PMCR_FORCE_MODE_ID(id) (((id) == ID_MT7531) ? \ +- MT7531_FORCE_MODE : \ +- PMCR_FORCE_MODE) ++#define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ ++ MT7531_FORCE_MODE : PMCR_FORCE_MODE) + #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ + PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ + PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ diff --git a/target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch b/target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch new file mode 100644 index 00000000000..074472f6dcb --- /dev/null +++ b/target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch @@ -0,0 +1,118 @@ +From eb1dd407b4be7ca38166a38c56c8edf52c6a399f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 16 Apr 2023 13:08:14 +0100 +Subject: [PATCH 13/13] net: dsa: mt7530: fix support for MT7531BE +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are two variants of the MT7531 switch IC which got different +features (and pins) regarding port 5: + * MT7531AE: SGMII/1000Base-X/2500Base-X SerDes PCS + * MT7531BE: RGMII + +Moving the creation of the SerDes PCS from mt753x_setup to mt7530_probe +with commit 6de285229773 ("net: dsa: mt7530: move SGMII PCS creation +to mt7530_probe function") works fine for MT7531AE which got two +instances of mtk-pcs-lynxi, however, MT7531BE requires mt7531_pll_setup +to setup clocks before the single PCS on port 6 (usually used as CPU +port) starts to work and hence the PCS creation failed on MT7531BE. + +Fix this by introducing a pointer to mt7531_create_sgmii function in +struct mt7530_priv and call it again at the end of mt753x_setup like it +was before commit 6de285229773 ("net: dsa: mt7530: move SGMII PCS +creation to mt7530_probe function"). + +Fixes: 6de285229773 ("net: dsa: mt7530: move SGMII PCS creation to mt7530_probe function") +Signed-off-by: Daniel Golle +Acked-by: Arınç ÜNAL +Link: https://lore.kernel.org/r/ZDvlLhhqheobUvOK@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530-mdio.c | 16 ++++++++-------- + drivers/net/dsa/mt7530.c | 6 ++++++ + drivers/net/dsa/mt7530.h | 4 ++-- + 3 files changed, 16 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -81,14 +81,17 @@ static const struct regmap_bus mt7530_re + }; + + static int +-mt7531_create_sgmii(struct mt7530_priv *priv) ++mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii) + { +- struct regmap_config *mt7531_pcs_config[2]; ++ struct regmap_config *mt7531_pcs_config[2] = {}; + struct phylink_pcs *pcs; + struct regmap *regmap; + int i, ret = 0; + +- for (i = 0; i < 2; i++) { ++ /* MT7531AE has two SGMII units for port 5 and port 6 ++ * MT7531BE has only one SGMII unit for port 6 ++ */ ++ for (i = dual_sgmii ? 0 : 1; i < 2; i++) { + mt7531_pcs_config[i] = devm_kzalloc(priv->dev, + sizeof(struct regmap_config), + GFP_KERNEL); +@@ -208,11 +211,8 @@ mt7530_probe(struct mdio_device *mdiodev + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + +- if (priv->id == ID_MT7531) { +- ret = mt7531_create_sgmii(priv); +- if (ret) +- return ret; +- } ++ if (priv->id == ID_MT7531) ++ priv->create_sgmii = mt7531_create_sgmii; + + return dsa_register_switch(priv->ds); + } +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3030,6 +3030,12 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + ++ if (priv->create_sgmii) { ++ ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv)); ++ if (ret && priv->irq) ++ mt7530_free_irq(priv); ++ } ++ + return ret; + } + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -741,10 +741,10 @@ struct mt753x_info { + * registers + * @p6_interface Holding the current port 6 interface + * @p5_intf_sel: Holding the current port 5 interface select +- * + * @irq: IRQ number of the switch + * @irq_domain: IRQ domain of the switch irq_chip + * @irq_enable: IRQ enable bits, synced to SYS_INT_EN ++ * @create_sgmii: Pointer to function creating SGMII PCS instance(s) + */ + struct mt7530_priv { + struct device *dev; +@@ -763,7 +763,6 @@ struct mt7530_priv { + unsigned int p5_intf_sel; + u8 mirror_rx; + u8 mirror_tx; +- + struct mt7530_port ports[MT7530_NUM_PORTS]; + struct mt753x_pcs pcs[MT7530_NUM_PORTS]; + /* protect among processes for registers access*/ +@@ -771,6 +770,7 @@ struct mt7530_priv { + int irq; + struct irq_domain *irq_domain; + u32 irq_enable; ++ int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii); + }; + + struct mt7530_hw_vlan_entry { diff --git a/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch index df2f65d4334..ddb32385e4e 100644 --- a/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch +++ b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch @@ -21,7 +21,7 @@ Signed-off-by: David S. Miller --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -13959,6 +13959,7 @@ F: include/uapi/linux/meye.h +@@ -13962,6 +13962,7 @@ F: include/uapi/linux/meye.h MOTORCOMM PHY DRIVER M: Peter Geis diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15 index 934af3a7681..340175f00dd 100644 --- a/target/linux/generic/config-5.15 +++ b/target/linux/generic/config-5.15 @@ -2077,7 +2077,6 @@ CONFIG_FORTIFY_SOURCE=y # CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set # CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set # CONFIG_FRAME_POINTER is not set -CONFIG_FRAME_WARN=1024 # CONFIG_FREEZER is not set # CONFIG_FRONTSWAP is not set # CONFIG_FSCACHE is not set @@ -5231,6 +5230,8 @@ CONFIG_RXKAD=y # CONFIG_SATA_AHCI is not set # CONFIG_SATA_AHCI_PLATFORM is not set # CONFIG_SATA_DWC is not set +# CONFIG_SATA_DWC_DEBUG is not set +# CONFIG_SATA_DWC_OLD_DMA is not set # CONFIG_SATA_FSL is not set # CONFIG_SATA_HIGHBANK is not set # CONFIG_SATA_HOST is not set diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1 index 6a4913535d7..18d4ec0bd88 100644 --- a/target/linux/generic/config-6.1 +++ b/target/linux/generic/config-6.1 @@ -150,6 +150,7 @@ CONFIG_AF_UNIX_OOB=y # CONFIG_AHCI_BRCM is not set # CONFIG_AHCI_CEVA is not set # CONFIG_AHCI_DWC is not set +# CONFIG_SATA_DWC_OLD_DMA is not set # CONFIG_AHCI_IMX is not set # CONFIG_AHCI_MVEBU is not set # CONFIG_AHCI_QORIQ is not set @@ -355,8 +356,13 @@ CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 # CONFIG_ARM64_ERRATUM_1530923 is not set # CONFIG_ARM64_ERRATUM_1542419 is not set # CONFIG_ARM64_ERRATUM_1742098 is not set +# CONFIG_ARM64_ERRATUM_2051678 is not set +# CONFIG_ARM64_ERRATUM_2054223 is not set +# CONFIG_ARM64_ERRATUM_2067961 is not set +# CONFIG_ARM64_ERRATUM_2077057 is not set # CONFIG_ARM64_ERRATUM_2441007 is not set # CONFIG_ARM64_ERRATUM_2441009 is not set +# CONFIG_ARM64_ERRATUM_2658417 is not set # CONFIG_ARM64_ERRATUM_819472 is not set # CONFIG_ARM64_ERRATUM_824069 is not set # CONFIG_ARM64_ERRATUM_826319 is not set @@ -378,6 +384,7 @@ CONFIG_ARM64_MODULE_PLTS=y # CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set # CONFIG_ARM64_RAS_EXTN is not set # CONFIG_ARM64_RELOC_TEST is not set +# CONFIG_ARM64_SME is not set # CONFIG_ARM64_SVE is not set CONFIG_ARM64_SW_TTBR0_PAN=y # CONFIG_ARM64_TLB_RANGE is not set @@ -598,7 +605,9 @@ CONFIG_BASE_SMALL=0 # CONFIG_BCM54140_PHY is not set # CONFIG_BCM63XX is not set # CONFIG_BCM63XX_PHY is not set +# CONFIG_BCM7038_L1_IRQ is not set # CONFIG_BCM7038_WDT is not set +# CONFIG_BCM7120_L2_IRQ is not set # CONFIG_BCM7XXX_PHY is not set # CONFIG_BCM84881_PHY is not set # CONFIG_BCM87XX_PHY is not set @@ -766,6 +775,7 @@ CONFIG_BRANCH_PROFILE_NONE=y # CONFIG_BRCMFMAC is not set # CONFIG_BRCMSMAC is not set # CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_BRCMSTB_L2_IRQ is not set CONFIG_BRIDGE=y # CONFIG_BRIDGE_CFM is not set # CONFIG_BRIDGE_EBT_802_3 is not set @@ -1295,6 +1305,7 @@ CONFIG_CRYPTO_SKCIPHER2=y # CONFIG_CRYPTO_SM3 is not set # CONFIG_CRYPTO_SM3_ARM64_CE is not set # CONFIG_CRYPTO_SM3_GENERIC is not set +# CONFIG_CRYPTO_SM3_NEON is not set # CONFIG_CRYPTO_SM4 is not set # CONFIG_CRYPTO_SM4_ARM64_CE is not set # CONFIG_CRYPTO_SM4_GENERIC is not set @@ -1592,13 +1603,18 @@ CONFIG_DQL=y # CONFIG_DRM_I2C_NXP_TDA998X is not set # CONFIG_DRM_I2C_SIL164 is not set # CONFIG_DRM_I915 is not set +# CONFIG_DRM_ITE_IT6505 is not set +# CONFIG_DRM_IMX_LCDIF is not set +# CONFIG_DRM_ITE_IT6505 is not set # CONFIG_DRM_ITE_IT66121 is not set # CONFIG_DRM_KOMEDA is not set # CONFIG_DRM_LEGACY is not set # CONFIG_DRM_LIB_RANDOM is not set # CONFIG_DRM_LIMA is not set # CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_LOGICVC is not set # CONFIG_DRM_LONTIUM_LT8912B is not set +# CONFIG_DRM_LONTIUM_LT9211 is not set # CONFIG_DRM_LONTIUM_LT9611 is not set # CONFIG_DRM_LONTIUM_LT9611UXC is not set # CONFIG_DRM_LVDS_CODEC is not set @@ -1618,6 +1634,7 @@ CONFIG_DQL=y # CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set # CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set # CONFIG_DRM_PANEL_DSI_CM is not set +# CONFIG_DRM_PANEL_EDP is not set # CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set # CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set # CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set @@ -1638,6 +1655,7 @@ CONFIG_DQL=y # CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set # CONFIG_DRM_PANEL_MIPI_DBI is not set # CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set +# CONFIG_DRM_PANEL_NEWVISION_NV3052C is not set # CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set # CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set # CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set @@ -1654,6 +1672,7 @@ CONFIG_DQL=y # CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set # CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D27A1 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set @@ -1688,11 +1707,15 @@ CONFIG_DQL=y # CONFIG_DRM_RADEON_USERPTR is not set # CONFIG_DRM_RCAR_DW_HDMI is not set # CONFIG_DRM_RCAR_LVDS is not set +# CONFIG_DRM_RCAR_USE_LVDS is not set +# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set +# CONFIG_DRM_ROCKCHIP is not set # CONFIG_DRM_SII902X is not set # CONFIG_DRM_SII9234 is not set # CONFIG_DRM_SIL_SII8620 is not set # CONFIG_DRM_SIMPLEDRM is not set # CONFIG_DRM_SIMPLE_BRIDGE is not set +# CONFIG_DRM_SSD130X is not set # CONFIG_DRM_STI is not set # CONFIG_DRM_STM is not set # CONFIG_DRM_SUN4I is not set @@ -1700,6 +1723,7 @@ CONFIG_DQL=y # CONFIG_DRM_TIDSS is not set # CONFIG_DRM_TILCDC is not set # CONFIG_DRM_TINYDRM is not set +# CONFIG_DRM_TI_DLPC3433 is not set # CONFIG_DRM_TI_SN65DSI83 is not set # CONFIG_DRM_TI_SN65DSI86 is not set # CONFIG_DRM_TI_TFP410 is not set @@ -2132,7 +2156,6 @@ CONFIG_FORTIFY_SOURCE=y # CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set # CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set # CONFIG_FRAME_POINTER is not set -CONFIG_FRAME_WARN=1024 # CONFIG_FREEZER is not set # CONFIG_FRONTSWAP is not set # CONFIG_FSCACHE is not set @@ -2158,6 +2181,7 @@ CONFIG_FSNOTIFY=y # CONFIG_FTMAC100 is not set # CONFIG_FTRACE is not set # CONFIG_FTRACE_RECORD_RECURSION is not set +# CONFIG_FTRACE_SORT_STARTUP_TEST is not set # CONFIG_FTRACE_STARTUP_TEST is not set # CONFIG_FTR_FIXUP_SELFTEST is not set # CONFIG_FTWDT010_WATCHDOG is not set @@ -2461,12 +2485,15 @@ CONFIG_HIGH_RES_TIMERS=y # CONFIG_HISI_DMA is not set # CONFIG_HISI_FEMAC is not set # CONFIG_HISI_HIKEY_USB is not set +# CONFIG_HISI_PCIE_PMU is not set +# CONFIG_HISI_PTT is not set # CONFIG_HIST_TRIGGERS_DEBUG is not set # CONFIG_HIX5HD2_GMAC is not set # CONFIG_HMC425 is not set # CONFIG_HMC6352 is not set # CONFIG_HNS is not set # CONFIG_HNS3 is not set +# CONFIG_HNS3_PMU is not set # CONFIG_HNS_DSAF is not set # CONFIG_HNS_ENET is not set # CONFIG_HOSTAP is not set @@ -3848,6 +3875,10 @@ CONFIG_MTD_MAP_BANK_WIDTH_4=y # CONFIG_MTD_NAND_BCH is not set # CONFIG_MTD_NAND_BF5XX is not set # CONFIG_MTD_NAND_BRCMNAND is not set +# CONFIG_MTD_NAND_BRCMNAND_BCM63XX is not set +# CONFIG_MTD_NAND_BRCMNAND_BCMBCA is not set +# CONFIG_MTD_NAND_BRCMNAND_BRCMSTB is not set +# CONFIG_MTD_NAND_BRCMNAND_IPROC is not set # CONFIG_MTD_NAND_CADENCE is not set # CONFIG_MTD_NAND_CAFE is not set # CONFIG_MTD_NAND_CM_X270 is not set @@ -4088,6 +4119,7 @@ CONFIG_NETDEVICES=y # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set # CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETFS_STATS is not set # CONFIG_NETLABEL is not set # CONFIG_NETLINK_DIAG is not set # CONFIG_NETLINK_MMAP is not set @@ -4607,6 +4639,7 @@ CONFIG_PACKET=y CONFIG_PAGE_SIZE_4KB=y # CONFIG_PAGE_SIZE_64KB is not set # CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PAGE_TABLE_CHECK is not set # CONFIG_PALMAS_GPADC is not set # CONFIG_PANASONIC_LAPTOP is not set # CONFIG_PANEL is not set @@ -4917,6 +4950,7 @@ CONFIG_PPP_MULTILINK=y # CONFIG_PREEMPT is not set # CONFIG_PREEMPTIRQ_DELAY_TEST is not set # CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_DYNAMIC is not set CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT_TRACER is not set # CONFIG_PREEMPT_VOLUNTARY is not set @@ -5053,6 +5087,7 @@ CONFIG_PWRSEQ_SIMPLE=y # CONFIG_RALINK is not set # CONFIG_RANDOM32_SELFTEST is not set # CONFIG_RANDOMIZE_BASE is not set +CONFIG_RANDOMIZE_KSTACK_OFFSET=y # CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set CONFIG_RANDOM_TRUST_BOOTLOADER=y CONFIG_RANDOM_TRUST_CPU=y @@ -5347,6 +5382,7 @@ CONFIG_RTC_SYSTOHC_DEVICE="rtc0" CONFIG_RT_MUTEXES=y # CONFIG_RUNTIME_DEBUG is not set CONFIG_RUNTIME_TESTING_MENU=y +# CONFIG_RV is not set CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_RXKAD=y # CONFIG_S2IO is not set @@ -5387,6 +5423,7 @@ CONFIG_SBITMAP=y # CONFIG_SCF_TORTURE_TEST is not set # CONFIG_SCHEDSTATS is not set # CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SCHED_CLUSTER is not set # CONFIG_SCHED_DEBUG is not set CONFIG_SCHED_HRTICK=y # CONFIG_SCHED_MC is not set @@ -5857,6 +5894,7 @@ CONFIG_SERIAL_EARLYCON=y # CONFIG_SGI_PARTITION is not set # CONFIG_SG_POOL is not set # CONFIG_SG_SPLIT is not set +# CONFIG_SHADOW_CALL_STACK is not set CONFIG_SHMEM=y # CONFIG_SHRINKER_DEBUG is not set # CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set @@ -6065,6 +6103,7 @@ CONFIG_SND_PROC_FS=y # CONFIG_SND_SBAWE_SEQ is not set # CONFIG_SND_SE6X is not set # CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_GENERIC is not set # CONFIG_SND_SERIAL_U16550 is not set # CONFIG_SND_SIMPLE_CARD is not set # CONFIG_SND_SIMPLE_SCU_CARD is not set @@ -6257,6 +6296,8 @@ CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y # CONFIG_SND_SOC_PCM512x_SPI is not set # CONFIG_SND_SOC_QCOM is not set # CONFIG_SND_SOC_RK3328 is not set +# CONFIG_SND_SOC_RK817 is not set +# CONFIG_SND_SOC_ROCKCHIP is not set # CONFIG_SND_SOC_RT5616 is not set # CONFIG_SND_SOC_RT5631 is not set # CONFIG_SND_SOC_RT5640 is not set @@ -6639,6 +6680,7 @@ CONFIG_TCP_CONG_CUBIC=y # CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set # CONFIG_TEST_DEBUG_VIRTUAL is not set # CONFIG_TEST_DIV64 is not set +# CONFIG_TEST_DYNAMIC_DEBUG is not set # CONFIG_TEST_FIRMWARE is not set # CONFIG_TEST_FREE_PAGES is not set # CONFIG_TEST_HASH is not set @@ -6710,6 +6752,7 @@ CONFIG_TIMERFD=y # CONFIG_TIMER_STATS is not set # CONFIG_TIME_NS is not set # CONFIG_TINYDRM_HX8357D is not set +# CONFIG_TINYDRM_ILI9163 is not set # CONFIG_TINYDRM_ILI9225 is not set # CONFIG_TINYDRM_ILI9341 is not set # CONFIG_TINYDRM_ILI9486 is not set @@ -6903,6 +6946,7 @@ CONFIG_TMPFS_XATTR=y # CONFIG_TRACE_EVAL_MAP_FILE is not set # CONFIG_TRACE_EVENT_INJECT is not set CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_TRACE_MMIO_ACCESS is not set # CONFIG_TRACE_SINK is not set # CONFIG_TRACING_EVENTS_GPIO is not set CONFIG_TRACING_SUPPORT=y @@ -6933,6 +6977,7 @@ CONFIG_TTY=y # CONFIG_TWL6040_CORE is not set # CONFIG_TXGBE is not set # CONFIG_TYPEC is not set +# CONFIG_TYPEC_DP_ALTMODE is not set # CONFIG_TYPEC_TCPM is not set # CONFIG_TYPEC_UCSI is not set # CONFIG_TYPHOON is not set @@ -7668,15 +7713,15 @@ CONFIG_WATCHDOG_OPEN_TIMEOUT=0 # CONFIG_WDAT_WDT is not set # CONFIG_WDTPCI is not set CONFIG_WERROR=y -CONFIG_WEXT_CORE=y -CONFIG_WEXT_PRIV=y -CONFIG_WEXT_PROC=y -CONFIG_WEXT_SPY=y +# CONFIG_WEXT_CORE is not set +# CONFIG_WEXT_PRIV is not set +# CONFIG_WEXT_PROC is not set +# CONFIG_WEXT_SPY is not set CONFIG_WILINK_PLATFORM_DATA=y # CONFIG_WIMAX is not set # CONFIG_WIREGUARD is not set CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y +# CONFIG_WIRELESS_EXT is not set # CONFIG_WIRELESS_WDS is not set # CONFIG_WIZNET_W5100 is not set # CONFIG_WIZNET_W5300 is not set diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c index a734e576080..5fa2b147c6f 100644 --- a/target/linux/generic/files/drivers/net/phy/swconfig.c +++ b/target/linux/generic/files/drivers/net/phy/swconfig.c @@ -1054,6 +1054,9 @@ static struct genl_family switch_fam = { .module = THIS_MODULE, .ops = swconfig_ops, .n_ops = ARRAY_SIZE(swconfig_ops), +#if LINUX_VERSION_CODE > KERNEL_VERSION(6,0,0) + .resv_start_op = SWITCH_CMD_SET_VLAN + 1, +#endif }; #ifdef CONFIG_OF diff --git a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch index dbdb141c6dc..2593f7c0fcb 100644 --- a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch +++ b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch @@ -116,7 +116,7 @@ Signed-off-by: Felix Fietkau help --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -3590,6 +3590,11 @@ static int xmit_one(struct sk_buff *skb, +@@ -3592,6 +3592,11 @@ static int xmit_one(struct sk_buff *skb, if (dev_nit_active(dev)) dev_queue_xmit_nit(skb, dev); diff --git a/target/linux/generic/pending-5.15/160-workqueue-fix-enum-type-for-gcc-13.patch b/target/linux/generic/pending-5.15/160-workqueue-fix-enum-type-for-gcc-13.patch new file mode 100644 index 00000000000..82076121ac3 --- /dev/null +++ b/target/linux/generic/pending-5.15/160-workqueue-fix-enum-type-for-gcc-13.patch @@ -0,0 +1,44 @@ +From 525ff9c2965770762b81d679820552a208070d59 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 17 Jan 2023 17:40:35 +0100 +Subject: workqueue: fix enum type for gcc-13 + +In gcc-13, the WORK_STRUCT_WQ_DATA_MASK constant is a signed 64-bit +type on 32-bit architectures because the enum definition has both +negative numbers and numbers above LONG_MAX in it: + +kernel/workqueue.c: In function 'get_work_pwq': +kernel/workqueue.c:709:24: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 709 | return (void *)(data & WORK_STRUCT_WQ_DATA_MASK); + | ^ +kernel/workqueue.c: In function 'get_work_pool': +kernel/workqueue.c:737:25: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 737 | return ((struct pool_workqueue *) + | ^ +kernel/workqueue.c: In function 'get_work_pool_id': +kernel/workqueue.c:759:25: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 759 | return ((struct pool_workqueue *) + | ^ + +Change the enum definition to ensure all values can fit into +the range of 'unsigned long' on all architectures. + +Signed-off-by: Arnd Bergmann +Tested-by: Thierry Reding +Tested-by: Lai Jiangshan +Signed-off-by: Tejun Heo +--- + include/linux/workqueue.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/workqueue.h ++++ b/include/linux/workqueue.h +@@ -83,7 +83,7 @@ enum { + + /* convenience constants */ + WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1, +- WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK, ++ WORK_STRUCT_WQ_DATA_MASK = (unsigned long)~WORK_STRUCT_FLAG_MASK, + WORK_STRUCT_NO_POOL = (unsigned long)WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT, + + /* bit mask for work_busy() return values */ diff --git a/target/linux/generic/pending-5.15/630-packet_socket_type.patch b/target/linux/generic/pending-5.15/630-packet_socket_type.patch index 2d2c7b6cc55..b95ceebac76 100644 --- a/target/linux/generic/pending-5.15/630-packet_socket_type.patch +++ b/target/linux/generic/pending-5.15/630-packet_socket_type.patch @@ -55,7 +55,7 @@ Signed-off-by: Felix Fietkau goto out; if (!net_eq(dev_net(dev), sock_net(sk))) -@@ -2093,12 +2095,12 @@ static int packet_rcv(struct sk_buff *sk +@@ -2091,12 +2093,12 @@ static int packet_rcv(struct sk_buff *sk unsigned int snaplen, res; bool is_drop_n_account = false; @@ -71,7 +71,7 @@ Signed-off-by: Felix Fietkau if (!net_eq(dev_net(dev), sock_net(sk))) goto drop; -@@ -2224,12 +2226,12 @@ static int tpacket_rcv(struct sk_buff *s +@@ -2222,12 +2224,12 @@ static int tpacket_rcv(struct sk_buff *s BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32); BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48); @@ -87,7 +87,7 @@ Signed-off-by: Felix Fietkau if (!net_eq(dev_net(dev), sock_net(sk))) goto drop; -@@ -3342,6 +3344,7 @@ static int packet_create(struct net *net +@@ -3340,6 +3342,7 @@ static int packet_create(struct net *net mutex_init(&po->pg_vec_lock); po->rollover = NULL; po->prot_hook.func = packet_rcv; @@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau if (sock->type == SOCK_PACKET) po->prot_hook.func = packet_rcv_spkt; -@@ -3979,6 +3982,16 @@ packet_setsockopt(struct socket *sock, i +@@ -3977,6 +3980,16 @@ packet_setsockopt(struct socket *sock, i WRITE_ONCE(po->xmit, val ? packet_direct_xmit : dev_queue_xmit); return 0; } @@ -112,7 +112,7 @@ Signed-off-by: Felix Fietkau default: return -ENOPROTOOPT; } -@@ -4035,6 +4048,13 @@ static int packet_getsockopt(struct sock +@@ -4033,6 +4046,13 @@ static int packet_getsockopt(struct sock case PACKET_VNET_HDR: val = po->has_vnet_hdr; break; diff --git a/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index 091e65f31e0..e56fd325123 100644 --- a/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau __u8 inner_protocol_type:1; --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -6065,6 +6065,9 @@ static enum gro_result dev_gro_receive(s +@@ -6067,6 +6067,9 @@ static enum gro_result dev_gro_receive(s int same_flow; int grow; @@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau if (netif_elide_gro(skb->dev)) goto normal; -@@ -8079,6 +8082,48 @@ static void __netdev_adjacent_dev_unlink +@@ -8081,6 +8084,48 @@ static void __netdev_adjacent_dev_unlink &upper_dev->adj_list.lower); } @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, void *upper_priv, void *upper_info, -@@ -8130,6 +8175,7 @@ static int __netdev_upper_dev_link(struc +@@ -8132,6 +8177,7 @@ static int __netdev_upper_dev_link(struc if (ret) return ret; @@ -99,7 +99,7 @@ Signed-off-by: Felix Fietkau ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); ret = notifier_to_errno(ret); -@@ -8226,6 +8272,7 @@ static void __netdev_upper_dev_unlink(st +@@ -8228,6 +8274,7 @@ static void __netdev_upper_dev_unlink(st __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); @@ -107,7 +107,7 @@ Signed-off-by: Felix Fietkau call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); -@@ -9045,6 +9092,7 @@ int dev_set_mac_address(struct net_devic +@@ -9047,6 +9094,7 @@ int dev_set_mac_address(struct net_devic if (err) return err; dev->addr_assign_type = NET_ADDR_SET; diff --git a/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch index 68170b6614b..0185bed0897 100644 --- a/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch +++ b/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -1,23 +1,40 @@ -From 20ac14fedba025b6b336a821ea60660afe2d46cd Mon Sep 17 00:00:00 2001 +From 3d833ad2cfc1ab503d9aae2967b7f10811bb3c9c Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Wed, 1 Mar 2023 11:56:04 +0000 -Subject: [PATCH 7/7] net: ethernet: mtk_eth_soc: add paths and SerDes modes +Subject: [PATCH 7/7] net: ethernet: mtk_eth_soc: add paths and SerDes modes for MT7988 -MT7988 comes with a built-in 2.5G PHY as well as USXGMII or 10Base-KR -compatible SerDes lanes for external PHYs. +MT7988 comes with a built-in 2.5G PHY as well as +USXGMII/10GBase-KR/5GBase-KR compatible SerDes lanes for external PHYs. Add support for configuring the MAC and SerDes parts for the new paths. Signed-off-by: Daniel Golle --- - drivers/net/ethernet/mediatek/Makefile | 2 +- - drivers/net/ethernet/mediatek/mtk_eth_path.c | 154 ++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 291 +++++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.h | 162 ++++- - drivers/net/ethernet/mediatek/mtk_usxgmii.c | 659 +++++++++++++++++++ - 5 files changed, 1236 insertions(+), 32 deletions(-) + drivers/net/ethernet/mediatek/Kconfig | 7 + + drivers/net/ethernet/mediatek/Makefile | 1 + + drivers/net/ethernet/mediatek/mtk_eth_path.c | 154 +++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 270 +++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 194 ++++- + drivers/net/ethernet/mediatek/mtk_usxgmii.c | 835 +++++++++++++++++++ + 6 files changed, 1428 insertions(+), 33 deletions(-) create mode 100644 drivers/net/ethernet/mediatek/mtk_usxgmii.c +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -24,6 +24,13 @@ config NET_MEDIATEK_SOC + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. + ++config NET_MEDIATEK_SOC_USXGMII ++ bool "Support USXGMII SerDes on MT7988" ++ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST ++ def_bool NET_MEDIATEK_SOC != n ++ help ++ Include support for 10G SerDes which can be found on MT7988. ++ + config NET_MEDIATEK_STAR_EMAC + tristate "MediaTek STAR Ethernet MAC support" + select PHYLIB --- a/drivers/net/ethernet/mediatek/Makefile +++ b/drivers/net/ethernet/mediatek/Makefile @@ -5,6 +5,7 @@ @@ -291,7 +308,20 @@ Signed-off-by: Daniel Golle static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { -@@ -462,7 +479,7 @@ static void mtk_mac_config(struct phylin +@@ -451,6 +468,12 @@ static struct phylink_pcs *mtk_mac_selec + 0 : mac->id; + + return eth->sgmii_pcs[sid]; ++ } else if ((interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GKR || ++ interface == PHY_INTERFACE_MODE_5GBASER) && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ mac->id != MTK_GMAC1_ID) { ++ return mtk_usxgmii_select_pcs(eth, mac->id); + } + + return NULL; +@@ -462,7 +485,7 @@ static void mtk_mac_config(struct phylin struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); struct mtk_eth *eth = mac->hw; @@ -300,7 +330,7 @@ Signed-off-by: Daniel Golle u32 i; /* MT76x8 has no hardware settings between for the MAC */ -@@ -506,6 +523,23 @@ static void mtk_mac_config(struct phylin +@@ -506,6 +529,23 @@ static void mtk_mac_config(struct phylin goto init_err; } break; @@ -324,7 +354,7 @@ Signed-off-by: Daniel Golle default: goto err_phy; } -@@ -584,14 +618,92 @@ static void mtk_mac_config(struct phylin +@@ -584,14 +624,78 @@ static void mtk_mac_config(struct phylin SYSCFG0_SGMII_MASK, ~(u32)SYSCFG0_SGMII_MASK); @@ -338,26 +368,14 @@ Signed-off-by: Daniel Golle + } /* Save the syscfg0 value for mac_finish */ mac->syscfg0 = val; -+ } else if (state->interface == PHY_INTERFACE_MODE_USXGMII || -+ state->interface == PHY_INTERFACE_MODE_10GKR || -+ state->interface == PHY_INTERFACE_MODE_5GBASER) { -+ -+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { -+ err = -EINVAL; -+ goto init_err; -+ } -+ if (phylink_autoneg_inband(mode)) -+ err = mtk_usxgmii_setup_mode_force(eth, mac->id, -+ state); -+ else -+ err = mtk_usxgmii_setup_mode_an(eth, mac->id, -+ SPEED_10000); -+ -+ if (err) -+ goto init_err; - } else if (phylink_autoneg_inband(mode)) { +- } else if (phylink_autoneg_inband(mode)) { ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GKR && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { dev_err(eth->dev, - "In-band mode not supported in non SGMII mode!\n"); +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); return; } @@ -417,7 +435,7 @@ Signed-off-by: Daniel Golle return; err_phy: -@@ -632,11 +744,37 @@ static int mtk_mac_finish(struct phylink +@@ -632,11 +736,40 @@ static int mtk_mac_finish(struct phylink return 0; } @@ -430,7 +448,12 @@ Signed-off-by: Daniel Golle + if (mac->id == MTK_GMAC2_ID) + sts = sts >> 16; + -+ state->duplex = 1; ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); ++ if (!state->link) ++ return; ++ ++ state->duplex = DUPLEX_FULL; ++ state->interface = mac->interface; + + switch (FIELD_GET(MTK_USXGMII_PCS_MODE, sts)) { + case 0: @@ -446,8 +469,6 @@ Signed-off-by: Daniel Golle + state->speed = SPEED_1000; + break; + } -+ -+ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); +} + +static void mtk_gdm_pcs_get_state(struct mtk_mac *mac, @@ -458,7 +479,7 @@ Signed-off-by: Daniel Golle u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id)); state->link = (pmsr & MAC_MSR_LINK); -@@ -664,15 +802,35 @@ static void mtk_mac_pcs_get_state(struct +@@ -664,15 +797,35 @@ static void mtk_mac_pcs_get_state(struct state->pause |= MLO_PAUSE_TX; } @@ -497,7 +518,7 @@ Signed-off-by: Daniel Golle } static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, -@@ -744,13 +902,11 @@ static void mtk_set_queue_speed(struct m +@@ -744,13 +897,11 @@ static void mtk_set_queue_speed(struct m mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); } @@ -515,7 +536,7 @@ Signed-off-by: Daniel Golle u32 mcr; mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); -@@ -784,6 +940,47 @@ static void mtk_mac_link_up(struct phyli +@@ -784,6 +935,47 @@ static void mtk_mac_link_up(struct phyli mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } @@ -563,7 +584,7 @@ Signed-off-by: Daniel Golle static const struct phylink_mac_ops mtk_phylink_ops = { .validate = phylink_generic_validate, .mac_select_pcs = mtk_mac_select_pcs, -@@ -836,10 +1033,21 @@ static int mtk_mdio_init(struct mtk_eth +@@ -836,10 +1028,21 @@ static int mtk_mdio_init(struct mtk_eth } divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); @@ -586,7 +607,7 @@ Signed-off-by: Daniel Golle mtk_w32(eth, val, MTK_PPSC); dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); -@@ -4433,8 +4641,8 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4433,8 +4636,8 @@ static int mtk_add_mac(struct mtk_eth *e const __be32 *_id = of_get_property(np, "reg", NULL); phy_interface_t phy_mode; struct phylink *phylink; @@ -596,7 +617,7 @@ Signed-off-by: Daniel Golle int txqs = 1; if (!_id) { -@@ -4525,6 +4733,32 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4525,6 +4728,32 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.supported_interfaces); } @@ -629,40 +650,20 @@ Signed-off-by: Daniel Golle phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, &mtk_phylink_ops); -@@ -4714,6 +4948,33 @@ static int mtk_probe(struct platform_dev - return err; - } +@@ -4712,6 +4941,13 @@ static int mtk_probe(struct platform_dev -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { -+ eth->xgmii = devm_kzalloc(eth->dev, sizeof(*eth->xgmii), GFP_KERNEL); -+ err = mtk_usxgmii_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "usxgmii init failed\n"); -+ return err; -+ } -+ -+ err = mtk_xfi_pextp_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "pextp init failed\n"); -+ return err; -+ } -+ -+ err = mtk_xfi_pll_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "xfi pll init failed\n"); -+ return err; -+ } -+ -+ err = mtk_toprgu_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "toprgu init failed\n"); -+ return err; -+ } + if (err) + return err; + } + ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { ++ err = mtk_usxgmii_init(eth); ++ ++ if (err) ++ return err; + } + if (eth->soc->required_pctl) { - eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "mediatek,pctl"); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -126,6 +126,11 @@ @@ -743,34 +744,54 @@ Signed-off-by: Daniel Golle /* ethernet subsystem clock register */ -@@ -506,16 +548,69 @@ +@@ -506,16 +548,91 @@ #define ETHSYS_DMA_AG_MAP_QDMA BIT(1) #define ETHSYS_DMA_AG_MAP_PPE BIT(2) +/* USXGMII subsystem config registers */ +/* Register to control speed */ +#define RG_PHY_TOP_SPEED_CTRL1 0x80C -+#define RG_USXGMII_RATE_UPDATE_MODE BIT(31) -+#define RG_MAC_CK_GATED BIT(29) -+#define RG_IF_FORCE_EN BIT(28) -+#define RG_RATE_ADAPT_MODE GENMASK(10, 8) -+#define RG_RATE_ADAPT_MODE_X1 0 -+#define RG_RATE_ADAPT_MODE_X2 1 -+#define RG_RATE_ADAPT_MODE_X4 2 -+#define RG_RATE_ADAPT_MODE_X10 3 -+#define RG_RATE_ADAPT_MODE_X100 4 -+#define RG_RATE_ADAPT_MODE_X5 5 -+#define RG_RATE_ADAPT_MODE_X50 6 -+#define RG_XFI_RX_MODE GENMASK(6, 4) -+#define RG_XFI_RX_MODE_10G 0 -+#define RG_XFI_RX_MODE_5G 1 -+#define RG_XFI_TX_MODE GENMASK(2, 0) -+#define RG_XFI_TX_MODE_10G 0 -+#define RG_XFI_TX_MODE_5G 1 ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_RX_MODE_10G 0 ++#define USXGMII_XFI_RX_MODE_5G 1 ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_TX_MODE_10G 0 ++#define USXGMII_XFI_TX_MODE_5G 1 + +/* Register to control PCS AN */ +#define RG_PCS_AN_CTRL0 0x810 -+#define RG_AN_ENABLE BIT(0) ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA_SPEED_MASK GENMASK(11, 9) ++#define USXGMII_LPA_SPEED_10 0 ++#define USXGMII_LPA_SPEED_100 1 ++#define USXGMII_LPA_SPEED_1000 2 ++#define USXGMII_LPA_SPEED_10000 3 ++#define USXGMII_LPA_SPEED_2500 4 ++#define USXGMII_LPA_SPEED_5000 5 ++#define USXGMII_LPA_DUPLEX BIT(12) ++#define USXGMII_LPA_LINK BIT(15) ++#define USXGMII_LPA_LATCH BIT(31) + +/* Register to control USXGMII XFI PLL digital */ +#define XFI_PLL_DIG_GLB8 0x08 @@ -791,6 +812,8 @@ Signed-off-by: Daniel Golle +#define SWSYSRST_XFI_PLL_GRST BIT(16) +#define SWSYSRST_XFI_PEXPT1_GRST BIT(15) +#define SWSYSRST_XFI_PEXPT0_GRST BIT(14) ++#define SWSYSRST_XFI1_GRST BIT(13) ++#define SWSYSRST_XFI0_GRST BIT(12) +#define SWSYSRST_SGMII1_GRST BIT(2) +#define SWSYSRST_SGMII0_GRST BIT(1) +#define TOPRGU_SWSYSRST_EN 0xFC @@ -813,7 +836,7 @@ Signed-off-by: Daniel Golle /* MT7628/88 specific stuff */ #define MT7628_PDMA_OFFSET 0x0800 #define MT7628_SDM_OFFSET 0x0c00 -@@ -809,13 +904,6 @@ enum mtk_gmac_id { +@@ -809,13 +926,6 @@ enum mtk_gmac_id { MTK_GMAC_ID_MAX }; @@ -827,7 +850,7 @@ Signed-off-by: Daniel Golle enum mtk_tx_buf_type { MTK_TYPE_SKB, MTK_TYPE_XDP_TX, -@@ -902,6 +990,7 @@ enum mkt_eth_capabilities { +@@ -902,6 +1012,7 @@ enum mkt_eth_capabilities { MTK_TRGMII_BIT, MTK_SGMII_BIT, MTK_USXGMII_BIT, @@ -835,7 +858,7 @@ Signed-off-by: Daniel Golle MTK_ESW_BIT, MTK_GEPHY_BIT, MTK_MUX_BIT, -@@ -922,6 +1011,7 @@ enum mkt_eth_capabilities { +@@ -922,6 +1033,7 @@ enum mkt_eth_capabilities { MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, @@ -843,7 +866,7 @@ Signed-off-by: Daniel Golle MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, -@@ -933,6 +1023,7 @@ enum mkt_eth_capabilities { +@@ -933,6 +1045,7 @@ enum mkt_eth_capabilities { MTK_ETH_PATH_GMAC1_SGMII_BIT, MTK_ETH_PATH_GMAC2_RGMII_BIT, MTK_ETH_PATH_GMAC2_SGMII_BIT, @@ -851,7 +874,7 @@ Signed-off-by: Daniel Golle MTK_ETH_PATH_GMAC2_GEPHY_BIT, MTK_ETH_PATH_GMAC3_SGMII_BIT, MTK_ETH_PATH_GDM1_ESW_BIT, -@@ -946,6 +1037,7 @@ enum mkt_eth_capabilities { +@@ -946,6 +1059,7 @@ enum mkt_eth_capabilities { #define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) #define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) #define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) @@ -859,7 +882,7 @@ Signed-off-by: Daniel Golle #define MTK_ESW BIT_ULL(MTK_ESW_BIT) #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) #define MTK_MUX BIT_ULL(MTK_MUX_BIT) -@@ -968,6 +1060,8 @@ enum mkt_eth_capabilities { +@@ -968,6 +1082,8 @@ enum mkt_eth_capabilities { BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) @@ -868,7 +891,7 @@ Signed-off-by: Daniel Golle #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ -@@ -983,6 +1077,7 @@ enum mkt_eth_capabilities { +@@ -983,6 +1099,7 @@ enum mkt_eth_capabilities { #define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) @@ -876,7 +899,7 @@ Signed-off-by: Daniel Golle #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) #define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) -@@ -996,6 +1091,7 @@ enum mkt_eth_capabilities { +@@ -996,6 +1113,7 @@ enum mkt_eth_capabilities { #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) @@ -884,7 +907,7 @@ Signed-off-by: Daniel Golle #define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) #define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) -@@ -1019,6 +1115,10 @@ enum mkt_eth_capabilities { +@@ -1019,6 +1137,10 @@ enum mkt_eth_capabilities { (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ MTK_SHARED_SGMII) @@ -895,7 +918,7 @@ Signed-off-by: Daniel Golle /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) -@@ -1077,7 +1177,8 @@ enum mkt_eth_capabilities { +@@ -1077,7 +1199,8 @@ enum mkt_eth_capabilities { MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | \ MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \ @@ -905,37 +928,54 @@ Signed-off-by: Daniel Golle struct mtk_tx_dma_desc_info { dma_addr_t addr; -@@ -1183,6 +1284,19 @@ struct mtk_soc_data { +@@ -1183,6 +1306,22 @@ struct mtk_soc_data { #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000) -+/* struct mtk_xgmii - This is the structure holding sgmii/usxgmii regmap and -+ * its characteristics -+ * @regmap: The register map pointing at the range used to setup -+ * SGMII/USXGMII modes -+ * @flags: The enum refers to which mode the sgmii wants to run on -+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap ++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii regmap and ++ * associated data ++ * @regmap: The register map pointing at the range used to setup ++ * USXGMII modes ++ * @interface: Currently selected interface mode ++ * @id: The element is used to record the index of PCS ++ * @pcs: Phylink PCS structure + */ -+struct mtk_xgmii { -+ struct regmap **regmap_usxgmii; -+ struct regmap **regmap_pextp; -+ struct regmap *regmap_pll; ++struct mtk_usxgmii_pcs { ++ struct mtk_eth *eth; ++ struct regmap *regmap; ++ phy_interface_t interface; ++ u8 id; ++ struct phylink_pcs pcs; +}; + /* struct mtk_eth - This is the main datasructure for holding the state * of the driver * @dev: The device pointer -@@ -1244,7 +1358,9 @@ struct mtk_eth { +@@ -1203,6 +1342,11 @@ struct mtk_soc_data { + * @infra: The register map pointing at the range used to setup + * SGMII and GePHY path + * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances ++ * @usxgmii_pll: The register map pointing at the range used to control ++ * the USXGMII SerDes PLL ++ * @regmap_pextp: The register map pointing at the range used to setup ++ * PHYA ++ * @usxgmii_pcs: Pointer to array of pointers to struct for USXGMII PCS + * @pctl: The register map pointing at the range used to setup + * GMAC port drive/slew values + * @dma_refcnt: track how many netdevs are using the DMA engine +@@ -1244,7 +1388,11 @@ struct mtk_eth { unsigned long sysclk; struct regmap *ethsys; struct regmap *infra; + struct regmap *toprgu; struct phylink_pcs **sgmii_pcs; -+ struct mtk_xgmii *xgmii; ++ struct regmap *usxgmii_pll; ++ struct regmap **regmap_pextp; ++ struct mtk_usxgmii_pcs **usxgmii_pcs; struct regmap *pctl; bool hwlro; refcount_t dma_refcnt; -@@ -1400,6 +1516,19 @@ static inline u32 mtk_get_ib2_multicast_ +@@ -1400,6 +1548,19 @@ static inline u32 mtk_get_ib2_multicast_ return MTK_FOE_IB2_MULTICAST; } @@ -955,7 +995,7 @@ Signed-off-by: Daniel Golle /* read the hardware status register */ void mtk_stats_update_mac(struct mtk_mac *mac); -@@ -1407,8 +1536,10 @@ void mtk_w32(struct mtk_eth *eth, u32 va +@@ -1407,8 +1568,10 @@ void mtk_w32(struct mtk_eth *eth, u32 va u32 mtk_r32(struct mtk_eth *eth, unsigned reg); int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); @@ -966,50 +1006,35 @@ Signed-off-by: Daniel Golle int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, -@@ -1418,5 +1549,36 @@ int mtk_flow_offload_cmd(struct mtk_eth +@@ -1418,5 +1581,20 @@ int mtk_flow_offload_cmd(struct mtk_eth void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list); void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); +#ifdef CONFIG_NET_MEDIATEK_SOC_USXGMII ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id); +int mtk_usxgmii_init(struct mtk_eth *eth); -+int mtk_xfi_pextp_init(struct mtk_eth *eth); -+int mtk_xfi_pll_init(struct mtk_eth *eth); -+int mtk_toprgu_init(struct mtk_eth *eth); +int mtk_xfi_pll_enable(struct mtk_eth *eth); -+int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, -+ int max_speed); -+int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state); -+void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id); -+void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id); +#else -+static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pextp_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pll_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_toprgu_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pll_enable(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, -+ int max_speed) { return 0; }; -+static inline int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state) { return 0; }; -+static inline void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) { }; -+#endif ++static inline struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id) { return NULL; } ++static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; } ++static inline int mtk_xfi_pll_enable(struct mtk_eth *eth) { return 0; } ++static inline void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) { } ++#endif /* NET_MEDIATEK_SOC_USXGMII */ #endif /* MTK_ETH_H */ --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_usxgmii.c -@@ -0,0 +1,646 @@ +@@ -0,0 +1,835 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen ++ * Daniel Golle + */ + +#include @@ -1017,43 +1042,20 @@ Signed-off-by: Daniel Golle +#include +#include "mtk_eth_soc.h" + -+int mtk_usxgmii_init(struct mtk_eth *eth) ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) +{ -+ struct device_node *r = eth->dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; -+ struct device *dev = eth->dev; -+ struct device_node *np; -+ int i; -+ -+ xs->regmap_usxgmii = devm_kzalloc(dev, sizeof(*xs->regmap_usxgmii) * -+ eth->soc->num_devs, GFP_KERNEL); -+ if (!xs->regmap_usxgmii) -+ return -ENOMEM; -+ -+ for (i = 0; i < eth->soc->num_devs; i++) { -+ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); -+ if (!np) -+ break; -+ -+ xs->regmap_usxgmii[i] = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_usxgmii[i])) -+ return PTR_ERR(xs->regmap_usxgmii[i]); -+ } -+ -+ return 0; ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); +} + -+int mtk_xfi_pextp_init(struct mtk_eth *eth) ++static int mtk_xfi_pextp_init(struct mtk_eth *eth) +{ + struct device *dev = eth->dev; + struct device_node *r = dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; + struct device_node *np; + int i; + -+ xs->regmap_pextp = devm_kzalloc(dev, sizeof(*xs->regmap_pextp) * -+ eth->soc->num_devs, GFP_KERNEL); -+ if (!xs->regmap_pextp) ++ eth->regmap_pextp = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->regmap_pextp), GFP_KERNEL); ++ if (!eth->regmap_pextp) + return -ENOMEM; + + for (i = 0; i < eth->soc->num_devs; i++) { @@ -1061,32 +1063,31 @@ Signed-off-by: Daniel Golle + if (!np) + break; + -+ xs->regmap_pextp[i] = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_pextp[i])) -+ return PTR_ERR(xs->regmap_pextp[i]); ++ eth->regmap_pextp[i] = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->regmap_pextp[i])) ++ return PTR_ERR(eth->regmap_pextp[i]); + } + + return 0; +} + -+int mtk_xfi_pll_init(struct mtk_eth *eth) ++static int mtk_xfi_pll_init(struct mtk_eth *eth) +{ + struct device_node *r = eth->dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; + struct device_node *np; + + np = of_parse_phandle(r, "mediatek,xfi_pll", 0); + if (!np) + return -1; + -+ xs->regmap_pll = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_pll)) -+ return PTR_ERR(xs->regmap_pll); ++ eth->usxgmii_pll = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pll)) ++ return PTR_ERR(eth->usxgmii_pll); + + return 0; +} + -+int mtk_toprgu_init(struct mtk_eth *eth) ++static int mtk_toprgu_init(struct mtk_eth *eth) +{ + struct device_node *r = eth->dev->of_node; + struct device_node *np; @@ -1104,18 +1105,17 @@ Signed-off-by: Daniel Golle + +int mtk_xfi_pll_enable(struct mtk_eth *eth) +{ -+ struct mtk_xgmii *xs = eth->xgmii; + u32 val = 0; + -+ if (!xs->regmap_pll) ++ if (!eth->usxgmii_pll) + return -EINVAL; + + /* Add software workaround for USXGMII PLL TCL issue */ -+ regmap_write(xs->regmap_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); ++ regmap_write(eth->usxgmii_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); + -+ regmap_read(xs->regmap_pll, XFI_PLL_DIG_GLB8, &val); ++ regmap_read(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, &val); + val |= RG_XFI_PLL_EN; -+ regmap_write(xs->regmap_pll, XFI_PLL_DIG_GLB8, val); ++ regmap_write(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, val); + + return 0; +} @@ -1141,531 +1141,727 @@ Signed-off-by: Daniel Golle + return xgmii_id; +} + -+void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id) ++static int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id) +{ -+ struct mtk_xgmii *xs = eth->xgmii; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ int mac_id = xgmii_id; + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ switch (xgmii_id) { ++ case 0: ++ mac_id = 2; ++ break; ++ case 1: ++ mac_id = 1; ++ break; ++ default: ++ mac_id = -1; ++ } ++ } ++ ++ return mac_id; ++} ++ ++ ++static void mtk_usxgmii_setup_phya_usxgmii(struct mtk_usxgmii_pcs *mpcs) ++{ ++ struct regmap *pextp; ++ ++ if (!mpcs->eth) + return; + -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, 0x000FFE6D); -+ regmap_write(xs->regmap_usxgmii[id], 0x818, 0x07B1EC7B); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x30000000); -+ ndelay(1020); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x10000000); -+ ndelay(1020); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x00000000); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; + -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00C9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x01423342); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000001); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_setup_phya_force_5000(struct mtk_eth *eth, int mac_id) ++static void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs) +{ -+ unsigned int val; -+ struct mtk_xgmii *xs = eth->xgmii; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++ if (!mpcs->eth) + return; + -+ /* Setup USXGMII speed */ -+ val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_5G) | -+ FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_5G); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Disable USXGMII AN mode */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val); -+ val &= ~RG_AN_ENABLE; -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val); -+ -+ /* Gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; + ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAAA5A5AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C018AA); ++ regmap_write(pextp, 0x50E0, 0x3777812B); ++ regmap_write(pextp, 0x506C, 0x005C9CFF); ++ regmap_write(pextp, 0x5070, 0x9DFAFAFA); ++ regmap_write(pextp, 0x5074, 0x273F3F3F); ++ regmap_write(pextp, 0x5078, 0xA8883868); ++ regmap_write(pextp, 0x507C, 0x14661466); ++ regmap_write(pextp, 0x5080, 0x0E001ABF); ++ regmap_write(pextp, 0x5084, 0x080B0D0D); ++ regmap_write(pextp, 0x5088, 0x02050909); ++ regmap_write(pextp, 0x50E4, 0x0C000000); ++ regmap_write(pextp, 0x50E8, 0x04000000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x50808C8C); ++ regmap_write(pextp, 0x6004, 0x18000000); ++ regmap_write(pextp, 0x00F8, 0x00A132A1); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ -+ /* USXGMII force mode setting */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_USXGMII_RATE_UPDATE_MODE; -+ val |= RG_IF_FORCE_EN; -+ val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Un-gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val &= ~RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); -+ -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAAA5A5AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C018AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777812B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005C9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9DFAFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x273F3F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA8883868); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x14661466); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E001ABF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x080B0D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02050909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C000000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04000000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x50808C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18000000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x00A132A1); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000003); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000003); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_setup_phya_force_10000(struct mtk_eth *eth, int mac_id) ++static void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs) +{ -+ struct mtk_xgmii *xs = eth->xgmii; -+ unsigned int val; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++ if (!mpcs->eth) + return; + -+ /* Setup USXGMII speed */ -+ val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_10G) | -+ FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_10G); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Disable USXGMII AN mode */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val); -+ val &= ~RG_AN_ENABLE; -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val); -+ -+ /* Gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; + ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x47684100); ++ regmap_write(pextp, 0x3050, 0x00000000); ++ regmap_write(pextp, 0x3054, 0x00000000); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ if (mpcs->id == 0) ++ regmap_write(pextp, 0xA008, 0x0007B400); + -+ /* USXGMII force mode setting */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_USXGMII_RATE_UPDATE_MODE; -+ val |= RG_IF_FORCE_EN; -+ val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Un-gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val &= ~RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); -+ -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00C9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x01423342); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x49664100); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0x00000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x00000000); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000001); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id) ++void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) +{ + u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; ++ ++ if (id >= eth->soc->num_devs) ++ return; ++ ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020207); ++ regmap_write(pextp, 0x2034, 0x0E05050F); ++ regmap_write(pextp, 0x2040, 0x00200032); ++ regmap_write(pextp, 0x50F0, 0x00C014BA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000EAF); ++ regmap_write(pextp, 0x5084, 0x08080E0D); ++ regmap_write(pextp, 0x5088, 0x02030B09); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0606); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x00FA32FA); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3064, 0x0000C000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x20200F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000007); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0201C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) ++{ ++ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; ++ ++ if (id >= eth->soc->num_devs) ++ return; ++ ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x009C329C); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x22000F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000005); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0201C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++static void mtk_usxgmii_reset(struct mtk_eth *eth, int id) ++{ ++ u32 val = 0; + + if (id >= eth->soc->num_devs || !eth->toprgu) + return; + -+ switch (mac_id) { -+ case MTK_GMAC2_ID: -+ regmap_write(eth->toprgu, 0xFC, 0x0000A004); -+ regmap_write(eth->toprgu, 0x18, 0x88F0A004); -+ regmap_write(eth->toprgu, 0xFC, 0x00000000); -+ regmap_write(eth->toprgu, 0x18, 0x88F00000); -+ regmap_write(eth->toprgu, 0x18, 0x00F00000); ++ switch (id) { ++ case 0: ++ /* Enable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val |= SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ ++ /* Assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | ++ SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ udelay(100); ++ ++ /* De-assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ /* Disable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; -+ case MTK_GMAC3_ID: -+ regmap_write(eth->toprgu, 0xFC, 0x00005002); -+ regmap_write(eth->toprgu, 0x18, 0x88F05002); -+ regmap_write(eth->toprgu, 0xFC, 0x00000000); -+ regmap_write(eth->toprgu, 0x18, 0x88F00000); -+ regmap_write(eth->toprgu, 0x18, 0x00F00000); ++ case 1: ++ /* Enable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val |= SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ ++ /* Assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | ++ SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ udelay(100); ++ ++ /* De-assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ /* Disable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + } + + mdelay(10); +} + -+int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, int max_speed) -+{ -+ if (mac_id < 0 || mac_id >= eth->soc->num_devs) -+ return -EINVAL; -+ -+ if ((max_speed != SPEED_10000) && (max_speed != SPEED_5000)) -+ return -EINVAL; -+ -+ mtk_xfi_pll_enable(eth); -+ mtk_usxgmii_reset(eth, mac_id); -+ mtk_usxgmii_setup_phya_an_10000(eth, mac_id); -+ -+ return 0; -+} -+ -+int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state) -+{ -+ if (mac_id < 0 || mac_id >= eth->soc->num_devs) -+ return -EINVAL; -+ -+ mtk_xfi_pll_enable(eth); -+ mtk_usxgmii_reset(eth, mac_id); -+ if (state->interface == PHY_INTERFACE_MODE_5GBASER) -+ mtk_usxgmii_setup_phya_force_5000(eth, mac_id); -+ else -+ mtk_usxgmii_setup_phya_force_10000(eth, mac_id); -+ -+ return 0; -+} -+ -+void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) -+{ -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); -+ struct mtk_xgmii *xs = eth->xgmii; -+ -+ if (id >= eth->soc->num_devs || !xs->regmap_pextp[id]) -+ return; -+ -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020207); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E05050F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00200032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014BA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000EAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080E0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030B09); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0606); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x00FA32FA); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F21); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00011110); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3064, 0x0000C000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x20200F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00050000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000007); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); -+ udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); -+ udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C111); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C101); -+ udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F01); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); -+ udelay(400); -+} -+ -+void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) -+{ -+ struct mtk_xgmii *xs = eth->xgmii; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); -+ -+ if (id >= eth->soc->num_devs || !xs->regmap_pextp[id]) -+ return; -+ -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x009C329C); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F21); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00011110); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x22000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00050000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000005); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); -+ udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); -+ udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C111); -+ ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C101); -+ udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F01); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); -+ udelay(400); -+} -+ +void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) +{ -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); -+ u32 val = 0; ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); + -+ if (id >= eth->soc->num_devs || !eth->toprgu) -+ return; ++ mtk_usxgmii_reset(eth, xgmii_id); ++} + -+ switch (mac_id) { -+ case MTK_GMAC2_ID: -+ /* Enable software reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val |= SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST; -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + -+ /* Assert SGMII reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); -+ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | -+ SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST; -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; + -+ udelay(100); ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | ++ USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GKR) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else ++ return -EINVAL; + -+ /* De-assert SGMII reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); -+ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); -+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST); -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); + -+ /* Disable software reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST); -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); -+ break; -+ case MTK_GMAC3_ID: -+ /* Enable Software reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val |= SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST; -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); -+ -+ /* Assert SGMII reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); -+ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | -+ SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST; -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); -+ -+ udelay(100); -+ -+ /* De-assert SGMII reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); -+ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); -+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST); -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); -+ -+ /* Disable software reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST); -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); -+ break; ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; + } + -+ mdelay(1); -+} ---- a/drivers/net/ethernet/mediatek/Kconfig -+++ b/drivers/net/ethernet/mediatek/Kconfig -@@ -11,6 +11,14 @@ config NET_MEDIATEK_SOC_WED - depends on ARCH_MEDIATEK || COMPILE_TEST - def_bool NET_MEDIATEK_SOC != n - -+config NET_MEDIATEK_SOC_USXGMII -+ bool "Support USXGMII SerDes on MT7988" -+ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST -+ def_bool NET_MEDIATEK_SOC != n -+ help -+ Include support for 10G USXGMII SerDes unit which can -+ be found on MT7988. ++ mtk_xfi_pll_enable(eth); ++ mtk_usxgmii_reset(eth, mpcs->id); + - config NET_MEDIATEK_SOC - tristate "MediaTek SoC Gigabit Ethernet support" - depends on NET_DSA || !NET_DSA ++ /* Setup USXGMII AN ctrl */ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ /* Gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ udelay(1); ++ ++ /* Un-gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, 0); ++ ++ udelay(1); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, 0); ++ ++ /* Setup USXGMIISYS with the determined property */ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) ++ mtk_usxgmii_setup_phya_usxgmii(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_10GKR) ++ mtk_usxgmii_setup_phya_10gbaser(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_5GBASER) ++ mtk_usxgmii_setup_phya_5gbaser(mpcs); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)]; ++ u32 val = 0; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ if (FIELD_GET(USXGMII_AN_ENABLE, val)) { ++ /* Refresh LPA by inverting LPA_LATCH */ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0, ++ USXGMII_LPA_LATCH, ++ !(val & USXGMII_LPA_LATCH)); ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(USXGMII_LPA_LINK, val); ++ state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val); ++ ++ switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) { ++ case USXGMII_LPA_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case USXGMII_LPA_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case USXGMII_LPA_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ case USXGMII_LPA_SPEED_2500: ++ state->speed = SPEED_2500; ++ break; ++ case USXGMII_LPA_SPEED_5000: ++ state->speed = SPEED_5000; ++ break; ++ case USXGMII_LPA_SPEED_10000: ++ state->speed = SPEED_10000; ++ break; ++ } ++ } else { ++ val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); ++ ++ if (mac->id == MTK_GMAC2_ID) ++ val = val >> 16; ++ ++ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) { ++ case 0: ++ state->speed = SPEED_10000; ++ break; ++ case 1: ++ state->speed = SPEED_5000; ++ break; ++ case 2: ++ state->speed = SPEED_2500; ++ break; ++ case 3: ++ state->speed = SPEED_1000; ++ break; ++ } ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val); ++ state->duplex = DUPLEX_FULL; ++ } ++ ++ if (state->link == 0) ++ mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND, ++ state->interface, NULL, false); ++} ++ ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int val = 0; ++ ++ if (!mpcs->regmap) ++ return; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ val |= USXGMII_AN_RESTART; ++ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, mode, ++ interface, NULL, false); ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++}; ++ ++int mtk_usxgmii_init(struct mtk_eth *eth) ++{ ++ struct device_node *r = eth->dev->of_node; ++ struct device *dev = eth->dev; ++ struct device_node *np; ++ int i, ret; ++ ++ eth->usxgmii_pcs = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs) ++ return -ENOMEM; ++ ++ for (i = 0; i < eth->soc->num_devs; i++) { ++ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); ++ if (!np) ++ break; ++ ++ eth->usxgmii_pcs[i] = devm_kzalloc(dev, sizeof(*eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs[i]) ++ return -ENOMEM; ++ ++ eth->usxgmii_pcs[i]->id = i; ++ eth->usxgmii_pcs[i]->eth = eth; ++ eth->usxgmii_pcs[i]->regmap = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pcs[i]->regmap)) ++ return PTR_ERR(eth->usxgmii_pcs[i]->regmap); ++ ++ eth->usxgmii_pcs[i]->pcs.ops = &mtk_usxgmii_pcs_ops; ++ eth->usxgmii_pcs[i]->pcs.poll = true; ++ eth->usxgmii_pcs[i]->interface = PHY_INTERFACE_MODE_NA; ++ ++ of_node_put(np); ++ } ++ ++ ret = mtk_xfi_pextp_init(eth); ++ if (ret) ++ return ret; ++ ++ ret = mtk_xfi_pll_init(eth); ++ if (ret) ++ return ret; ++ ++ return mtk_toprgu_init(eth); ++} ++ ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int mac_id) ++{ ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); ++ ++ if (!eth->usxgmii_pcs[xgmii_id]->regmap) ++ return NULL; ++ ++ return ð->usxgmii_pcs[xgmii_id]->pcs; ++} diff --git a/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch b/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch index fc83416b2a9..67e9d6419bc 100644 --- a/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch +++ b/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau #endif --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -4574,7 +4574,7 @@ static int rps_ipi_queued(struct softnet +@@ -4576,7 +4576,7 @@ static int rps_ipi_queued(struct softnet #ifdef CONFIG_RPS struct softnet_data *mysd = this_cpu_ptr(&softnet_data); @@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau sd->rps_ipi_next = mysd->rps_ipi_list; mysd->rps_ipi_list = sd; -@@ -5755,6 +5755,8 @@ static DEFINE_PER_CPU(struct work_struct +@@ -5757,6 +5757,8 @@ static DEFINE_PER_CPU(struct work_struct /* Network device is going away, flush any packets still pending */ static void flush_backlog(struct work_struct *work) { @@ -48,7 +48,7 @@ Signed-off-by: Felix Fietkau struct sk_buff *skb, *tmp; struct softnet_data *sd; -@@ -5770,9 +5772,18 @@ static void flush_backlog(struct work_st +@@ -5772,9 +5774,18 @@ static void flush_backlog(struct work_st input_queue_head_incr(sd); } } @@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau skb_queue_walk_safe(&sd->process_queue, skb, tmp) { if (skb->dev->reg_state == NETREG_UNREGISTERING) { __skb_unlink(skb, &sd->process_queue); -@@ -5780,7 +5791,18 @@ static void flush_backlog(struct work_st +@@ -5782,7 +5793,18 @@ static void flush_backlog(struct work_st input_queue_head_incr(sd); } } @@ -86,7 +86,7 @@ Signed-off-by: Felix Fietkau } static bool flush_required(int cpu) -@@ -6463,6 +6485,7 @@ static int process_backlog(struct napi_s +@@ -6465,6 +6487,7 @@ static int process_backlog(struct napi_s local_irq_disable(); rps_lock(sd); @@ -94,7 +94,7 @@ Signed-off-by: Felix Fietkau if (skb_queue_empty(&sd->input_pkt_queue)) { /* * Inline a custom version of __napi_complete(). -@@ -6472,7 +6495,8 @@ static int process_backlog(struct napi_s +@@ -6474,7 +6497,8 @@ static int process_backlog(struct napi_s * We can use a plain write instead of clear_bit(), * and we dont need an smp_mb() memory barrier. */ @@ -104,7 +104,7 @@ Signed-off-by: Felix Fietkau again = false; } else { skb_queue_splice_tail_init(&sd->input_pkt_queue, -@@ -6889,6 +6913,57 @@ int dev_set_threaded(struct net_device * +@@ -6891,6 +6915,57 @@ int dev_set_threaded(struct net_device * } EXPORT_SYMBOL(dev_set_threaded); @@ -162,7 +162,7 @@ Signed-off-by: Felix Fietkau void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { -@@ -11367,6 +11442,9 @@ static int dev_cpu_dead(unsigned int old +@@ -11369,6 +11444,9 @@ static int dev_cpu_dead(unsigned int old raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_enable(); @@ -172,7 +172,7 @@ Signed-off-by: Felix Fietkau #ifdef CONFIG_RPS remsd = oldsd->rps_ipi_list; oldsd->rps_ipi_list = NULL; -@@ -11706,6 +11784,7 @@ static int __init net_dev_init(void) +@@ -11708,6 +11786,7 @@ static int __init net_dev_init(void) sd->cpu = i; #endif diff --git a/target/linux/generic/pending-6.1/160-workqueue-fix-enum-type-for-gcc-13.patch b/target/linux/generic/pending-6.1/160-workqueue-fix-enum-type-for-gcc-13.patch new file mode 100644 index 00000000000..82076121ac3 --- /dev/null +++ b/target/linux/generic/pending-6.1/160-workqueue-fix-enum-type-for-gcc-13.patch @@ -0,0 +1,44 @@ +From 525ff9c2965770762b81d679820552a208070d59 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 17 Jan 2023 17:40:35 +0100 +Subject: workqueue: fix enum type for gcc-13 + +In gcc-13, the WORK_STRUCT_WQ_DATA_MASK constant is a signed 64-bit +type on 32-bit architectures because the enum definition has both +negative numbers and numbers above LONG_MAX in it: + +kernel/workqueue.c: In function 'get_work_pwq': +kernel/workqueue.c:709:24: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 709 | return (void *)(data & WORK_STRUCT_WQ_DATA_MASK); + | ^ +kernel/workqueue.c: In function 'get_work_pool': +kernel/workqueue.c:737:25: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 737 | return ((struct pool_workqueue *) + | ^ +kernel/workqueue.c: In function 'get_work_pool_id': +kernel/workqueue.c:759:25: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] + 759 | return ((struct pool_workqueue *) + | ^ + +Change the enum definition to ensure all values can fit into +the range of 'unsigned long' on all architectures. + +Signed-off-by: Arnd Bergmann +Tested-by: Thierry Reding +Tested-by: Lai Jiangshan +Signed-off-by: Tejun Heo +--- + include/linux/workqueue.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/workqueue.h ++++ b/include/linux/workqueue.h +@@ -83,7 +83,7 @@ enum { + + /* convenience constants */ + WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1, +- WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK, ++ WORK_STRUCT_WQ_DATA_MASK = (unsigned long)~WORK_STRUCT_FLAG_MASK, + WORK_STRUCT_NO_POOL = (unsigned long)WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT, + + /* bit mask for work_busy() return values */ diff --git a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch index 06642ec8662..00a43e3e551 100644 --- a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch +++ b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch @@ -15,7 +15,7 @@ Signed-off-by: Alexander Couzens --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2196,6 +2196,10 @@ mt7530_setup(struct dsa_switch *ds) +@@ -2236,6 +2236,10 @@ mt7530_setup(struct dsa_switch *ds) return -ENODEV; } diff --git a/target/linux/generic/pending-6.1/737-01-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V1-capabilit.patch b/target/linux/generic/pending-6.1/737-01-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V1-capabilit.patch new file mode 100644 index 00000000000..bf1bc78b23a --- /dev/null +++ b/target/linux/generic/pending-6.1/737-01-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V1-capabilit.patch @@ -0,0 +1,223 @@ +From 663fa1b7e0cb2c929008482014a70c6625caad75 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Mar 2023 15:55:13 +0000 +Subject: [PATCH 1/7] net: ethernet: mtk_eth_soc: add MTK_NETSYS_V1 capability + bit + +Introduce MTK_NETSYS_V1 bit in the device capabilities for +MT7621/MT7622/MT7623/MT7628/MT7629 SoCs. +Use !MTK_NETSYS_V1 instead of MTK_NETSYS_V2 in the driver codebase. +This is a preliminary patch to introduce support for MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 45 ++++++++++++--------- + 2 files changed, 41 insertions(+), 34 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -640,7 +640,7 @@ static void mtk_set_queue_speed(struct m + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) | + MTK_QTX_SCH_LEAKY_BUCKET_SIZE; +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; + + if (IS_ENABLED(CONFIG_SOC_MT7621)) { +@@ -1018,7 +1018,7 @@ static bool mtk_rx_get_desc(struct mtk_e + rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); + rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); + rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + rxd->rxd5 = READ_ONCE(dma_rxd->rxd5); + rxd->rxd6 = READ_ONCE(dma_rxd->rxd6); + } +@@ -1076,7 +1076,7 @@ static int mtk_init_fq_dma(struct mtk_et + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + txd->txd4 = 0; +- if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V1)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; +@@ -1267,7 +1267,7 @@ static void mtk_tx_set_dma_desc(struct n + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + mtk_tx_set_dma_desc_v2(dev, txd, info); + else + mtk_tx_set_dma_desc_v1(dev, txd, info); +@@ -1950,7 +1950,7 @@ static int mtk_poll_rx(struct napi_struc + break; + + /* find out which mac the packet come from. values start at 1 */ +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1; + else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && + !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) +@@ -2046,7 +2046,7 @@ static int mtk_poll_rx(struct napi_struc + skb->dev = netdev; + bytes += skb->len; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5); + hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY; + if (hash != MTK_RXD5_FOE_ENTRY) +@@ -2071,7 +2071,7 @@ static int mtk_poll_rx(struct napi_struc + /* When using VLAN untagging in combination with DSA, the + * hardware treats the MTK special tag as a VLAN and untags it. + */ +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) && ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1) && + (trxd.rxd2 & RX_DMA_VTAG) && netdev_uses_dsa(netdev)) { + unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0); + +@@ -2382,7 +2382,7 @@ static int mtk_tx_alloc(struct mtk_eth * + txd->txd2 = next_ptr; + txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + txd->txd4 = 0; +- if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V1)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; +@@ -2435,7 +2435,7 @@ static int mtk_tx_alloc(struct mtk_eth * + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) | + MTK_QTX_SCH_LEAKY_BUCKET_SIZE; +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; + mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); + ofs += MTK_QTX_OFFSET; +@@ -2571,7 +2571,7 @@ static int mtk_rx_alloc(struct mtk_eth * + + rxd->rxd3 = 0; + rxd->rxd4 = 0; +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + rxd->rxd5 = 0; + rxd->rxd6 = 0; + rxd->rxd7 = 0; +@@ -3119,7 +3119,7 @@ static int mtk_start_dma(struct mtk_eth + MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | + MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + val |= MTK_MUTLI_CNT | MTK_RESV_BUF | + MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | + MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN; +@@ -3529,7 +3529,7 @@ static void mtk_hw_reset(struct mtk_eth + { + u32 val; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); + val = RSTCTRL_PPE0_V2; + } else { +@@ -3541,7 +3541,7 @@ static void mtk_hw_reset(struct mtk_eth + + ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, + 0x3ffffff); + } +@@ -3737,7 +3737,7 @@ static int mtk_hw_init(struct mtk_eth *e + else + mtk_hw_reset(eth); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + /* Set FE to PDMAv2 if necessary */ + val = mtk_r32(eth, MTK_FE_GLO_MISC); + mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC); +@@ -3774,7 +3774,7 @@ static int mtk_hw_init(struct mtk_eth *e + */ + val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); + mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { + val = mtk_r32(eth, MTK_CDMP_IG_CTRL); + mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -819,6 +819,7 @@ enum mkt_eth_capabilities { + MTK_SHARED_INT_BIT, + MTK_TRGMII_MT7621_CLK_BIT, + MTK_QDMA_BIT, ++ MTK_NETSYS_V1_BIT, + MTK_NETSYS_V2_BIT, + MTK_SOC_MT7628_BIT, + MTK_RSTCTRL_PPE1_BIT, +@@ -854,6 +855,7 @@ enum mkt_eth_capabilities { + #define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) + #define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) + #define MTK_QDMA BIT(MTK_QDMA_BIT) ++#define MTK_NETSYS_V1 BIT(MTK_NETSYS_V1_BIT) + #define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) + #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) + #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) +@@ -916,25 +918,30 @@ enum mkt_eth_capabilities { + + #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x) & ~(MTK_CAP_MASK)) == (_x)) + +-#define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ +- MTK_GMAC2_RGMII | MTK_SHARED_INT | \ +- MTK_TRGMII_MT7621_CLK | MTK_QDMA) +- +-#define MT7622_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_SGMII | MTK_GMAC2_RGMII | \ +- MTK_GMAC2_SGMII | MTK_GDM1_ESW | \ +- MTK_MUX_GDM1_TO_GMAC1_ESW | \ +- MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_QDMA) +- +-#define MT7623_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII | \ +- MTK_QDMA) +- +-#define MT7628_CAPS (MTK_SHARED_INT | MTK_SOC_MT7628) +- +-#define MT7629_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ +- MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW | \ +- MTK_MUX_GMAC2_GMAC0_TO_GEPHY | \ +- MTK_MUX_U3_GMAC2_TO_QPHY | \ +- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) ++#define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ ++ MTK_GMAC2_RGMII | MTK_SHARED_INT | \ ++ MTK_TRGMII_MT7621_CLK | MTK_QDMA | \ ++ MTK_NETSYS_V1) ++ ++#define MT7622_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_SGMII | \ ++ MTK_GMAC2_RGMII | MTK_GMAC2_SGMII | \ ++ MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW |\ ++ MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | \ ++ MTK_QDMA | MTK_NETSYS_V1) ++ ++#define MT7623_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ ++ MTK_GMAC2_RGMII | MTK_QDMA | \ ++ MTK_NETSYS_V1) ++ ++#define MT7628_CAPS (MTK_SHARED_INT | MTK_SOC_MT7628 | \ ++ MTK_NETSYS_V1) ++ ++#define MT7629_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ ++ MTK_GMAC2_GEPHY | MTK_GDM1_ESW | \ ++ MTK_MUX_GMAC2_GMAC0_TO_GEPHY | MTK_QDMA | \ ++ MTK_MUX_U3_GMAC2_TO_QPHY | MTK_NETSYS_V1 |\ ++ MTK_MUX_GDM1_TO_GMAC1_ESW | \ ++ MTK_MUX_GMAC12_TO_GEPHY_SGMII) + + #define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ diff --git a/target/linux/generic/pending-6.1/737-02-net-ethernet-mtk_eth_soc-move-MAX_DEVS-in-mtk_soc_da.patch b/target/linux/generic/pending-6.1/737-02-net-ethernet-mtk_eth_soc-move-MAX_DEVS-in-mtk_soc_da.patch new file mode 100644 index 00000000000..82848a7c972 --- /dev/null +++ b/target/linux/generic/pending-6.1/737-02-net-ethernet-mtk_eth_soc-move-MAX_DEVS-in-mtk_soc_da.patch @@ -0,0 +1,181 @@ +From 5af2b2dc4d6ba0ff7696e79f18e5b2bf862194eb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Mar 2023 15:55:24 +0000 +Subject: [PATCH 2/7] net: ethernet: mtk_eth_soc: move MAX_DEVS in mtk_soc_data + +This is a preliminary patch to add MT7988 SoC support since it runs 3 +macs instead of 2. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 +++++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 11 +++---- + 2 files changed, 36 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4043,7 +4043,10 @@ static void mtk_sgmii_destroy(struct mtk + { + int i; + +- for (i = 0; i < MTK_MAX_DEVS; i++) ++ if (!eth->sgmii_pcs) ++ return; ++ ++ for (i = 0; i < eth->soc->num_devs; i++) + mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]); + } + +@@ -4486,7 +4489,12 @@ static int mtk_sgmii_init(struct mtk_eth + u32 flags; + int i; + +- for (i = 0; i < MTK_MAX_DEVS; i++) { ++ eth->sgmii_pcs = devm_kzalloc(eth->dev, ++ sizeof(*eth->sgmii_pcs) * ++ eth->soc->num_devs, ++ GFP_KERNEL); ++ ++ for (i = 0; i < eth->soc->num_devs; i++) { + np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i); + if (!np) + break; +@@ -4531,6 +4539,18 @@ static int mtk_probe(struct platform_dev + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + eth->ip_align = NET_IP_ALIGN; + ++ eth->netdev = devm_kzalloc(eth->dev, ++ sizeof(*eth->netdev) * eth->soc->num_devs, ++ GFP_KERNEL); ++ if (!eth->netdev) ++ return -ENOMEM; ++ ++ eth->mac = devm_kzalloc(eth->dev, ++ sizeof(*eth->mac) * eth->soc->num_devs, ++ GFP_KERNEL); ++ if (!eth->mac) ++ return -ENOMEM; ++ + spin_lock_init(ð->page_lock); + spin_lock_init(ð->tx_irq_lock); + spin_lock_init(ð->rx_irq_lock); +@@ -4716,7 +4736,7 @@ static int mtk_probe(struct platform_dev + goto err_deinit_ppe; + } + +- for (i = 0; i < MTK_MAX_DEVS; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i]) + continue; + +@@ -4792,6 +4812,7 @@ static const struct mtk_soc_data mt2701_ + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, ++ .num_devs = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4810,6 +4831,7 @@ static const struct mtk_soc_data mt7621_ + .required_pctl = false, + .offload_version = 1, + .hash_offset = 2, ++ .num_devs = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), +@@ -4831,6 +4853,7 @@ static const struct mtk_soc_data mt7622_ + .offload_version = 2, + .hash_offset = 2, + .has_accounting = true, ++ .num_devs = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), +@@ -4850,6 +4873,7 @@ static const struct mtk_soc_data mt7623_ + .required_pctl = true, + .offload_version = 1, + .hash_offset = 2, ++ .num_devs = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), +@@ -4869,6 +4893,7 @@ static const struct mtk_soc_data mt7629_ + .required_clks = MT7629_CLKS_BITMAP, + .required_pctl = false, + .has_accounting = true, ++ .num_devs = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4890,6 +4915,7 @@ static const struct mtk_soc_data mt7981_ + .hash_offset = 4, + .foe_entry_size = sizeof(struct mtk_foe_entry), + .has_accounting = true, ++ .num_devs = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), +@@ -4908,6 +4934,7 @@ static const struct mtk_soc_data mt7986_ + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, + .hash_offset = 4, ++ .num_devs = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry), + .has_accounting = true, + .txrx = { +@@ -4926,6 +4953,7 @@ static const struct mtk_soc_data rt5350_ + .hw_features = MTK_HW_FEATURES_MT7628, + .required_clks = MT7628_CLKS_BITMAP, + .required_pctl = false, ++ .num_devs = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1021,6 +1021,7 @@ struct mtk_reg_map { + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. + * @hash_offset Flow table hash offset. ++ * @num_devs SoC number of macs. + * @foe_entry_size Foe table entry size. + * @has_accounting Bool indicating support for accounting of + * offloaded flows. +@@ -1039,6 +1040,7 @@ struct mtk_soc_data { + bool required_pctl; + u8 offload_version; + u8 hash_offset; ++ u8 num_devs; + u16 foe_entry_size; + netdev_features_t hw_features; + bool has_accounting; +@@ -1054,9 +1056,6 @@ struct mtk_soc_data { + + #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000) + +-/* currently no SoC has more than 2 macs */ +-#define MTK_MAX_DEVS 2 +- + /* struct mtk_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer +@@ -1111,14 +1110,14 @@ struct mtk_eth { + spinlock_t tx_irq_lock; + spinlock_t rx_irq_lock; + struct net_device dummy_dev; +- struct net_device *netdev[MTK_MAX_DEVS]; +- struct mtk_mac *mac[MTK_MAX_DEVS]; ++ struct net_device **netdev; ++ struct mtk_mac **mac; + int irq[3]; + u32 msg_enable; + unsigned long sysclk; + struct regmap *ethsys; + struct regmap *infra; +- struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS]; ++ struct phylink_pcs **sgmii_pcs; + struct regmap *pctl; + bool hwlro; + refcount_t dma_refcnt; diff --git a/target/linux/generic/pending-6.1/737-03-net-ethernet-mtk_eth_soc-rely-on-num_devs-and-remove.patch b/target/linux/generic/pending-6.1/737-03-net-ethernet-mtk_eth_soc-rely-on-num_devs-and-remove.patch new file mode 100644 index 00000000000..be12aa5c066 --- /dev/null +++ b/target/linux/generic/pending-6.1/737-03-net-ethernet-mtk_eth_soc-rely-on-num_devs-and-remove.patch @@ -0,0 +1,153 @@ +From 4e35e80750b33727e606be9e7ce447bde2e0deb7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Mar 2023 15:55:35 +0000 +Subject: [PATCH 3/7] net: ethernet: mtk_eth_soc: rely on num_devs and remove + MTK_MAC_COUNT + +Get rid of MTK_MAC_COUNT since it is a duplicated of eth->soc->num_devs. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 ++++++++++----------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 - + 2 files changed, 15 insertions(+), 16 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -944,7 +944,7 @@ static void mtk_stats_update(struct mtk_ + { + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->mac[i] || !eth->mac[i]->hw_stats) + continue; + if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) { +@@ -1449,7 +1449,7 @@ static int mtk_queue_stopped(struct mtk_ + { + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i]) + continue; + if (netif_queue_stopped(eth->netdev[i])) +@@ -1463,7 +1463,7 @@ static void mtk_wake_queue(struct mtk_et + { + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i]) + continue; + netif_tx_wake_all_queues(eth->netdev[i]); +@@ -1956,7 +1956,7 @@ static int mtk_poll_rx(struct napi_struc + !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) + mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1; + +- if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || ++ if (unlikely(mac < 0 || mac >= eth->soc->num_devs || + !eth->netdev[mac])) + goto release_desc; + +@@ -2993,7 +2993,7 @@ static void mtk_dma_free(struct mtk_eth + const struct mtk_soc_data *soc = eth->soc; + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) ++ for (i = 0; i < soc->num_devs; i++) + if (eth->netdev[i]) + netdev_reset_queue(eth->netdev[i]); + if (eth->scratch_ring) { +@@ -3147,7 +3147,7 @@ static void mtk_gdm_config(struct mtk_et + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + return; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); + + /* default setup the forward port to send frame to PDMA */ +@@ -3758,7 +3758,7 @@ static int mtk_hw_init(struct mtk_eth *e + * up with the more appropriate value when mtk_mac_config call is being + * invoked. + */ +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + struct net_device *dev = eth->netdev[i]; + + mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i)); +@@ -3963,7 +3963,7 @@ static void mtk_pending_work(struct work + mtk_prepare_for_reset(eth); + + /* stop all devices to make sure that dma is properly shut down */ +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i] || !netif_running(eth->netdev[i])) + continue; + +@@ -3979,7 +3979,7 @@ static void mtk_pending_work(struct work + mtk_hw_init(eth, true); + + /* restart DMA and enable IRQs */ +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!test_bit(i, &restart)) + continue; + +@@ -4007,7 +4007,7 @@ static int mtk_free_dev(struct mtk_eth * + { + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i]) + continue; + free_netdev(eth->netdev[i]); +@@ -4026,7 +4026,7 @@ static int mtk_unreg_dev(struct mtk_eth + { + int i; + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + struct mtk_mac *mac; + if (!eth->netdev[i]) + continue; +@@ -4331,7 +4331,7 @@ static int mtk_add_mac(struct mtk_eth *e + } + + id = be32_to_cpup(_id); +- if (id >= MTK_MAC_COUNT) { ++ if (id >= eth->soc->num_devs) { + dev_err(eth->dev, "%d is not a valid mac id\n", id); + return -EINVAL; + } +@@ -4461,7 +4461,7 @@ void mtk_eth_set_dma_device(struct mtk_e + + rtnl_lock(); + +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + dev = eth->netdev[i]; + + if (!dev || !(dev->flags & IFF_UP)) +@@ -4787,7 +4787,7 @@ static int mtk_remove(struct platform_de + int i; + + /* stop all devices to make sure that dma is properly shut down */ +- for (i = 0; i < MTK_MAC_COUNT; i++) { ++ for (i = 0; i < eth->soc->num_devs; i++) { + if (!eth->netdev[i]) + continue; + mtk_stop(eth->netdev[i]); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -33,7 +33,6 @@ + #define MTK_TX_DMA_BUF_LEN_V2 0xffff + #define MTK_QDMA_RING_SIZE 2048 + #define MTK_DMA_SIZE 512 +-#define MTK_MAC_COUNT 2 + #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + ETH_FCS_LEN) + #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) + #define MTK_DMA_DUMMY_DESC 0xffffffff diff --git a/target/linux/generic/pending-6.1/737-04-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V3-capabilit.patch b/target/linux/generic/pending-6.1/737-04-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V3-capabilit.patch new file mode 100644 index 00000000000..a383a5e4ef8 --- /dev/null +++ b/target/linux/generic/pending-6.1/737-04-net-ethernet-mtk_eth_soc-add-MTK_NETSYS_V3-capabilit.patch @@ -0,0 +1,292 @@ +From ab817f559d505329d8a413c7d29250f6d87d77a0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Mar 2023 15:55:47 +0000 +Subject: [PATCH 4/7] net: ethernet: mtk_eth_soc: add MTK_NETSYS_V3 capability + bit + +Introduce MTK_NETSYS_V3 bit in the device capabilities. +This is a preliminary patch to introduce support for MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 115 ++++++++++++++++---- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 44 +++++++- + 2 files changed, 134 insertions(+), 25 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -924,17 +924,32 @@ void mtk_stats_update_mac(struct mtk_mac + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs); + hw_stats->rx_flow_control_packets += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs); +- hw_stats->tx_skip += +- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs); +- hw_stats->tx_collisions += +- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs); +- hw_stats->tx_bytes += +- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs); +- stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs); +- if (stats) +- hw_stats->tx_bytes += (stats << 32); +- hw_stats->tx_packets += +- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs); ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ hw_stats->tx_skip += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x50 + offs); ++ hw_stats->tx_collisions += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x54 + offs); ++ hw_stats->tx_bytes += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x40 + offs); ++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x44 + offs); ++ if (stats) ++ hw_stats->tx_bytes += (stats << 32); ++ hw_stats->tx_packets += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x48 + offs); ++ } else { ++ hw_stats->tx_skip += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs); ++ hw_stats->tx_collisions += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs); ++ hw_stats->tx_bytes += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs); ++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs); ++ if (stats) ++ hw_stats->tx_bytes += (stats << 32); ++ hw_stats->tx_packets += ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs); ++ } + } + + u64_stats_update_end(&hw_stats->syncp); +@@ -1238,7 +1253,10 @@ static void mtk_tx_set_dma_desc_v2(struc + data |= TX_DMA_LS0; + WRITE_ONCE(desc->txd3, data); + +- data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ ++ if (mac->id == MTK_GMAC3_ID) ++ data = PSE_GDM3_PORT; ++ else ++ data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ + data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); + WRITE_ONCE(desc->txd4, data); + +@@ -1249,6 +1267,9 @@ static void mtk_tx_set_dma_desc_v2(struc + /* tx checksum offload */ + if (info->csum) + data |= TX_DMA_CHKSUM_V2; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ netdev_uses_dsa(dev)) ++ data |= TX_DMA_SPTAG_V3; + } + WRITE_ONCE(desc->txd5, data); + +@@ -1314,8 +1335,13 @@ static int mtk_tx_map(struct sk_buff *sk + mtk_tx_set_dma_desc(dev, itxd, &txd_info); + + itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; +- itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : +- MTK_TX_FLAGS_FPORT1; ++ if (mac->id == MTK_GMAC1_ID) ++ itx_buf->flags |= MTK_TX_FLAGS_FPORT0; ++ else if (mac->id == MTK_GMAC2_ID) ++ itx_buf->flags |= MTK_TX_FLAGS_FPORT1; ++ else ++ itx_buf->flags |= MTK_TX_FLAGS_FPORT2; ++ + setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, + k++); + +@@ -1363,8 +1389,13 @@ static int mtk_tx_map(struct sk_buff *sk + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; + tx_buf->flags |= MTK_TX_FLAGS_PAGE0; +- tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : +- MTK_TX_FLAGS_FPORT1; ++ ++ if (mac->id == MTK_GMAC1_ID) ++ tx_buf->flags |= MTK_TX_FLAGS_FPORT0; ++ else if (mac->id == MTK_GMAC2_ID) ++ tx_buf->flags |= MTK_TX_FLAGS_FPORT1; ++ else ++ tx_buf->flags |= MTK_TX_FLAGS_FPORT2; + + setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, + txd_info.size, k++); +@@ -1950,11 +1981,24 @@ static int mtk_poll_rx(struct napi_struc + break; + + /* find out which mac the packet come from. values start at 1 */ +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) +- mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1; +- else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && +- !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) { ++ u32 val = RX_DMA_GET_SPORT_V2(trxd.rxd5); ++ ++ switch (val) { ++ case PSE_GDM1_PORT: ++ case PSE_GDM2_PORT: ++ mac = val - 1; ++ break; ++ case PSE_GDM3_PORT: ++ mac = MTK_GMAC3_ID; ++ break; ++ default: ++ break; ++ } ++ } else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && ++ !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) { + mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1; ++ } + + if (unlikely(mac < 0 || mac >= eth->soc->num_devs || + !eth->netdev[mac])) +@@ -2185,7 +2229,9 @@ static int mtk_poll_tx_qdma(struct mtk_e + tx_buf = mtk_desc_to_tx_buf(ring, desc, + eth->soc->txrx.txd_size); + if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) +- mac = 1; ++ mac = MTK_GMAC2_ID; ++ else if (tx_buf->flags & MTK_TX_FLAGS_FPORT2) ++ mac = MTK_GMAC3_ID; + + if (!tx_buf->data) + break; +@@ -3796,7 +3842,26 @@ static int mtk_hw_init(struct mtk_eth *e + mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4); + mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ /* PSE should not drop port1, port8 and port9 packets */ ++ mtk_w32(eth, 0x00000302, PSE_DROP_CFG); ++ ++ /* GDM and CDM Threshold */ ++ mtk_w32(eth, 0x00000707, MTK_CDMW0_THRES); ++ mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES); ++ ++ /* Disable GDM1 RX CRC stripping */ ++ val = mtk_r32(eth, MTK_GDMA_FWD_CFG(0)); ++ val &= ~MTK_GDMA_STRP_CRC; ++ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(0)); ++ ++ /* PSE GDM3 MIB counter has incorrect hw default values, ++ * so the driver ought to read clear the values beforehand ++ * in case ethtool retrieve wrong mib values. ++ */ ++ for (i = 0; i < 0x80; i += 0x4) ++ mtk_r32(eth, reg_map->gdm1_cnt + 0x100 + i); ++ } else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + /* PSE should not drop port8 and port9 packets from WDMA Tx */ + mtk_w32(eth, 0x00000300, PSE_DROP_CFG); + +@@ -4368,7 +4433,11 @@ static int mtk_add_mac(struct mtk_eth *e + } + spin_lock_init(&mac->hw_stats->stats_lock); + u64_stats_init(&mac->hw_stats->syncp); +- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) ++ mac->hw_stats->reg_offset = id * 0x80; ++ else ++ mac->hw_stats->reg_offset = id * 0x40; + + /* phylink create */ + err = of_get_phy_mode(np, &phy_mode); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -121,6 +121,7 @@ + #define MTK_GDMA_ICS_EN BIT(22) + #define MTK_GDMA_TCS_EN BIT(21) + #define MTK_GDMA_UCS_EN BIT(20) ++#define MTK_GDMA_STRP_CRC BIT(16) + #define MTK_GDMA_TO_PDMA 0x0 + #define MTK_GDMA_DROP_ALL 0x7777 + +@@ -286,8 +287,6 @@ + /* QDMA Interrupt grouping registers */ + #define MTK_RLS_DONE_INT BIT(0) + +-#define MTK_STAT_OFFSET 0x40 +- + /* QDMA TX NUM */ + #define QID_BITS_V2(x) (((x) & 0x3f) << 16) + #define MTK_QDMA_GMAC2_QID 8 +@@ -300,6 +299,8 @@ + #define TX_DMA_CHKSUM_V2 (0x7 << 28) + #define TX_DMA_TSO_V2 BIT(31) + ++#define TX_DMA_SPTAG_V3 BIT(27) ++ + /* QDMA V2 descriptor txd4 */ + #define TX_DMA_FPORT_SHIFT_V2 8 + #define TX_DMA_FPORT_MASK_V2 0xf +@@ -639,6 +640,7 @@ enum mtk_tx_flags { + */ + MTK_TX_FLAGS_FPORT0 = 0x04, + MTK_TX_FLAGS_FPORT1 = 0x08, ++ MTK_TX_FLAGS_FPORT2 = 0x10, + }; + + /* This enum allows us to identify how the clock is defined on the array of the +@@ -724,6 +726,42 @@ enum mtk_dev_state { + MTK_RESETTING + }; + ++/* PSE Port Definition */ ++enum mtk_pse_port { ++ PSE_ADMA_PORT = 0, ++ PSE_GDM1_PORT, ++ PSE_GDM2_PORT, ++ PSE_PPE0_PORT, ++ PSE_PPE1_PORT, ++ PSE_QDMA_TX_PORT, ++ PSE_QDMA_RX_PORT, ++ PSE_DROP_PORT, ++ PSE_WDMA0_PORT, ++ PSE_WDMA1_PORT, ++ PSE_TDMA_PORT, ++ PSE_NONE_PORT, ++ PSE_PPE2_PORT, ++ PSE_WDMA2_PORT, ++ PSE_EIP197_PORT, ++ PSE_GDM3_PORT, ++ PSE_PORT_MAX ++}; ++ ++/* GMAC Identifier */ ++enum mtk_gmac_id { ++ MTK_GMAC1_ID = 0, ++ MTK_GMAC2_ID, ++ MTK_GMAC3_ID, ++ MTK_GMAC_ID_MAX ++}; ++ ++/* GDM Type */ ++enum mtk_gdm_type { ++ MTK_GDM_TYPE = 0, ++ MTK_XGDM_TYPE, ++ MTK_GDM_TYPE_MAX ++}; ++ + enum mtk_tx_buf_type { + MTK_TYPE_SKB, + MTK_TYPE_XDP_TX, +@@ -820,6 +858,7 @@ enum mkt_eth_capabilities { + MTK_QDMA_BIT, + MTK_NETSYS_V1_BIT, + MTK_NETSYS_V2_BIT, ++ MTK_NETSYS_V3_BIT, + MTK_SOC_MT7628_BIT, + MTK_RSTCTRL_PPE1_BIT, + MTK_U3_COPHY_V2_BIT, +@@ -856,6 +895,7 @@ enum mkt_eth_capabilities { + #define MTK_QDMA BIT(MTK_QDMA_BIT) + #define MTK_NETSYS_V1 BIT(MTK_NETSYS_V1_BIT) + #define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) ++#define MTK_NETSYS_V3 BIT(MTK_NETSYS_V3_BIT) + #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) + #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) + #define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT) diff --git a/target/linux/generic/pending-6.1/737-05-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch b/target/linux/generic/pending-6.1/737-05-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch new file mode 100644 index 00000000000..068201cf7df --- /dev/null +++ b/target/linux/generic/pending-6.1/737-05-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch @@ -0,0 +1,197 @@ +From 45b575fd9e6a455090820248bf1b98b1f2c7b6c8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Mar 2023 15:56:00 +0000 +Subject: [PATCH 5/7] net: ethernet: mtk_eth_soc: convert caps in mtk_soc_data + struct to u64 + +This is a preliminary patch to introduce support for MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_path.c | 22 +++---- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 62 ++++++++++---------- + 2 files changed, 42 insertions(+), 42 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -15,10 +15,10 @@ + struct mtk_eth_muxc { + const char *name; + int cap_bit; +- int (*set_path)(struct mtk_eth *eth, int path); ++ int (*set_path)(struct mtk_eth *eth, u64 path); + }; + +-static const char *mtk_eth_path_name(int path) ++static const char *mtk_eth_path_name(u64 path) + { + switch (path) { + case MTK_ETH_PATH_GMAC1_RGMII: +@@ -40,7 +40,7 @@ static const char *mtk_eth_path_name(int + } + } + +-static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path) ++static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path) + { + bool updated = true; + u32 val, mask, set; +@@ -71,7 +71,7 @@ static int set_mux_gdm1_to_gmac1_esw(str + return 0; + } + +-static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path) ++static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -94,7 +94,7 @@ static int set_mux_gmac2_gmac0_to_gephy( + return 0; + } + +-static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path) ++static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0, mask = 0, reg = 0; + bool updated = true; +@@ -125,7 +125,7 @@ static int set_mux_u3_gmac2_to_qphy(stru + return 0; + } + +-static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path) ++static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -163,7 +163,7 @@ static int set_mux_gmac1_gmac2_to_sgmii_ + return 0; + } + +-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path) ++static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -218,7 +218,7 @@ static const struct mtk_eth_muxc mtk_eth + }, + }; + +-static int mtk_eth_mux_setup(struct mtk_eth *eth, int path) ++static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path) + { + int i, err = 0; + +@@ -249,7 +249,7 @@ out: + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) + { +- int path; ++ u64 path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : + MTK_ETH_PATH_GMAC2_SGMII; +@@ -260,7 +260,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk + + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id) + { +- int path = 0; ++ u64 path = 0; + + if (mac_id == 1) + path = MTK_ETH_PATH_GMAC2_GEPHY; +@@ -274,7 +274,7 @@ int mtk_gmac_gephy_path_setup(struct mtk + + int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id) + { +- int path; ++ u64 path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII : + MTK_ETH_PATH_GMAC2_RGMII; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -881,44 +881,44 @@ enum mkt_eth_capabilities { + }; + + /* Supported hardware group on SoCs */ +-#define MTK_RGMII BIT(MTK_RGMII_BIT) +-#define MTK_TRGMII BIT(MTK_TRGMII_BIT) +-#define MTK_SGMII BIT(MTK_SGMII_BIT) +-#define MTK_ESW BIT(MTK_ESW_BIT) +-#define MTK_GEPHY BIT(MTK_GEPHY_BIT) +-#define MTK_MUX BIT(MTK_MUX_BIT) +-#define MTK_INFRA BIT(MTK_INFRA_BIT) +-#define MTK_SHARED_SGMII BIT(MTK_SHARED_SGMII_BIT) +-#define MTK_HWLRO BIT(MTK_HWLRO_BIT) +-#define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) +-#define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) +-#define MTK_QDMA BIT(MTK_QDMA_BIT) +-#define MTK_NETSYS_V1 BIT(MTK_NETSYS_V1_BIT) +-#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) +-#define MTK_NETSYS_V3 BIT(MTK_NETSYS_V3_BIT) +-#define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) +-#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) +-#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT) ++#define MTK_RGMII BIT_ULL(MTK_RGMII_BIT) ++#define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) ++#define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) ++#define MTK_ESW BIT_ULL(MTK_ESW_BIT) ++#define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) ++#define MTK_MUX BIT_ULL(MTK_MUX_BIT) ++#define MTK_INFRA BIT_ULL(MTK_INFRA_BIT) ++#define MTK_SHARED_SGMII BIT_ULL(MTK_SHARED_SGMII_BIT) ++#define MTK_HWLRO BIT_ULL(MTK_HWLRO_BIT) ++#define MTK_SHARED_INT BIT_ULL(MTK_SHARED_INT_BIT) ++#define MTK_TRGMII_MT7621_CLK BIT_ULL(MTK_TRGMII_MT7621_CLK_BIT) ++#define MTK_QDMA BIT_ULL(MTK_QDMA_BIT) ++#define MTK_NETSYS_V1 BIT_ULL(MTK_NETSYS_V1_BIT) ++#define MTK_NETSYS_V2 BIT_ULL(MTK_NETSYS_V2_BIT) ++#define MTK_NETSYS_V3 BIT_ULL(MTK_NETSYS_V3_BIT) ++#define MTK_SOC_MT7628 BIT_ULL(MTK_SOC_MT7628_BIT) ++#define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT) ++#define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT) + + #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ +- BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) ++ BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) + #define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY \ +- BIT(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) + #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ +- BIT(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) + #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ +- BIT(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ +- BIT(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++ BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) + + /* Supported path present on SoCs */ +-#define MTK_ETH_PATH_GMAC1_RGMII BIT(MTK_ETH_PATH_GMAC1_RGMII_BIT) +-#define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT) +-#define MTK_ETH_PATH_GMAC1_SGMII BIT(MTK_ETH_PATH_GMAC1_SGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_RGMII BIT(MTK_ETH_PATH_GMAC2_RGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT) +-#define MTK_ETH_PATH_GMAC2_GEPHY BIT(MTK_ETH_PATH_GMAC2_GEPHY_BIT) +-#define MTK_ETH_PATH_GDM1_ESW BIT(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) ++#define MTK_ETH_PATH_GMAC1_TRGMII BIT_ULL(MTK_ETH_PATH_GMAC1_TRGMII_BIT) ++#define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) ++#define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) + + #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) + #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +@@ -1074,7 +1074,7 @@ struct mtk_reg_map { + struct mtk_soc_data { + const struct mtk_reg_map *reg_map; + u32 ana_rgc3; +- u32 caps; ++ u64 caps; + u32 required_clks; + bool required_pctl; + u8 offload_version; diff --git a/target/linux/generic/pending-6.1/737-06-net-ethernet-mtk_eth_soc-add-support-for-MT7988-SoC.patch b/target/linux/generic/pending-6.1/737-06-net-ethernet-mtk_eth_soc-add-support-for-MT7988-SoC.patch new file mode 100644 index 00000000000..4e52e0da547 --- /dev/null +++ b/target/linux/generic/pending-6.1/737-06-net-ethernet-mtk_eth_soc-add-support-for-MT7988-SoC.patch @@ -0,0 +1,495 @@ +From 661bacf4363ca68939c15e20056b5f72fbd034e7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 25 Feb 2023 00:08:24 +0100 +Subject: [PATCH 6/7] net: ethernet: mtk_eth_soc: add support for MT7988 SoC + +Introduce support for ethernet chip available in MT7988 SoC to +mtk_eth_soc driver. +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 153 ++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 193 ++++++++++++++------ + 2 files changed, 279 insertions(+), 67 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -152,6 +152,54 @@ static const struct mtk_reg_map mt7986_r + .pse_oq_sta = 0x01a0, + }; + ++static const struct mtk_reg_map mt7988_reg_map = { ++ .tx_irq_mask = 0x461c, ++ .tx_irq_status = 0x4618, ++ .pdma = { ++ .rx_ptr = 0x6900, ++ .rx_cnt_cfg = 0x6904, ++ .pcrx_ptr = 0x6908, ++ .glo_cfg = 0x6a04, ++ .rst_idx = 0x6a08, ++ .delay_irq = 0x6a0c, ++ .irq_status = 0x6a20, ++ .irq_mask = 0x6a28, ++ .adma_rx_dbg0 = 0x6a38, ++ .int_grp = 0x6a50, ++ }, ++ .qdma = { ++ .qtx_cfg = 0x4400, ++ .qtx_sch = 0x4404, ++ .rx_ptr = 0x4500, ++ .rx_cnt_cfg = 0x4504, ++ .qcrx_ptr = 0x4508, ++ .glo_cfg = 0x4604, ++ .rst_idx = 0x4608, ++ .delay_irq = 0x460c, ++ .fc_th = 0x4610, ++ .int_grp = 0x4620, ++ .hred = 0x4644, ++ .ctx_ptr = 0x4700, ++ .dtx_ptr = 0x4704, ++ .crx_ptr = 0x4710, ++ .drx_ptr = 0x4714, ++ .fq_head = 0x4720, ++ .fq_tail = 0x4724, ++ .fq_count = 0x4728, ++ .fq_blen = 0x472c, ++ .tx_sch_rate = 0x4798, ++ }, ++ .gdm1_cnt = 0x1c00, ++ .gdma_to_ppe = 0x3333, ++ .ppe_base = 0x2200, ++ .wdma_base = { ++ [0] = 0x4800, ++ [1] = 0x4c00, ++ }, ++ .pse_iq_sta = 0x0180, ++ .pse_oq_sta = 0x01a0, ++}; ++ + /* strings used by ethtool */ + static const struct mtk_ethtool_stats { + char str[ETH_GSTRING_LEN]; +@@ -179,10 +227,54 @@ static const struct mtk_ethtool_stats { + }; + + static const char * const mtk_clks_source_name[] = { +- "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "trgpll", +- "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", +- "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb", +- "sgmii_ck", "eth2pll", "wocpu0", "wocpu1", "netsys0", "netsys1" ++ "ethif", ++ "sgmiitop", ++ "esw", ++ "gp0", ++ "gp1", ++ "gp2", ++ "gp3", ++ "xgp1", ++ "xgp2", ++ "xgp3", ++ "crypto", ++ "fe", ++ "trgpll", ++ "sgmii_tx250m", ++ "sgmii_rx250m", ++ "sgmii_cdr_ref", ++ "sgmii_cdr_fb", ++ "sgmii2_tx250m", ++ "sgmii2_rx250m", ++ "sgmii2_cdr_ref", ++ "sgmii2_cdr_fb", ++ "sgmii_ck", ++ "eth2pll", ++ "wocpu0", ++ "wocpu1", ++ "netsys0", ++ "netsys1", ++ "ethwarp_wocpu2", ++ "ethwarp_wocpu1", ++ "ethwarp_wocpu0", ++ "top_usxgmii0_sel", ++ "top_usxgmii1_sel", ++ "top_sgm0_sel", ++ "top_sgm1_sel", ++ "top_xfi_phy0_xtal_sel", ++ "top_xfi_phy1_xtal_sel", ++ "top_eth_gmii_sel", ++ "top_eth_refck_50m_sel", ++ "top_eth_sys_200m_sel", ++ "top_eth_sys_sel", ++ "top_eth_xgmii_sel", ++ "top_eth_mii_sel", ++ "top_netsys_sel", ++ "top_netsys_500m_sel", ++ "top_netsys_pao_2x_sel", ++ "top_netsys_sync_250m_sel", ++ "top_netsys_ppefb_250m_sel", ++ "top_netsys_warp_sel", + }; + + void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) +@@ -1253,10 +1345,19 @@ static void mtk_tx_set_dma_desc_v2(struc + data |= TX_DMA_LS0; + WRITE_ONCE(desc->txd3, data); + +- if (mac->id == MTK_GMAC3_ID) +- data = PSE_GDM3_PORT; +- else +- data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ ++ /* set forward port */ ++ switch (mac->id) { ++ case MTK_GMAC1_ID: ++ data = PSE_GDM1_PORT << TX_DMA_FPORT_SHIFT_V2; ++ break; ++ case MTK_GMAC2_ID: ++ data = PSE_GDM2_PORT << TX_DMA_FPORT_SHIFT_V2; ++ break; ++ case MTK_GMAC3_ID: ++ data = PSE_GDM3_PORT << TX_DMA_FPORT_SHIFT_V2; ++ break; ++ } ++ + data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); + WRITE_ONCE(desc->txd4, data); + +@@ -5016,6 +5117,25 @@ static const struct mtk_soc_data mt7986_ + }, + }; + ++static const struct mtk_soc_data mt7988_data = { ++ .reg_map = &mt7988_reg_map, ++ .ana_rgc3 = 0x128, ++ .caps = MT7988_CAPS, ++ .hw_features = MTK_HW_FEATURES, ++ .required_clks = MT7988_CLKS_BITMAP, ++ .required_pctl = false, ++ .num_devs = 3, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma_v2), ++ .rxd_size = sizeof(struct mtk_rx_dma_v2), ++ .rx_irq_done_mask = MTK_RX_DONE_INT_V2, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, ++ .dma_len_offset = 8, ++ }, ++}; ++ ++ + static const struct mtk_soc_data rt5350_data = { + .reg_map = &mt7628_reg_map, + .caps = MT7628_CAPS, +@@ -5034,14 +5154,15 @@ static const struct mtk_soc_data rt5350_ + }; + + const struct of_device_id of_mtk_match[] = { +- { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, +- { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data}, +- { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, +- { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, +- { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, +- { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data}, +- { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data}, +- { .compatible = "ralink,rt5350-eth", .data = &rt5350_data}, ++ { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data }, ++ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data }, ++ { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data }, ++ { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data }, ++ { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data }, ++ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data }, ++ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data }, ++ { .compatible = "mediatek,mt7988-eth", .data = &mt7988_data }, ++ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data }, + {}, + }; + MODULE_DEVICE_TABLE(of, of_mtk_match); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -116,7 +116,8 @@ + #define MTK_CDMP_EG_CTRL 0x404 + + /* GDM Exgress Control Register */ +-#define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000)) ++#define MTK_GDMA_FWD_CFG(x) ((x == MTK_GMAC3_ID) ? \ ++ 0x540 : 0x500 + (x * 0x1000)) + #define MTK_GDMA_SPECIAL_TAG BIT(24) + #define MTK_GDMA_ICS_EN BIT(22) + #define MTK_GDMA_TCS_EN BIT(21) +@@ -653,6 +654,11 @@ enum mtk_clks_map { + MTK_CLK_GP0, + MTK_CLK_GP1, + MTK_CLK_GP2, ++ MTK_CLK_GP3, ++ MTK_CLK_XGP1, ++ MTK_CLK_XGP2, ++ MTK_CLK_XGP3, ++ MTK_CLK_CRYPTO, + MTK_CLK_FE, + MTK_CLK_TRGPLL, + MTK_CLK_SGMII_TX_250M, +@@ -669,57 +675,108 @@ enum mtk_clks_map { + MTK_CLK_WOCPU1, + MTK_CLK_NETSYS0, + MTK_CLK_NETSYS1, ++ MTK_CLK_ETHWARP_WOCPU2, ++ MTK_CLK_ETHWARP_WOCPU1, ++ MTK_CLK_ETHWARP_WOCPU0, ++ MTK_CLK_TOP_USXGMII_SBUS_0_SEL, ++ MTK_CLK_TOP_USXGMII_SBUS_1_SEL, ++ MTK_CLK_TOP_SGM_0_SEL, ++ MTK_CLK_TOP_SGM_1_SEL, ++ MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL, ++ MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL, ++ MTK_CLK_TOP_ETH_GMII_SEL, ++ MTK_CLK_TOP_ETH_REFCK_50M_SEL, ++ MTK_CLK_TOP_ETH_SYS_200M_SEL, ++ MTK_CLK_TOP_ETH_SYS_SEL, ++ MTK_CLK_TOP_ETH_XGMII_SEL, ++ MTK_CLK_TOP_ETH_MII_SEL, ++ MTK_CLK_TOP_NETSYS_SEL, ++ MTK_CLK_TOP_NETSYS_500M_SEL, ++ MTK_CLK_TOP_NETSYS_PAO_2X_SEL, ++ MTK_CLK_TOP_NETSYS_SYNC_250M_SEL, ++ MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL, ++ MTK_CLK_TOP_NETSYS_WARP_SEL, + MTK_CLK_MAX + }; + +-#define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ +- BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ +- BIT(MTK_CLK_TRGPLL)) +-#define MT7622_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ +- BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ +- BIT(MTK_CLK_GP2) | \ +- BIT(MTK_CLK_SGMII_TX_250M) | \ +- BIT(MTK_CLK_SGMII_RX_250M) | \ +- BIT(MTK_CLK_SGMII_CDR_REF) | \ +- BIT(MTK_CLK_SGMII_CDR_FB) | \ +- BIT(MTK_CLK_SGMII_CK) | \ +- BIT(MTK_CLK_ETH2PLL)) ++#define MT7623_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \ ++ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \ ++ BIT_ULL(MTK_CLK_TRGPLL)) ++#define MT7622_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \ ++ BIT_ULL(MTK_CLK_GP0) | BIT_ULL(MTK_CLK_GP1) | \ ++ BIT_ULL(MTK_CLK_GP2) | \ ++ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII_CK) | \ ++ BIT_ULL(MTK_CLK_ETH2PLL)) + #define MT7621_CLKS_BITMAP (0) + #define MT7628_CLKS_BITMAP (0) +-#define MT7629_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ +- BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ +- BIT(MTK_CLK_GP2) | BIT(MTK_CLK_FE) | \ +- BIT(MTK_CLK_SGMII_TX_250M) | \ +- BIT(MTK_CLK_SGMII_RX_250M) | \ +- BIT(MTK_CLK_SGMII_CDR_REF) | \ +- BIT(MTK_CLK_SGMII_CDR_FB) | \ +- BIT(MTK_CLK_SGMII2_TX_250M) | \ +- BIT(MTK_CLK_SGMII2_RX_250M) | \ +- BIT(MTK_CLK_SGMII2_CDR_REF) | \ +- BIT(MTK_CLK_SGMII2_CDR_FB) | \ +- BIT(MTK_CLK_SGMII_CK) | \ +- BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP)) +-#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ +- BIT(MTK_CLK_WOCPU0) | \ +- BIT(MTK_CLK_SGMII_TX_250M) | \ +- BIT(MTK_CLK_SGMII_RX_250M) | \ +- BIT(MTK_CLK_SGMII_CDR_REF) | \ +- BIT(MTK_CLK_SGMII_CDR_FB) | \ +- BIT(MTK_CLK_SGMII2_TX_250M) | \ +- BIT(MTK_CLK_SGMII2_RX_250M) | \ +- BIT(MTK_CLK_SGMII2_CDR_REF) | \ +- BIT(MTK_CLK_SGMII2_CDR_FB) | \ +- BIT(MTK_CLK_SGMII_CK)) +-#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ +- BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \ +- BIT(MTK_CLK_SGMII_TX_250M) | \ +- BIT(MTK_CLK_SGMII_RX_250M) | \ +- BIT(MTK_CLK_SGMII_CDR_REF) | \ +- BIT(MTK_CLK_SGMII_CDR_FB) | \ +- BIT(MTK_CLK_SGMII2_TX_250M) | \ +- BIT(MTK_CLK_SGMII2_RX_250M) | \ +- BIT(MTK_CLK_SGMII2_CDR_REF) | \ +- BIT(MTK_CLK_SGMII2_CDR_FB)) ++#define MT7629_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \ ++ BIT_ULL(MTK_CLK_GP0) | BIT_ULL(MTK_CLK_GP1) | \ ++ BIT_ULL(MTK_CLK_GP2) | BIT_ULL(MTK_CLK_FE) | \ ++ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII_CK) | \ ++ BIT_ULL(MTK_CLK_ETH2PLL) | BIT_ULL(MTK_CLK_SGMIITOP)) ++#define MT7981_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_GP2) | BIT_ULL(MTK_CLK_GP1) | \ ++ BIT_ULL(MTK_CLK_WOCPU0) | \ ++ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII_CK)) ++#define MT7986_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_GP2) | BIT_ULL(MTK_CLK_GP1) | \ ++ BIT_ULL(MTK_CLK_WOCPU1) | BIT_ULL(MTK_CLK_WOCPU0) | \ ++ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \ ++ BIT_ULL(MTK_CLK_SGMII2_CDR_FB)) ++#define MT7988_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_ESW) | \ ++ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \ ++ BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \ ++ BIT_ULL(MTK_CLK_XGP2) | BIT_ULL(MTK_CLK_XGP3) | \ ++ BIT_ULL(MTK_CLK_CRYPTO) | \ ++ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT_ULL(MTK_CLK_ETHWARP_WOCPU2) | \ ++ BIT_ULL(MTK_CLK_ETHWARP_WOCPU1) | \ ++ BIT_ULL(MTK_CLK_ETHWARP_WOCPU0) | \ ++ BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_0_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_1_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_SGM_0_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_SGM_1_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_GMII_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_SYS_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_XGMII_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_ETH_MII_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_500M_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_PAO_2X_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_SYNC_250M_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL) | \ ++ BIT_ULL(MTK_CLK_TOP_NETSYS_WARP_SEL)) + + enum mtk_dev_state { + MTK_HW_INIT, +@@ -847,6 +904,7 @@ enum mkt_eth_capabilities { + MTK_RGMII_BIT = 0, + MTK_TRGMII_BIT, + MTK_SGMII_BIT, ++ MTK_USXGMII_BIT, + MTK_ESW_BIT, + MTK_GEPHY_BIT, + MTK_MUX_BIT, +@@ -869,6 +927,8 @@ enum mkt_eth_capabilities { + MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, + MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT, + + /* PATH BITS */ + MTK_ETH_PATH_GMAC1_RGMII_BIT, +@@ -877,13 +937,18 @@ enum mkt_eth_capabilities { + MTK_ETH_PATH_GMAC2_RGMII_BIT, + MTK_ETH_PATH_GMAC2_SGMII_BIT, + MTK_ETH_PATH_GMAC2_GEPHY_BIT, ++ MTK_ETH_PATH_GMAC3_SGMII_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, ++ MTK_ETH_PATH_GMAC1_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC2_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT, + }; + + /* Supported hardware group on SoCs */ + #define MTK_RGMII BIT_ULL(MTK_RGMII_BIT) + #define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) + #define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) ++#define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) + #define MTK_ESW BIT_ULL(MTK_ESW_BIT) + #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) + #define MTK_MUX BIT_ULL(MTK_MUX_BIT) +@@ -910,6 +975,10 @@ enum mkt_eth_capabilities { + BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_USXGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT) + + /* Supported path present on SoCs */ + #define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) +@@ -918,7 +987,11 @@ enum mkt_eth_capabilities { + #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) + #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) + #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) ++#define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) + #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC3_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT) + + #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) + #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +@@ -926,7 +999,11 @@ enum mkt_eth_capabilities { + #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) + #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) + #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) ++#define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) + #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) ++#define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) ++#define MTK_GMAC2_USXGMII (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII) ++#define MTK_GMAC3_USXGMII (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII) + + /* MUXes present on SoCs */ + /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ +@@ -949,6 +1026,12 @@ enum mkt_eth_capabilities { + #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) + ++#define MTK_MUX_GMAC123_TO_GEPHY_SGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX) ++ ++#define MTK_MUX_GMAC123_TO_USXGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_USXGMII | MTK_MUX | MTK_INFRA) ++ + #ifdef CONFIG_SOC_MT7621 + #define MTK_CAP_MASK MTK_NETSYS_V2 + #else +@@ -987,9 +1070,17 @@ enum mkt_eth_capabilities { + MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \ + MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1) + +-#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ +- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ +- MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1) ++#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ ++ MTK_MUX_GMAC12_TO_GEPHY_SGMII | \ ++ MTK_QDMA | MTK_NETSYS_V2 | \ ++ MTK_RSTCTRL_PPE1) ++ ++#define MT7988_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ ++ MTK_GMAC3_SGMII | MTK_QDMA | \ ++ MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ ++ MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | \ ++ MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \ ++ MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII) + + struct mtk_tx_dma_desc_info { + dma_addr_t addr; +@@ -1075,7 +1166,7 @@ struct mtk_soc_data { + const struct mtk_reg_map *reg_map; + u32 ana_rgc3; + u64 caps; +- u32 required_clks; ++ u64 required_clks; + bool required_pctl; + u8 offload_version; + u8 hash_offset; diff --git a/target/linux/generic/pending-6.1/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.1/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch new file mode 100644 index 00000000000..abde00cf617 --- /dev/null +++ b/target/linux/generic/pending-6.1/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -0,0 +1,1867 @@ +From 3d833ad2cfc1ab503d9aae2967b7f10811bb3c9c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 1 Mar 2023 11:56:04 +0000 +Subject: [PATCH 7/7] net: ethernet: mtk_eth_soc: add paths and SerDes modes + for MT7988 + +MT7988 comes with a built-in 2.5G PHY as well as +USXGMII/10GBase-KR/5GBase-KR compatible SerDes lanes for external PHYs. +Add support for configuring the MAC and SerDes parts for the new paths. + +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/Kconfig | 7 + + drivers/net/ethernet/mediatek/Makefile | 1 + + drivers/net/ethernet/mediatek/mtk_eth_path.c | 154 +++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 270 +++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 194 ++++- + drivers/net/ethernet/mediatek/mtk_usxgmii.c | 835 +++++++++++++++++++ + 6 files changed, 1428 insertions(+), 33 deletions(-) + create mode 100644 drivers/net/ethernet/mediatek/mtk_usxgmii.c + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -25,6 +25,13 @@ config NET_MEDIATEK_SOC + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. + ++config NET_MEDIATEK_SOC_USXGMII ++ bool "Support USXGMII SerDes on MT7988" ++ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST ++ def_bool NET_MEDIATEK_SOC != n ++ help ++ Include support for 10G SerDes which can be found on MT7988. ++ + config NET_MEDIATEK_STAR_EMAC + tristate "MediaTek STAR Ethernet MAC support" + select PHYLIB +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -5,6 +5,7 @@ + + obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o + mtk_eth-y := mtk_eth_soc.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o ++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_USXGMII) += mtk_usxgmii.o + mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o + ifdef CONFIG_DEBUG_FS + mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o +--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -31,10 +31,20 @@ static const char *mtk_eth_path_name(u64 + return "gmac2_rgmii"; + case MTK_ETH_PATH_GMAC2_SGMII: + return "gmac2_sgmii"; ++ case MTK_ETH_PATH_GMAC2_2P5GPHY: ++ return "gmac2_2p5gphy"; + case MTK_ETH_PATH_GMAC2_GEPHY: + return "gmac2_gephy"; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ return "gmac3_sgmii"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ return "gmac1_usxgmii"; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ return "gmac2_usxgmii"; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ return "gmac3_usxgmii"; + default: + return "unknown path"; + } +@@ -42,8 +52,8 @@ static const char *mtk_eth_path_name(u64 + + static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path) + { ++ u32 val, mask, set, reg; + bool updated = true; +- u32 val, mask, set; + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: +@@ -59,10 +69,15 @@ static int set_mux_gdm1_to_gmac1_esw(str + break; + } + ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) ++ reg = MTK_MAC_MISC_V3; ++ else ++ reg = MTK_MAC_MISC; ++ + if (updated) { +- val = mtk_r32(eth, MTK_MAC_MISC); ++ val = mtk_r32(eth, reg); + val = (val & mask) | set; +- mtk_w32(eth, val, MTK_MAC_MISC); ++ mtk_w32(eth, val, reg); + } + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", +@@ -125,6 +140,31 @@ static int set_mux_u3_gmac2_to_qphy(stru + return 0; + } + ++static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path) ++{ ++ unsigned int val = 0; ++ bool updated = true; ++ int mac_id = 0; ++ ++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); ++ ++ switch (path) { ++ case MTK_ETH_PATH_GMAC2_2P5GPHY: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; ++ mac_id = MTK_GMAC2_ID; ++ break; ++ default: ++ updated = false; ++ break; ++ }; ++ ++ if (updated) ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, val); ++ ++ return 0; ++} ++ + static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; +@@ -163,7 +203,61 @@ static int set_mux_gmac1_gmac2_to_sgmii_ + return 0; + } + +-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path) ++static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path) ++{ ++ unsigned int val = 0; ++ bool updated = true; ++ int mac_id = 0; ++ ++ dev_dbg(eth->dev, "path %s in %s updated = %d\n", ++ mtk_eth_path_name(path), __func__, updated); ++ ++ /* Disable SYSCFG1 SGMII */ ++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); ++ ++ switch (path) { ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2; ++ mac_id = MTK_GMAC1_ID; ++ break; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; ++ mac_id = MTK_GMAC2_ID; ++ break; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2; ++ mac_id = MTK_GMAC3_ID; ++ break; ++ default: ++ updated = false; ++ }; ++ ++ if (updated) { ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, val); ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ mac_id == MTK_GMAC2_ID) { ++ regmap_update_bits(eth->infra, ++ TOP_MISC_NETSYS_PCS_MUX, ++ NETSYS_PCS_MUX_MASK, ++ MUX_G2_USXGMII_SEL); ++ } ++ } ++ ++ /* Enable XGDM Path */ ++ val = mtk_r32(eth, MTK_GDMA_EG_CTRL(mac_id)); ++ val |= MTK_GDMA_XGDM_SEL; ++ mtk_w32(eth, val, MTK_GDMA_EG_CTRL(mac_id)); ++ ++ dev_dbg(eth->dev, "path %s in %s updated = %d\n", ++ mtk_eth_path_name(path), __func__, updated); ++ ++ ++ return 0; ++} ++ ++static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -180,6 +274,9 @@ static int set_mux_gmac12_to_gephy_sgmii + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ val |= SYSCFG0_SGMII_GMAC3_V2; ++ break; + default: + updated = false; + } +@@ -208,13 +305,25 @@ static const struct mtk_eth_muxc mtk_eth + .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY, + .set_path = set_mux_u3_gmac2_to_qphy, + }, { ++ .name = "mux_gmac2_to_2p5gphy", ++ .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY, ++ .set_path = set_mux_gmac2_to_2p5gphy, ++ }, { + .name = "mux_gmac1_gmac2_to_sgmii_rgmii", + .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII, + .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, + }, { + .name = "mux_gmac12_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII, +- .set_path = set_mux_gmac12_to_gephy_sgmii, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_gephy_sgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_usxgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII, ++ .set_path = set_mux_gmac123_to_usxgmii, + }, + }; + +@@ -243,16 +352,46 @@ static int mtk_eth_mux_setup(struct mtk_ + } + } + ++ dev_dbg(eth->dev, "leaving mux_setup %s\n", ++ mtk_eth_path_name(path)); ++ + out: + return err; + } + ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path; ++ ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_USXGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_USXGMII : ++ MTK_ETH_PATH_GMAC3_USXGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) + { + u64 path; + +- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : +- MTK_ETH_PATH_GMAC2_SGMII; ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII : ++ MTK_ETH_PATH_GMAC3_SGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path = 0; ++ ++ if (mac_id == MTK_GMAC2_ID) ++ path = MTK_ETH_PATH_GMAC2_2P5GPHY; ++ ++ if (!path) ++ return -EINVAL; + + /* Setup proper MUXes along the path */ + return mtk_eth_mux_setup(eth, path); +@@ -282,4 +421,3 @@ int mtk_gmac_rgmii_path_setup(struct mtk + /* Setup proper MUXes along the path */ + return mtk_eth_mux_setup(eth, path); + } +- +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -480,6 +480,23 @@ static void mtk_gmac0_rgmii_adjust(struc + mtk_w32(eth, val, TRGMII_TCK_CTRL); + } + ++static void mtk_setup_bridge_switch(struct mtk_eth *eth) ++{ ++ int val; ++ ++ /* Force Port1 XGMAC Link Up */ ++ val = mtk_r32(eth, MTK_XGMAC_STS(MTK_GMAC1_ID)); ++ mtk_w32(eth, val | MTK_XGMAC_FORCE_LINK(MTK_GMAC1_ID), ++ MTK_XGMAC_STS(MTK_GMAC1_ID)); ++ ++ /* Adjust GSW bridge IPG to 11*/ ++ val = mtk_r32(eth, MTK_GSW_CFG); ++ val &= ~(GSWTX_IPG_MASK | GSWRX_IPG_MASK); ++ val |= (GSW_IPG_11 << GSWTX_IPG_SHIFT) | ++ (GSW_IPG_11 << GSWRX_IPG_SHIFT); ++ mtk_w32(eth, val, MTK_GSW_CFG); ++} ++ + static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) + { +@@ -494,6 +511,12 @@ static struct phylink_pcs *mtk_mac_selec + 0 : mac->id; + + return eth->sgmii_pcs[sid]; ++ } else if ((interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GKR || ++ interface == PHY_INTERFACE_MODE_5GBASER) && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ mac->id != MTK_GMAC1_ID) { ++ return mtk_usxgmii_select_pcs(eth, mac->id); + } + + return NULL; +@@ -505,7 +528,7 @@ static void mtk_mac_config(struct phylin + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; +- int val, ge_mode, err = 0; ++ int val, ge_mode, force_link, err = 0; + u32 i; + + /* MT76x8 has no hardware settings between for the MAC */ +@@ -549,6 +572,23 @@ static void mtk_mac_config(struct phylin + goto init_err; + } + break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_5GBASER: ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { ++ err = mtk_gmac_usxgmii_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } ++ break; ++ case PHY_INTERFACE_MODE_INTERNAL: ++ if (mac->id == MTK_GMAC2_ID && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) { ++ err = mtk_gmac_2p5gphy_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } ++ break; + default: + goto err_phy; + } +@@ -627,14 +667,78 @@ static void mtk_mac_config(struct phylin + SYSCFG0_SGMII_MASK, + ~(u32)SYSCFG0_SGMII_MASK); + ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ mtk_xfi_pll_enable(eth); ++ mtk_sgmii_reset(eth, mac->id); ++ if (phylink_autoneg_inband(mode)) ++ mtk_sgmii_setup_phya_gen1(eth, mac->id); ++ else ++ mtk_sgmii_setup_phya_gen2(eth, mac->id); ++ } + /* Save the syscfg0 value for mac_finish */ + mac->syscfg0 = val; +- } else if (phylink_autoneg_inband(mode)) { ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GKR && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { + dev_err(eth->dev, +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); + return; + } + ++ /* Setup gmac */ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ (mtk_interface_mode_is_xgmii(state->interface) || ++ mac->interface == PHY_INTERFACE_MODE_INTERNAL)) { ++ mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); ++ mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); ++ ++ switch (mac->id) { ++ case MTK_GMAC1_ID: ++ mtk_setup_bridge_switch(eth); ++ break; ++ case MTK_GMAC2_ID: ++ force_link = (mac->interface == ++ PHY_INTERFACE_MODE_INTERNAL) ? ++ MTK_XGMAC_FORCE_LINK(mac->id) : 0; ++ val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); ++ mtk_w32(eth, val | force_link, ++ MTK_XGMAC_STS(mac->id)); ++ break; ++ case MTK_GMAC3_ID: ++ val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); ++ mtk_w32(eth, val | MTK_XGMAC_FORCE_LINK(mac->id), ++ MTK_XGMAC_STS(mac->id)); ++ break; ++ } ++ } else { ++ val = mtk_r32(eth, MTK_GDMA_EG_CTRL(mac->id)); ++ mtk_w32(eth, val & ~MTK_GDMA_XGDM_SEL, ++ MTK_GDMA_EG_CTRL(mac->id)); ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ switch (mac->id) { ++ case MTK_GMAC2_ID: ++ case MTK_GMAC3_ID: ++ val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); ++ mtk_w32(eth, ++ val & ~MTK_XGMAC_FORCE_LINK(mac->id), ++ MTK_XGMAC_STS(mac->id)); ++ break; ++ } ++ } ++ ++/* ++ if (mac->type != mac_type) { ++ if (atomic_read(&reset_pending) == 0) { ++ atomic_inc(&force); ++ schedule_work(ð->pending_work); ++ atomic_inc(&reset_pending); ++ } else ++ atomic_dec(&reset_pending); ++ } ++*/ ++ } + return; + + err_phy: +@@ -675,11 +779,40 @@ static int mtk_mac_finish(struct phylink + return 0; + } + +-static void mtk_mac_pcs_get_state(struct phylink_config *config, ++static void mtk_xgdm_pcs_get_state(struct mtk_mac *mac, ++ struct phylink_link_state *state) ++{ ++ u32 sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); ++ ++ if (mac->id == MTK_GMAC2_ID) ++ sts = sts >> 16; ++ ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); ++ if (!state->link) ++ return; ++ ++ state->duplex = DUPLEX_FULL; ++ state->interface = mac->interface; ++ ++ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, sts)) { ++ case 0: ++ state->speed = SPEED_10000; ++ break; ++ case 1: ++ state->speed = SPEED_5000; ++ break; ++ case 2: ++ state->speed = SPEED_2500; ++ break; ++ case 3: ++ state->speed = SPEED_1000; ++ break; ++ } ++} ++ ++static void mtk_gdm_pcs_get_state(struct mtk_mac *mac, + struct phylink_link_state *state) + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); + u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id)); + + state->link = (pmsr & MAC_MSR_LINK); +@@ -707,15 +840,35 @@ static void mtk_mac_pcs_get_state(struct + state->pause |= MLO_PAUSE_TX; + } + ++static void mtk_mac_pcs_get_state(struct phylink_config *config, ++ struct phylink_link_state *state) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mtk_interface_mode_is_xgmii(state->interface)) ++ mtk_xgdm_pcs_get_state(mac, state); ++ else ++ mtk_gdm_pcs_get_state(mac, state); ++} ++ + static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ u32 mcr; + +- mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); +- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ if (!mtk_interface_mode_is_xgmii(interface)) { ++ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); ++ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ } else if (mac->id != MTK_GMAC1_ID) { ++ mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); ++ mcr &= 0xfffffff0; ++ mcr |= XMAC_MCR_TRX_DISABLE; ++ mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); ++ } + } + + static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, +@@ -787,13 +940,11 @@ static void mtk_set_queue_speed(struct m + mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); + } + +-static void mtk_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, bool tx_pause, bool rx_pause) ++static void mtk_gdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); + u32 mcr; + + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); +@@ -827,6 +978,47 @@ static void mtk_mac_link_up(struct phyli + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } + ++static void mtk_xgdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ u32 mcr; ++ ++ if (mac->id == MTK_GMAC1_ID) ++ return; ++ ++ mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); ++ ++ mcr &= ~(XMAC_MCR_FORCE_TX_FC | XMAC_MCR_FORCE_RX_FC); ++ /* Configure pause modes - ++ * phylink will avoid these for half duplex ++ */ ++ if (tx_pause) ++ mcr |= XMAC_MCR_FORCE_TX_FC; ++ if (rx_pause) ++ mcr |= XMAC_MCR_FORCE_RX_FC; ++ ++ mcr &= ~(XMAC_MCR_TRX_DISABLE); ++ mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); ++} ++ ++static void mtk_mac_link_up(struct phylink_config *config, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mtk_interface_mode_is_xgmii(interface)) ++ mtk_xgdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++ else ++ mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++} ++ + static const struct phylink_mac_ops mtk_phylink_ops = { + .validate = phylink_generic_validate, + .mac_select_pcs = mtk_mac_select_pcs, +@@ -880,10 +1072,21 @@ static int mtk_mdio_init(struct mtk_eth + } + divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); + ++ /* Configure MDC Turbo Mode */ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ val = mtk_r32(eth, MTK_MAC_MISC_V3); ++ val |= MISC_MDC_TURBO; ++ mtk_w32(eth, val, MTK_MAC_MISC_V3); ++ } else { ++ val = mtk_r32(eth, MTK_PPSC); ++ val |= PPSC_MDC_TURBO; ++ mtk_w32(eth, val, MTK_PPSC); ++ } ++ + /* Configure MDC Divider */ + val = mtk_r32(eth, MTK_PPSC); + val &= ~PPSC_MDC_CFG; +- val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO; ++ val |= FIELD_PREP(PPSC_MDC_CFG, divider); + mtk_w32(eth, val, MTK_PPSC); + + dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); +@@ -4487,8 +4690,8 @@ static int mtk_add_mac(struct mtk_eth *e + const __be32 *_id = of_get_property(np, "reg", NULL); + phy_interface_t phy_mode; + struct phylink *phylink; +- struct mtk_mac *mac; + int id, err; ++ struct mtk_mac *mac; + int txqs = 1; + + if (!_id) { +@@ -4579,6 +4782,32 @@ static int mtk_add_mac(struct mtk_eth *e + mac->phylink_config.supported_interfaces); + } + ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII)) { ++ if (id == MTK_GMAC1_ID) { ++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | ++ MAC_SYM_PAUSE | ++ MAC_10000FD; ++ phy_interface_zero( ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ mac->phylink_config.supported_interfaces); ++ } else { ++ mac->phylink_config.mac_capabilities |= MAC_5000FD | MAC_10000FD; ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GKR, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ mac->phylink_config.supported_interfaces); ++ } ++ } ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY)) { ++ if (id == MTK_GMAC2_ID) ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ mac->phylink_config.supported_interfaces); ++ } ++ + phylink = phylink_create(&mac->phylink_config, + of_fwnode_handle(mac->of_node), + phy_mode, &mtk_phylink_ops); +@@ -4766,6 +4995,13 @@ static int mtk_probe(struct platform_dev + + if (err) + return err; ++ } ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { ++ err = mtk_usxgmii_init(eth); ++ ++ if (err) ++ return err; + } + + if (eth->soc->required_pctl) { +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -126,6 +126,11 @@ + #define MTK_GDMA_TO_PDMA 0x0 + #define MTK_GDMA_DROP_ALL 0x7777 + ++/* GDM Egress Control Register */ ++#define MTK_GDMA_EG_CTRL(x) ((x == MTK_GMAC3_ID) ? \ ++ 0x544 : 0x504 + (x * 0x1000)) ++#define MTK_GDMA_XGDM_SEL BIT(31) ++ + /* Unicast Filter MAC Address Register - Low */ + #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) + +@@ -389,7 +394,26 @@ + #define PHY_IAC_TIMEOUT HZ + + #define MTK_MAC_MISC 0x1000c ++#define MTK_MAC_MISC_V3 0x10010 + #define MTK_MUX_TO_ESW BIT(0) ++#define MISC_MDC_TURBO BIT(4) ++ ++/* XMAC status registers */ ++#define MTK_XGMAC_STS(x) ((x == MTK_GMAC3_ID) ? 0x1001C : 0x1000C) ++#define MTK_XGMAC_FORCE_LINK(x) ((x == MTK_GMAC2_ID) ? BIT(31) : BIT(15)) ++#define MTK_USXGMII_PCS_LINK BIT(8) ++#define MTK_XGMAC_RX_FC BIT(5) ++#define MTK_XGMAC_TX_FC BIT(4) ++#define MTK_USXGMII_PCS_MODE GENMASK(3, 1) ++#define MTK_XGMAC_LINK_STS BIT(0) ++ ++/* GSW bridge registers */ ++#define MTK_GSW_CFG (0x10080) ++#define GSWTX_IPG_MASK GENMASK(19, 16) ++#define GSWTX_IPG_SHIFT 16 ++#define GSWRX_IPG_MASK GENMASK(3, 0) ++#define GSWRX_IPG_SHIFT 0 ++#define GSW_IPG_11 11 + + /* Mac control registers */ + #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) +@@ -414,6 +438,17 @@ + #define MAC_MCR_FORCE_LINK BIT(0) + #define MAC_MCR_FORCE_LINK_DOWN (MAC_MCR_FORCE_MODE) + ++/* Mac EEE control registers */ ++#define MTK_MAC_EEE(x) (0x10104 + (x * 0x100)) ++#define MAC_EEE_WAKEUP_TIME_1000 GENMASK(31, 24) ++#define MAC_EEE_WAKEUP_TIME_100 GENMASK(23, 16) ++#define MAC_EEE_LPI_TXIDLE_THD GENMASK(15, 8) ++#define MAC_EEE_RESV0 GENMASK(7, 4) ++#define MAC_EEE_CKG_TXILDE BIT(3) ++#define MAC_EEE_CKG_RXLPI BIT(2) ++#define MAC_EEE_TX_DOWN_REQ BIT(1) ++#define MAC_EEE_LPI_MODE BIT(0) ++ + /* Mac status registers */ + #define MTK_MAC_MSR(x) (0x10108 + (x * 0x100)) + #define MAC_MSR_EEE1G BIT(7) +@@ -458,6 +493,12 @@ + #define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) + #define INTF_MODE_RGMII_10_100 0 + ++/* XFI Mac control registers */ ++#define MTK_XMAC_MCR(x) (0x12000 + ((x - 1) * 0x1000)) ++#define XMAC_MCR_TRX_DISABLE 0xf ++#define XMAC_MCR_FORCE_TX_FC BIT(5) ++#define XMAC_MCR_FORCE_RX_FC BIT(4) ++ + /* GPIO port control registers for GMAC 2*/ + #define GPIO_OD33_CTRL8 0x4c0 + #define GPIO_BIAS_CTRL 0xed0 +@@ -483,6 +524,7 @@ + #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) + #define SYSCFG0_SGMII_GMAC1_V2 BIT(9) + #define SYSCFG0_SGMII_GMAC2_V2 BIT(8) ++#define SYSCFG0_SGMII_GMAC3_V2 BIT(7) + + + /* ethernet subsystem clock register */ +@@ -509,16 +551,91 @@ + #define ETHSYS_DMA_AG_MAP_QDMA BIT(1) + #define ETHSYS_DMA_AG_MAP_PPE BIT(2) + ++/* USXGMII subsystem config registers */ ++/* Register to control speed */ ++#define RG_PHY_TOP_SPEED_CTRL1 0x80C ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_RX_MODE_10G 0 ++#define USXGMII_XFI_RX_MODE_5G 1 ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_TX_MODE_10G 0 ++#define USXGMII_XFI_TX_MODE_5G 1 ++ ++/* Register to control PCS AN */ ++#define RG_PCS_AN_CTRL0 0x810 ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA_SPEED_MASK GENMASK(11, 9) ++#define USXGMII_LPA_SPEED_10 0 ++#define USXGMII_LPA_SPEED_100 1 ++#define USXGMII_LPA_SPEED_1000 2 ++#define USXGMII_LPA_SPEED_10000 3 ++#define USXGMII_LPA_SPEED_2500 4 ++#define USXGMII_LPA_SPEED_5000 5 ++#define USXGMII_LPA_DUPLEX BIT(12) ++#define USXGMII_LPA_LINK BIT(15) ++#define USXGMII_LPA_LATCH BIT(31) ++ ++/* Register to control USXGMII XFI PLL digital */ ++#define XFI_PLL_DIG_GLB8 0x08 ++#define RG_XFI_PLL_EN BIT(31) ++ ++/* Register to control USXGMII XFI PLL analog */ ++#define XFI_PLL_ANA_GLB8 0x108 ++#define RG_XFI_PLL_ANA_SWWA 0x02283248 ++ + /* Infrasys subsystem config registers */ + #define INFRA_MISC2 0x70c + #define CO_QPHY_SEL BIT(0) + #define GEPHY_MAC_SEL BIT(1) + ++/* Toprgu subsystem config registers */ ++#define TOPRGU_SWSYSRST 0x18 ++#define SWSYSRST_UNLOCK_KEY GENMASK(31, 24) ++#define SWSYSRST_XFI_PLL_GRST BIT(16) ++#define SWSYSRST_XFI_PEXPT1_GRST BIT(15) ++#define SWSYSRST_XFI_PEXPT0_GRST BIT(14) ++#define SWSYSRST_XFI1_GRST BIT(13) ++#define SWSYSRST_XFI0_GRST BIT(12) ++#define SWSYSRST_SGMII1_GRST BIT(2) ++#define SWSYSRST_SGMII0_GRST BIT(1) ++#define TOPRGU_SWSYSRST_EN 0xFC ++ + /* Top misc registers */ ++#define TOP_MISC_NETSYS_PCS_MUX 0x84 ++#define NETSYS_PCS_MUX_MASK GENMASK(1, 0) ++#define MUX_G2_USXGMII_SEL BIT(1) ++#define MUX_HSGMII1_G1_SEL BIT(0) ++ + #define USB_PHY_SWITCH_REG 0x218 + #define QPHY_SEL_MASK GENMASK(1, 0) + #define SGMII_QPHY_SEL 0x2 + ++/* MDIO control */ ++#define MII_MMD_ACC_CTL_REG 0x0d ++#define MII_MMD_ADDR_DATA_REG 0x0e ++#define MMD_OP_MODE_DATA BIT(14) ++ + /* MT7628/88 specific stuff */ + #define MT7628_PDMA_OFFSET 0x0800 + #define MT7628_SDM_OFFSET 0x0c00 +@@ -812,13 +929,6 @@ enum mtk_gmac_id { + MTK_GMAC_ID_MAX + }; + +-/* GDM Type */ +-enum mtk_gdm_type { +- MTK_GDM_TYPE = 0, +- MTK_XGDM_TYPE, +- MTK_GDM_TYPE_MAX +-}; +- + enum mtk_tx_buf_type { + MTK_TYPE_SKB, + MTK_TYPE_XDP_TX, +@@ -905,6 +1015,7 @@ enum mkt_eth_capabilities { + MTK_TRGMII_BIT, + MTK_SGMII_BIT, + MTK_USXGMII_BIT, ++ MTK_2P5GPHY_BIT, + MTK_ESW_BIT, + MTK_GEPHY_BIT, + MTK_MUX_BIT, +@@ -925,6 +1036,7 @@ enum mkt_eth_capabilities { + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, + MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, + MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, ++ MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT, + MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, + MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, +@@ -936,6 +1048,7 @@ enum mkt_eth_capabilities { + MTK_ETH_PATH_GMAC1_SGMII_BIT, + MTK_ETH_PATH_GMAC2_RGMII_BIT, + MTK_ETH_PATH_GMAC2_SGMII_BIT, ++ MTK_ETH_PATH_GMAC2_2P5GPHY_BIT, + MTK_ETH_PATH_GMAC2_GEPHY_BIT, + MTK_ETH_PATH_GMAC3_SGMII_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, +@@ -949,6 +1062,7 @@ enum mkt_eth_capabilities { + #define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) + #define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) + #define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) ++#define MTK_2P5GPHY BIT_ULL(MTK_2P5GPHY_BIT) + #define MTK_ESW BIT_ULL(MTK_ESW_BIT) + #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) + #define MTK_MUX BIT_ULL(MTK_MUX_BIT) +@@ -971,6 +1085,8 @@ enum mkt_eth_capabilities { + BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) + #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ + BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY \ ++ BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT) + #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ +@@ -986,6 +1102,7 @@ enum mkt_eth_capabilities { + #define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) + #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) + #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_2P5GPHY BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT) + #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) + #define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) + #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) +@@ -999,6 +1116,7 @@ enum mkt_eth_capabilities { + #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) + #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) + #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) ++#define MTK_GMAC2_2P5GPHY (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY) + #define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) + #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) + #define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) +@@ -1022,6 +1140,10 @@ enum mkt_eth_capabilities { + (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ + MTK_SHARED_SGMII) + ++/* 2: GMAC2 -> XGMII */ ++#define MTK_MUX_GMAC2_TO_2P5GPHY \ ++ (MTK_ETH_MUX_GMAC2_TO_2P5GPHY | MTK_MUX | MTK_INFRA) ++ + /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ + #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) +@@ -1080,7 +1202,8 @@ enum mkt_eth_capabilities { + MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ + MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | \ + MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \ +- MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII) ++ MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII | \ ++ MTK_GMAC2_2P5GPHY | MTK_MUX_GMAC2_TO_2P5GPHY) + + struct mtk_tx_dma_desc_info { + dma_addr_t addr; +@@ -1186,6 +1309,22 @@ struct mtk_soc_data { + + #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000) + ++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii regmap and ++ * associated data ++ * @regmap: The register map pointing at the range used to setup ++ * USXGMII modes ++ * @interface: Currently selected interface mode ++ * @id: The element is used to record the index of PCS ++ * @pcs: Phylink PCS structure ++ */ ++struct mtk_usxgmii_pcs { ++ struct mtk_eth *eth; ++ struct regmap *regmap; ++ phy_interface_t interface; ++ u8 id; ++ struct phylink_pcs pcs; ++}; ++ + /* struct mtk_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer +@@ -1206,6 +1345,11 @@ struct mtk_soc_data { + * @infra: The register map pointing at the range used to setup + * SGMII and GePHY path + * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances ++ * @usxgmii_pll: The register map pointing at the range used to control ++ * the USXGMII SerDes PLL ++ * @regmap_pextp: The register map pointing at the range used to setup ++ * PHYA ++ * @usxgmii_pcs: Pointer to array of pointers to struct for USXGMII PCS + * @pctl: The register map pointing at the range used to setup + * GMAC port drive/slew values + * @dma_refcnt: track how many netdevs are using the DMA engine +@@ -1247,7 +1391,11 @@ struct mtk_eth { + unsigned long sysclk; + struct regmap *ethsys; + struct regmap *infra; ++ struct regmap *toprgu; + struct phylink_pcs **sgmii_pcs; ++ struct regmap *usxgmii_pll; ++ struct regmap **regmap_pextp; ++ struct mtk_usxgmii_pcs **usxgmii_pcs; + struct regmap *pctl; + bool hwlro; + refcount_t dma_refcnt; +@@ -1403,6 +1551,19 @@ static inline u32 mtk_get_ib2_multicast_ + return MTK_FOE_IB2_MULTICAST; + } + ++static inline bool mtk_interface_mode_is_xgmii(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_5GBASER: ++ return true; ++ break; ++ default: ++ return false; ++ } ++} ++ + /* read the hardware status register */ + void mtk_stats_update_mac(struct mtk_mac *mac); + +@@ -1410,8 +1571,10 @@ void mtk_w32(struct mtk_eth *eth, u32 va + u32 mtk_r32(struct mtk_eth *eth, unsigned reg); + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id); + + int mtk_eth_offload_init(struct mtk_eth *eth); + int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, +@@ -1421,5 +1584,20 @@ int mtk_flow_offload_cmd(struct mtk_eth + void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list); + void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); + ++#ifdef CONFIG_NET_MEDIATEK_SOC_USXGMII ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id); ++int mtk_usxgmii_init(struct mtk_eth *eth); ++int mtk_xfi_pll_enable(struct mtk_eth *eth); ++void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id); ++void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id); ++void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id); ++#else ++static inline struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id) { return NULL; } ++static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; } ++static inline int mtk_xfi_pll_enable(struct mtk_eth *eth) { return 0; } ++static inline void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) { } ++#endif /* NET_MEDIATEK_SOC_USXGMII */ + + #endif /* MTK_ETH_H */ +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_usxgmii.c +@@ -0,0 +1,835 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (c) 2022 MediaTek Inc. ++ * Author: Henry Yen ++ * Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include "mtk_eth_soc.h" ++ ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); ++} ++ ++static int mtk_xfi_pextp_init(struct mtk_eth *eth) ++{ ++ struct device *dev = eth->dev; ++ struct device_node *r = dev->of_node; ++ struct device_node *np; ++ int i; ++ ++ eth->regmap_pextp = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->regmap_pextp), GFP_KERNEL); ++ if (!eth->regmap_pextp) ++ return -ENOMEM; ++ ++ for (i = 0; i < eth->soc->num_devs; i++) { ++ np = of_parse_phandle(r, "mediatek,xfi_pextp", i); ++ if (!np) ++ break; ++ ++ eth->regmap_pextp[i] = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->regmap_pextp[i])) ++ return PTR_ERR(eth->regmap_pextp[i]); ++ } ++ ++ return 0; ++} ++ ++static int mtk_xfi_pll_init(struct mtk_eth *eth) ++{ ++ struct device_node *r = eth->dev->of_node; ++ struct device_node *np; ++ ++ np = of_parse_phandle(r, "mediatek,xfi_pll", 0); ++ if (!np) ++ return -1; ++ ++ eth->usxgmii_pll = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pll)) ++ return PTR_ERR(eth->usxgmii_pll); ++ ++ return 0; ++} ++ ++static int mtk_toprgu_init(struct mtk_eth *eth) ++{ ++ struct device_node *r = eth->dev->of_node; ++ struct device_node *np; ++ ++ np = of_parse_phandle(r, "mediatek,toprgu", 0); ++ if (!np) ++ return -1; ++ ++ eth->toprgu = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->toprgu)) ++ return PTR_ERR(eth->toprgu); ++ ++ return 0; ++} ++ ++int mtk_xfi_pll_enable(struct mtk_eth *eth) ++{ ++ u32 val = 0; ++ ++ if (!eth->usxgmii_pll) ++ return -EINVAL; ++ ++ /* Add software workaround for USXGMII PLL TCL issue */ ++ regmap_write(eth->usxgmii_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); ++ ++ regmap_read(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, &val); ++ val |= RG_XFI_PLL_EN; ++ regmap_write(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, val); ++ ++ return 0; ++} ++ ++static int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id) ++{ ++ int xgmii_id = mac_id; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ switch (mac_id) { ++ case MTK_GMAC1_ID: ++ case MTK_GMAC2_ID: ++ xgmii_id = 1; ++ break; ++ case MTK_GMAC3_ID: ++ xgmii_id = 0; ++ break; ++ default: ++ xgmii_id = -1; ++ } ++ } ++ ++ return xgmii_id; ++} ++ ++static int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id) ++{ ++ int mac_id = xgmii_id; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ switch (xgmii_id) { ++ case 0: ++ mac_id = 2; ++ break; ++ case 1: ++ mac_id = 1; ++ break; ++ default: ++ mac_id = -1; ++ } ++ } ++ ++ return mac_id; ++} ++ ++ ++static void mtk_usxgmii_setup_phya_usxgmii(struct mtk_usxgmii_pcs *mpcs) ++{ ++ struct regmap *pextp; ++ ++ if (!mpcs->eth) ++ return; ++ ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0202C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++static void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs) ++{ ++ struct regmap *pextp; ++ ++ if (!mpcs->eth) ++ return; ++ ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAAA5A5AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C018AA); ++ regmap_write(pextp, 0x50E0, 0x3777812B); ++ regmap_write(pextp, 0x506C, 0x005C9CFF); ++ regmap_write(pextp, 0x5070, 0x9DFAFAFA); ++ regmap_write(pextp, 0x5074, 0x273F3F3F); ++ regmap_write(pextp, 0x5078, 0xA8883868); ++ regmap_write(pextp, 0x507C, 0x14661466); ++ regmap_write(pextp, 0x5080, 0x0E001ABF); ++ regmap_write(pextp, 0x5084, 0x080B0D0D); ++ regmap_write(pextp, 0x5088, 0x02050909); ++ regmap_write(pextp, 0x50E4, 0x0C000000); ++ regmap_write(pextp, 0x50E8, 0x04000000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x50808C8C); ++ regmap_write(pextp, 0x6004, 0x18000000); ++ regmap_write(pextp, 0x00F8, 0x00A132A1); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000003); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0202C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++static void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs) ++{ ++ struct regmap *pextp; ++ ++ if (!mpcs->eth) ++ return; ++ ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x47684100); ++ regmap_write(pextp, 0x3050, 0x00000000); ++ regmap_write(pextp, 0x3054, 0x00000000); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ if (mpcs->id == 0) ++ regmap_write(pextp, 0xA008, 0x0007B400); ++ ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0202C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) ++{ ++ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; ++ ++ if (id >= eth->soc->num_devs) ++ return; ++ ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020207); ++ regmap_write(pextp, 0x2034, 0x0E05050F); ++ regmap_write(pextp, 0x2040, 0x00200032); ++ regmap_write(pextp, 0x50F0, 0x00C014BA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000EAF); ++ regmap_write(pextp, 0x5084, 0x08080E0D); ++ regmap_write(pextp, 0x5088, 0x02030B09); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0606); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x00FA32FA); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3064, 0x0000C000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x20200F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000007); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0201C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) ++{ ++ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; ++ ++ if (id >= eth->soc->num_devs) ++ return; ++ ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x009C329C); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); ++ ndelay(1020); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x22000F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000005); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); ++ udelay(150); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0200C101); ++ udelay(15); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); ++ ndelay(1020); ++ regmap_write(pextp, 0x0070, 0x0201C101); ++ udelay(100); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); ++ udelay(400); ++} ++ ++static void mtk_usxgmii_reset(struct mtk_eth *eth, int id) ++{ ++ u32 val = 0; ++ ++ if (id >= eth->soc->num_devs || !eth->toprgu) ++ return; ++ ++ switch (id) { ++ case 0: ++ /* Enable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val |= SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ ++ /* Assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | ++ SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ udelay(100); ++ ++ /* De-assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ /* Disable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ break; ++ case 1: ++ /* Enable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val |= SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ ++ /* Assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | ++ SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ udelay(100); ++ ++ /* De-assert USXGMII reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); ++ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); ++ ++ /* Disable software reset */ ++ regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); ++ regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); ++ break; ++ } ++ ++ mdelay(10); ++} ++ ++void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) ++{ ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); ++ ++ mtk_usxgmii_reset(eth, xgmii_id); ++} ++ ++ ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | ++ USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GKR) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else ++ return -EINVAL; ++ ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); ++ ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ mtk_xfi_pll_enable(eth); ++ mtk_usxgmii_reset(eth, mpcs->id); ++ ++ /* Setup USXGMII AN ctrl */ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ /* Gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ udelay(1); ++ ++ /* Un-gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, 0); ++ ++ udelay(1); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, 0); ++ ++ /* Setup USXGMIISYS with the determined property */ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) ++ mtk_usxgmii_setup_phya_usxgmii(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_10GKR) ++ mtk_usxgmii_setup_phya_10gbaser(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_5GBASER) ++ mtk_usxgmii_setup_phya_5gbaser(mpcs); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)]; ++ u32 val = 0; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ if (FIELD_GET(USXGMII_AN_ENABLE, val)) { ++ /* Refresh LPA by inverting LPA_LATCH */ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0, ++ USXGMII_LPA_LATCH, ++ !(val & USXGMII_LPA_LATCH)); ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(USXGMII_LPA_LINK, val); ++ state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val); ++ ++ switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) { ++ case USXGMII_LPA_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case USXGMII_LPA_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case USXGMII_LPA_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ case USXGMII_LPA_SPEED_2500: ++ state->speed = SPEED_2500; ++ break; ++ case USXGMII_LPA_SPEED_5000: ++ state->speed = SPEED_5000; ++ break; ++ case USXGMII_LPA_SPEED_10000: ++ state->speed = SPEED_10000; ++ break; ++ } ++ } else { ++ val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); ++ ++ if (mac->id == MTK_GMAC2_ID) ++ val = val >> 16; ++ ++ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) { ++ case 0: ++ state->speed = SPEED_10000; ++ break; ++ case 1: ++ state->speed = SPEED_5000; ++ break; ++ case 2: ++ state->speed = SPEED_2500; ++ break; ++ case 3: ++ state->speed = SPEED_1000; ++ break; ++ } ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val); ++ state->duplex = DUPLEX_FULL; ++ } ++ ++ if (state->link == 0) ++ mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND, ++ state->interface, NULL, false); ++} ++ ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int val = 0; ++ ++ if (!mpcs->regmap) ++ return; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ val |= USXGMII_AN_RESTART; ++ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, mode, ++ interface, NULL, false); ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++}; ++ ++int mtk_usxgmii_init(struct mtk_eth *eth) ++{ ++ struct device_node *r = eth->dev->of_node; ++ struct device *dev = eth->dev; ++ struct device_node *np; ++ int i, ret; ++ ++ eth->usxgmii_pcs = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs) ++ return -ENOMEM; ++ ++ for (i = 0; i < eth->soc->num_devs; i++) { ++ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); ++ if (!np) ++ break; ++ ++ eth->usxgmii_pcs[i] = devm_kzalloc(dev, sizeof(*eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs[i]) ++ return -ENOMEM; ++ ++ eth->usxgmii_pcs[i]->id = i; ++ eth->usxgmii_pcs[i]->eth = eth; ++ eth->usxgmii_pcs[i]->regmap = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pcs[i]->regmap)) ++ return PTR_ERR(eth->usxgmii_pcs[i]->regmap); ++ ++ eth->usxgmii_pcs[i]->pcs.ops = &mtk_usxgmii_pcs_ops; ++ eth->usxgmii_pcs[i]->pcs.poll = true; ++ eth->usxgmii_pcs[i]->interface = PHY_INTERFACE_MODE_NA; ++ ++ of_node_put(np); ++ } ++ ++ ret = mtk_xfi_pextp_init(eth); ++ if (ret) ++ return ret; ++ ++ ret = mtk_xfi_pll_init(eth); ++ if (ret) ++ return ret; ++ ++ return mtk_toprgu_init(eth); ++} ++ ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int mac_id) ++{ ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); ++ ++ if (!eth->usxgmii_pcs[xgmii_id]->regmap) ++ return NULL; ++ ++ return ð->usxgmii_pcs[xgmii_id]->pcs; ++} diff --git a/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch b/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch index 0e0af2a718b..26f40d9f87b 100644 --- a/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch +++ b/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch @@ -16,7 +16,7 @@ Signed-off-by: David Bauer --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2086,10 +2086,13 @@ mt7530_setup_mdio(struct mt7530_priv *pr +@@ -2126,10 +2126,13 @@ mt7530_setup_mdio(struct mt7530_priv *pr { struct dsa_switch *ds = priv->ds; struct device *dev = priv->dev; @@ -30,7 +30,7 @@ Signed-off-by: David Bauer bus = devm_mdiobus_alloc(dev); if (!bus) return -ENOMEM; -@@ -2106,7 +2109,9 @@ mt7530_setup_mdio(struct mt7530_priv *pr +@@ -2146,7 +2149,9 @@ mt7530_setup_mdio(struct mt7530_priv *pr if (priv->irq) mt7530_setup_mdio_irq(priv); diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network index 28648c0db99..b131d0d6182 100644 --- a/target/linux/ipq40xx/base-files/etc/board.d/02_network +++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network @@ -82,6 +82,7 @@ ipq40xx_setup_interfaces() ucidef_set_interface_lan "sw-eth1 sw-eth2" ;; aruba,ap-303h|\ + buffalo,wtr-m2133hp|\ ezviz,cs-w3-wd1200g-eup|\ netgear,rbr50|\ netgear,rbs50|\ @@ -97,6 +98,12 @@ ipq40xx_setup_interfaces() plasmacloud,pa2200) ucidef_set_interfaces_lan_wan "ethernet1" "ethernet2" ;; + qxwlan,e2600ac-c1) + ucidef_set_interfaces_lan_wan "sw-eth1" "sw-eth2" + ;; + qxwlan,e2600ac-c2) + ucidef_set_interfaces_lan_wan "sw-eth1 sw-eth2" "sw-eth3" + ;; zte,mf286d) ucidef_set_interfaces_lan_wan "lan2 lan3 lan4" "wan" ;; diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c1.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c1.dts index f9205c28fa4..70e05c7eef3 100644 --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c1.dts +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c1.dts @@ -79,6 +79,14 @@ precal_art_5000: precal@5000 { reg = <0x5000 0x2f20>; }; + + macaddr_gmac0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_gmac1: macaddr@6 { + reg = <0x6 0x6>; + }; }; partition@180000 { compatible = "denx,fit"; @@ -102,3 +110,27 @@ nvmem-cells = <&precal_art_5000>; qcom,ath10k-calibration-variant = "Qxwlan-E2600AC-C1"; }; + +&gmac { + status = "okay"; +}; + +&switch { + status = "okay"; +}; + +&swport4 { + status = "okay"; + label = "sw-eth1"; + + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_gmac0>; +}; + +&swport5 { + status = "okay"; + + label = "sw-eth2"; + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_gmac1>; +}; diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c2.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c2.dts index 1f88322a4f1..2ad794e8ecb 100644 --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c2.dts +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-e2600ac-c2.dts @@ -79,6 +79,14 @@ precal_art_5000: precal@5000 { reg = <0x5000 0x2f20>; }; + + macaddr_gmac0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_gmac1: macaddr@6 { + reg = <0x6 0x6>; + }; }; }; }; @@ -137,3 +145,35 @@ nvmem-cells = <&precal_art_5000>; qcom,ath10k-calibration-variant = "Qxwlan-E2600AC-C2"; }; + +&gmac { + status = "okay"; +}; + +&switch { + status = "okay"; +}; + +&swport2 { + status = "okay"; + label = "sw-eth1"; + + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_gmac0>; +}; + +&swport4 { + status = "okay"; + label = "sw-eth2"; + + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_gmac0>; +}; + +&swport5 { + status = "okay"; + + label = "sw-eth3"; + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_gmac1>; +}; diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-wtr-m2133hp.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-wtr-m2133hp.dts index f1d58ccba91..3260de23bd4 100644 --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-wtr-m2133hp.dts +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-wtr-m2133hp.dts @@ -417,6 +417,35 @@ qcom,ath10k-calibration-variant = "Buffalo-WTR-M2133HP"; }; +&switch { + status = "okay"; +}; + +&swport2 { + status = "okay"; + label = "lan3"; +}; + +&swport3 { + status = "okay"; + label = "lan2"; +}; + +&swport4 { + status = "okay"; + label = "lan1"; +}; + +&swport5 { + status = "okay"; +}; + +&gmac { + status = "okay"; + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_orgdata_20>; +}; + &mdio { status = "okay"; pinctrl-0 = <&mdio_pins>; diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk index 59bdd042962..59d4f082ff8 100644 --- a/target/linux/ipq40xx/image/generic.mk +++ b/target/linux/ipq40xx/image/generic.mk @@ -305,8 +305,7 @@ define Device/buffalo_wtr-m2133hp BLOCKSIZE := 128k PAGESIZE := 2048 endef -# Missing DSA Setup -#TARGET_DEVICES += buffalo_wtr-m2133hp +TARGET_DEVICES += buffalo_wtr-m2133hp define Device/cellc_rtl30vw KERNEL_SUFFIX := -zImage.itb @@ -1028,13 +1027,10 @@ define Device/qxwlan_e2600ac-c1 DEVICE_VARIANT := C1 BOARD_NAME := e2600ac-c1 SOC := qcom-ipq4019 - KERNEL_SIZE := 4096k IMAGE_SIZE := 31232k IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | append-metadata - DEFAULT := n endef -# Missing DSA Setup -#TARGET_DEVICES += qxwlan_e2600ac-c1 +TARGET_DEVICES += qxwlan_e2600ac-c1 define Device/qxwlan_e2600ac-c2 $(call Device/FitImage) @@ -1047,8 +1043,7 @@ define Device/qxwlan_e2600ac-c2 BLOCKSIZE := 128k PAGESIZE := 2048 endef -# Missing DSA Setup -#TARGET_DEVICES += qxwlan_e2600ac-c2 +TARGET_DEVICES += qxwlan_e2600ac-c2 define Device/sony_ncp-hg100-cellular $(call Device/FitImage) diff --git a/target/linux/ipq806x/image/generic.mk b/target/linux/ipq806x/image/generic.mk index 7ed2b808473..a3e9eb59e72 100644 --- a/target/linux/ipq806x/image/generic.mk +++ b/target/linux/ipq806x/image/generic.mk @@ -245,7 +245,7 @@ define Device/nec_wg2600hp3 pad-rootfs | append-metadata DEVICE_PACKAGES := -kmod-ata-ahci -kmod-ata-ahci-platform \ -kmod-usb-ohci -kmod-usb2 -kmod-usb-ledtrig-usbport \ - -kmod-usb-phy-qcom-dwc3 -kmod-usb3 -kmod-usb-dwc3-qcom \ + -kmod-phy-qcom-ipq806x-usb -kmod-usb3 -kmod-usb-dwc3-qcom \ ath10k-firmware-qca9984-ct endef TARGET_DEVICES += nec_wg2600hp3 diff --git a/target/linux/ipq807x/Makefile b/target/linux/ipq807x/Makefile index fdabf575d36..53606ef52a4 100644 --- a/target/linux/ipq807x/Makefile +++ b/target/linux/ipq807x/Makefile @@ -9,6 +9,7 @@ CPU_TYPE:=cortex-a53 SUBTARGETS:=generic KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES += \ diff --git a/target/linux/ipq807x/base-files/etc/board.d/02_network b/target/linux/ipq807x/base-files/etc/board.d/02_network index 1da5c3ae1e2..edd1e131b66 100644 --- a/target/linux/ipq807x/base-files/etc/board.d/02_network +++ b/target/linux/ipq807x/base-files/etc/board.d/02_network @@ -28,6 +28,7 @@ ipq807x_setup_interfaces() qnap,301w) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 10g-2" "10g-1" ;; + compex,wpq873|\ redmi,ax6|\ xiaomi,ax3600) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" diff --git a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata index b3564fe9c6d..ddb0737d209 100644 --- a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata +++ b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata @@ -10,6 +10,7 @@ case "$FIRMWARE" in "ath11k/IPQ8074/hw2.0/cal-ahb-c000000.wifi.bin") case "$board" in buffalo,wxr-5950ax12|\ + compex,wpq873|\ edgecore,eap102|\ edimax,cax1800|\ dynalink,dl-wrx36|\ diff --git a/target/linux/ipq807x/base-files/lib/upgrade/platform.sh b/target/linux/ipq807x/base-files/lib/upgrade/platform.sh index 11717d6324d..f7c47723150 100644 --- a/target/linux/ipq807x/base-files/lib/upgrade/platform.sh +++ b/target/linux/ipq807x/base-files/lib/upgrade/platform.sh @@ -66,6 +66,7 @@ platform_do_upgrade() { fw_setenv upgrade_available 1 nand_do_upgrade "$1" ;; + compex,wpq873|\ edimax,cax1800|\ netgear,wax218) nand_do_upgrade "$1" diff --git a/target/linux/ipq807x/config-6.1 b/target/linux/ipq807x/config-6.1 new file mode 100644 index 00000000000..4ef752c550b --- /dev/null +++ b/target/linux/ipq807x/config-6.1 @@ -0,0 +1,566 @@ +CONFIG_64BIT=y +# CONFIG_APQ_GCC_8084 is not set +# CONFIG_APQ_MMCC_8084 is not set +CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y +CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_2051678=y +CONFIG_ARM64_ERRATUM_2054223=y +CONFIG_ARM64_ERRATUM_2067961=y +CONFIG_ARM64_ERRATUM_2077057=y +CONFIG_ARM64_ERRATUM_2658417=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_SME=y +CONFIG_ARM64_SVE=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_QCOM_CPUFREQ_HW is not set +CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_BLK_PM=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_COREDUMP=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +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_FREQ_THERMAL=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRC8=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_QCE_AEAD=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set +CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set +CONFIG_CRYPTO_DEV_QCE_SHA=y +CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y +CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SM3_NEON is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEV_COREDUMP=y +CONFIG_DMADEVICES=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +CONFIG_DT_IDLE_GENPD=y +CONFIG_DT_IDLE_STATES=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FRAME_POINTER=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +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_IIO=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IPQ_APSS_6018=y +CONFIG_IPQ_APSS_PLL=y +# CONFIG_IPQ_GCC_4019 is not set +# CONFIG_IPQ_GCC_6018 is not set +# CONFIG_IPQ_GCC_806X is not set +CONFIG_IPQ_GCC_8074=y +# 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 is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAILBOX=y +# CONFIG_MAILBOX_TEST is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_IPQ4019=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 is not set +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_CQHCI=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_MODULES_USE_ELF_RELA=y +# CONFIG_MSM_GCC_8660 is not set +# CONFIG_MSM_GCC_8909 is not set +# 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_8976 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_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_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_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_NEED_SG_DMA_LENGTH=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +CONFIG_NVMEM=y +CONFIG_NVMEM_QCOM_QFPROM=y +# CONFIG_NVMEM_SPMI_SDAM is not set +CONFIG_NVMEM_SYSFS=y +CONFIG_NVMEM_U_BOOT_ENV=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +# CONFIG_PAGE_TABLE_CHECK is not set +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_PME=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PHY_QCOM_APQ8064_SATA is not set +# CONFIG_PHY_QCOM_EDP is not set +# CONFIG_PHY_QCOM_IPQ4019_USB is not set +# CONFIG_PHY_QCOM_IPQ806X_SATA is not set +# CONFIG_PHY_QCOM_IPQ806X_USB is not set +# CONFIG_PHY_QCOM_PCIE2 is not set +CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_QUSB2=y +# 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_IPQ6018 is not set +CONFIG_PINCTRL_IPQ8074=y +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8916 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_QCM2290 is not set +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +# CONFIG_PINCTRL_QCS404 is not set +# CONFIG_PINCTRL_SC7180 is not set +# CONFIG_PINCTRL_SC8280XP is not set +# CONFIG_PINCTRL_SDM660 is not set +# CONFIG_PINCTRL_SDM845 is not set +# CONFIG_PINCTRL_SM6350 is not set +# CONFIG_PINCTRL_SM6375 is not set +# CONFIG_PINCTRL_SM8150 is not set +# CONFIG_PINCTRL_SM8250 is not set +# CONFIG_PINCTRL_SM8450 is not set +CONFIG_PM=y +# CONFIG_PM8916_WATCHDOG is not set +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_MSM is not set +# CONFIG_POWER_RESET_QCOM_PON is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_PREEMPT_DYNAMIC is not set +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +# CONFIG_QCM_DISPCC_2290 is not set +# CONFIG_QCM_GCC_2290 is not set +# CONFIG_QCOM_A53PLL is not set +# CONFIG_QCOM_AOSS_QMP is not set +CONFIG_QCOM_APCS_IPC=y +CONFIG_QCOM_APM=y +# CONFIG_QCOM_APR is not set +CONFIG_QCOM_BAM_DMA=y +# CONFIG_QCOM_CLK_APCC_MSM8996 is not set +# CONFIG_QCOM_CLK_APCS_MSM8916 is not set +# CONFIG_QCOM_CLK_APCS_SDX55 is not set +# CONFIG_QCOM_COINCELL is not set +# CONFIG_QCOM_COMMAND_DB is not set +# CONFIG_QCOM_CPR is not set +# CONFIG_QCOM_EBI2 is not set +# CONFIG_QCOM_FASTRPC is not set +CONFIG_QCOM_GDSC=y +# CONFIG_QCOM_GENI_SE is not set +# CONFIG_QCOM_GSBI is not set +# CONFIG_QCOM_HFPLL is not set +# CONFIG_QCOM_ICC_BWMON is not set +# CONFIG_QCOM_IPCC is not set +# CONFIG_QCOM_LLCC is not set +CONFIG_QCOM_MDT_LOADER=y +# CONFIG_QCOM_MPM is not set +# CONFIG_QCOM_OCMEM is not set +# CONFIG_QCOM_PDC is not set +CONFIG_QCOM_PIL_INFO=y +# CONFIG_QCOM_Q6V5_ADSP is not set +CONFIG_QCOM_Q6V5_COMMON=y +# CONFIG_QCOM_Q6V5_MSS is not set +# CONFIG_QCOM_Q6V5_PAS is not set +CONFIG_QCOM_Q6V5_WCSS=y +# CONFIG_QCOM_RMTFS_MEM is not set +# CONFIG_QCOM_RPMH is not set +CONFIG_QCOM_RPROC_COMMON=y +CONFIG_QCOM_SCM=y +# CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT is not set +# CONFIG_QCOM_SMD_RPM is not set +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMEM_STATE=y +CONFIG_QCOM_SMP2P=y +# CONFIG_QCOM_SMSM is not set +CONFIG_QCOM_SOCINFO=y +# CONFIG_QCOM_SPM is not set +CONFIG_QCOM_SPMI_ADC5=y +# CONFIG_QCOM_SPMI_RRADC is not set +# CONFIG_QCOM_STATS is not set +# CONFIG_QCOM_SYSMON is not set +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_VADC_COMMON=y +# CONFIG_QCOM_WCNSS_CTRL is not set +# CONFIG_QCOM_WCNSS_PIL is not set +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_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDOMIZE_KSTACK_OFFSET=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPMI=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_CPR3=y +# CONFIG_REGULATOR_CPR3_NPU is not set +CONFIG_REGULATOR_CPR4_APSS=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_QCOM_LABIBB is not set +CONFIG_REGULATOR_QCOM_SPMI=y +# CONFIG_REGULATOR_QCOM_USB_VBUS is not set +# CONFIG_REGULATOR_VQMMC_IPQ4019 is not set +CONFIG_RELOCATABLE=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_RESET_QCOM_AOSS is not set +# CONFIG_RESET_QCOM_PDC is not set +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPMSG=y +CONFIG_RPMSG_CHAR=y +# CONFIG_RPMSG_CTRL is not set +# CONFIG_RPMSG_NS is not set +CONFIG_RPMSG_QCOM_GLINK=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_SMD=y +# CONFIG_RPMSG_TTY is not set +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +# CONFIG_SCHED_CLUSTER is not set +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_SCHED_THERMAL_PRESSURE=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_PROC_FS is not set +# CONFIG_SC_CAMCC_7280 is not set +# CONFIG_SC_DISPCC_7180 is not set +# CONFIG_SC_GCC_7180 is not set +# CONFIG_SC_GCC_8280XP is not set +# CONFIG_SC_GPUCC_7180 is not set +# CONFIG_SC_LPASSCC_7280 is not set +# CONFIG_SC_LPASS_CORECC_7180 is not set +# CONFIG_SC_LPASS_CORECC_7280 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_SDX_GCC_65 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_SG_POOL=y +# CONFIG_SHADOW_CALL_STACK is not set +CONFIG_SMP=y +# CONFIG_SM_CAMCC_8450 is not set +# CONFIG_SM_GCC_8150 is not set +# CONFIG_SM_GCC_8250 is not set +# CONFIG_SM_GCC_8450 is not set +# CONFIG_SM_GPUCC_6350 is not set +# CONFIG_SM_GPUCC_8150 is not set +# CONFIG_SM_GPUCC_8250 is not set +# CONFIG_SM_GPUCC_8350 is not set +# CONFIG_SM_VIDEOCC_8150 is not set +# CONFIG_SM_VIDEOCC_8250 is not set +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BUS=y +CONFIG_SOFTIRQ_ON_OWN_STACK=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_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_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=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_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +# CONFIG_UCLAMP_TASK is not set +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_SUPPORT=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_ANCHOR=y +# CONFIG_VIRTIO_BLK is not set +# CONFIG_VIRTIO_NET is not set +CONFIG_VMAP_STACK=y +CONFIG_WANT_DEV_COREDUMP=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts new file mode 100644 index 00000000000..9779070725b --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright 2023 Nokia */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "Compex WPQ873"; + compatible = "compex,wpq873", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_power_blue; + led-failsafe = &led_power_red; + led-running = &led_system_green; + led-upgrade = &led_system_blue; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp6; + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + label-mac-device = &dp6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 21 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_power_red: power-red { + label = "red:power"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_power_blue: power-blue { + label = "blue:power"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 28 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_green: system-green { + label = "green:system"; + gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 19 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + i2c_pins: i2c-pins { + pins = "gpio0", "gpio2"; + function = "blsp5_i2c"; + drive-strength = <8>; + bias-disable; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&blsp1_i2c6 { + status = "okay"; + + pinctrl-0 = <&i2c_pins>; + pinctrl-names = "default"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&blsp1_spi1 { + status = "okay"; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + reset_button { + pins = "gpio66"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_spi1 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xa0000>; + read-only; + }; + + partition@550000 { + label = "0:appsbl_1"; + reg = <0x530000 0xa0000>; + read-only; + }; + + partition@610000 { + label = "0:art"; + reg = <0x5d0000 0x40000>; + read-only; + }; + + partition@650000 { + label = "0:ethphyfw"; + reg = <0x610000 0x80000>; + read-only; + }; + }; + }; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs"; + reg = <0x0000000 0x3400000>; + }; + + partition@3400000 { + label = "0:wififw"; + reg = <0x3400000 0x800000>; + read-only; + }; + + partition@3c00000 { + label = "rootfs_1"; + reg = <0x3c00000 0x3400000>; + }; + + partition@7000000 { + label = "0:wififw_1"; + reg = <0x7000000 0x800000>; + read-only; + }; + }; + }; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + + +&mdio { + status = "okay"; + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@28 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <28>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; + +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0x40>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0x0f>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0x0f>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@2 { + port_id = <2>; + phy_address = <1>; + }; + port@3 { + port_id = <3>; + phy_address = <2>; + }; + port@4 { + port_id = <4>; + phy_address = <3>; + }; + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "lan1"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan2"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan3"; +}; + +&dp6 { + status = "okay"; + phy-handle = <&qca8081>; + label = "wan"; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00020000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; +}; + +&pcie_qmp1 { + status = "okay"; +}; + +&pcie1 { + status = "okay"; + + perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; + + bridge@1,0 { + reg = <0x00010000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Compex-WPQ873"; +}; diff --git a/target/linux/ipq807x/image/generic.mk b/target/linux/ipq807x/image/generic.mk index 609c6690d17..4e80e876d9b 100644 --- a/target/linux/ipq807x/image/generic.mk +++ b/target/linux/ipq807x/image/generic.mk @@ -31,6 +31,19 @@ define Device/buffalo_wxr-5950ax12 endef TARGET_DEVICES += buffalo_wxr-5950ax12 +define Device/compex_wpq873 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Compex + DEVICE_MODEL := WPQ873 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@hk09.wpq873 + SOC := ipq8072 + IMAGE/factory.ubi := append-ubi | qsdk-ipq-factory-nand +endef +TARGET_DEVICES += compex_wpq873 + define Device/dynalink_dl-wrx36 $(call Device/FitImage) $(call Device/UbiFit) diff --git a/target/linux/ipq807x/patches-5.15/0106-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch b/target/linux/ipq807x/patches-5.15/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch similarity index 97% rename from target/linux/ipq807x/patches-5.15/0106-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch rename to target/linux/ipq807x/patches-5.15/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch index 0fa38394b99..8719bf74c4d 100644 --- a/target/linux/ipq807x/patches-5.15/0106-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch +++ b/target/linux/ipq807x/patches-5.15/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch @@ -14,7 +14,7 @@ Signed-off-by: Robert Marko --- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -801,6 +801,7 @@ +@@ -766,6 +766,7 @@ linux,pci-domain = <1>; bus-range = <0x00 0xff>; num-lanes = <1>; diff --git a/target/linux/ipq807x/patches-5.15/0107-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch b/target/linux/ipq807x/patches-5.15/0081-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch similarity index 100% rename from target/linux/ipq807x/patches-5.15/0107-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch rename to target/linux/ipq807x/patches-5.15/0081-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch diff --git a/target/linux/ipq807x/patches-5.15/0131-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch b/target/linux/ipq807x/patches-5.15/0082-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch similarity index 100% rename from target/linux/ipq807x/patches-5.15/0131-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch rename to target/linux/ipq807x/patches-5.15/0082-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch diff --git a/target/linux/ipq807x/patches-6.1/0040-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch b/target/linux/ipq807x/patches-6.1/0040-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch new file mode 100644 index 00000000000..dd57eae360b --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0040-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch @@ -0,0 +1,32 @@ +From 6463c10bfdbd684ec7ecfd408ea541283215a088 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:06:28 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add A53 PLL node + +Add the required node for A53 PLL which will be used to provide the CPU +clock via APCS for APSS scaling. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220628.339366-9-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -675,6 +675,14 @@ + #mbox-cells = <1>; + }; + ++ a53pll: clock@b116000 { ++ compatible = "qcom,ipq8074-a53pll"; ++ reg = <0x0b116000 0x40>; ++ #clock-cells = <0>; ++ clocks = <&xo>; ++ clock-names = "xo"; ++ }; ++ + timer@b120000 { + #address-cells = <1>; + #size-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0042-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch b/target/linux/ipq807x/patches-6.1/0042-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch new file mode 100644 index 00000000000..03207259996 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0042-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch @@ -0,0 +1,134 @@ +From e593e834fe8ba9bf314d8215ac05d8787f81efda Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:42 +0200 +Subject: [PATCH] thermal/drivers/tsens: Add support for combined interrupt + +Despite using tsens v2.3 IP, IPQ8074 and IPQ6018 only have one IRQ for +signaling both up/low and critical trips. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-2-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-8960.c | 1 + + drivers/thermal/qcom/tsens-v0_1.c | 1 + + drivers/thermal/qcom/tsens-v1.c | 1 + + drivers/thermal/qcom/tsens-v2.c | 1 + + drivers/thermal/qcom/tsens.c | 38 ++++++++++++++++++++++++++----- + drivers/thermal/qcom/tsens.h | 2 ++ + 6 files changed, 38 insertions(+), 6 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -269,6 +269,7 @@ static const struct tsens_ops ops_8960 = + static struct tsens_features tsens_8960_feat = { + .ver_major = VER_0, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 0, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -549,6 +549,7 @@ static int __init init_8939(struct tsens + static struct tsens_features tsens_v0_1_feat = { + .ver_major = VER_0_1, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -273,6 +273,7 @@ static int calibrate_8976(struct tsens_p + static struct tsens_features tsens_v1_feat = { + .ver_major = VER_1_X, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -31,6 +31,7 @@ + static struct tsens_features tsens_v2_feat = { + .ver_major = VER_2_X, + .crit_int = 1, ++ .combo_int = 0, + .adc = 0, + .srot_split = 1, + .max_sensors = 16, +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -532,6 +532,27 @@ static irqreturn_t tsens_irq_thread(int + return IRQ_HANDLED; + } + ++/** ++ * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts ++ * @irq: irq number ++ * @data: tsens controller private data ++ * ++ * Handle the combined interrupt as if it were 2 separate interrupts, so call the ++ * critical handler first and then the up/low one. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t tsens_combined_irq_thread(int irq, void *data) ++{ ++ irqreturn_t ret; ++ ++ ret = tsens_critical_irq_thread(irq, data); ++ if (ret != IRQ_HANDLED) ++ return ret; ++ ++ return tsens_irq_thread(irq, data); ++} ++ + static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high) + { + struct tsens_sensor *s = tz->devdata; +@@ -1074,13 +1095,18 @@ static int tsens_register(struct tsens_p + tsens_mC_to_hw(priv->sensor, 0)); + } + +- ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); +- if (ret < 0) +- return ret; ++ if (priv->feat->combo_int) { ++ ret = tsens_register_irq(priv, "combined", ++ tsens_combined_irq_thread); ++ } else { ++ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); ++ if (ret < 0) ++ return ret; + +- if (priv->feat->crit_int) +- ret = tsens_register_irq(priv, "critical", +- tsens_critical_irq_thread); ++ if (priv->feat->crit_int) ++ ret = tsens_register_irq(priv, "critical", ++ tsens_critical_irq_thread); ++ } + + return ret; + } +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -493,6 +493,7 @@ enum regfield_ids { + * struct tsens_features - Features supported by the IP + * @ver_major: Major number of IP version + * @crit_int: does the IP support critical interrupts? ++ * @combo_int: does the IP use one IRQ for up, low and critical thresholds? + * @adc: do the sensors only output adc code (instead of temperature)? + * @srot_split: does the IP neatly splits the register space into SROT and TM, + * with SROT only being available to secure boot firmware? +@@ -502,6 +503,7 @@ enum regfield_ids { + struct tsens_features { + unsigned int ver_major; + unsigned int crit_int:1; ++ unsigned int combo_int:1; + unsigned int adc:1; + unsigned int srot_split:1; + unsigned int has_watchdog:1; diff --git a/target/linux/ipq807x/patches-6.1/0043-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch b/target/linux/ipq807x/patches-6.1/0043-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch new file mode 100644 index 00000000000..363061813a8 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0043-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch @@ -0,0 +1,101 @@ +From 7805365fee582056b32c69cf35aafbb94b14a8ca Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:43 +0200 +Subject: [PATCH] thermal/drivers/tsens: Allow configuring min and max trips + +IPQ8074 and IPQ6018 dont support negative trip temperatures and support +up to 204 degrees C as the max trip temperature. + +So, instead of always setting the -40 as min and 120 degrees C as max +allow it to be configured as part of the features. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-3-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-8960.c | 2 ++ + drivers/thermal/qcom/tsens-v0_1.c | 2 ++ + drivers/thermal/qcom/tsens-v1.c | 2 ++ + drivers/thermal/qcom/tsens-v2.c | 2 ++ + drivers/thermal/qcom/tsens.c | 4 ++-- + drivers/thermal/qcom/tsens.h | 4 ++++ + 6 files changed, 14 insertions(+), 2 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -273,6 +273,8 @@ static struct tsens_features tsens_8960_ + .adc = 1, + .srot_split = 0, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + struct tsens_plat_data data_8960 = { +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -553,6 +553,8 @@ static struct tsens_features tsens_v0_1_ + .adc = 1, + .srot_split = 1, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -277,6 +277,8 @@ static struct tsens_features tsens_v1_fe + .adc = 1, + .srot_split = 1, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -35,6 +35,8 @@ static struct tsens_features tsens_v2_fe + .adc = 0, + .srot_split = 1, + .max_sensors = 16, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -573,8 +573,8 @@ static int tsens_set_trips(struct therma + dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", + hw_id, __func__, low, high); + +- cl_high = clamp_val(high, -40000, 120000); +- cl_low = clamp_val(low, -40000, 120000); ++ cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); ++ cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); + + high_val = tsens_mC_to_hw(s, cl_high); + low_val = tsens_mC_to_hw(s, cl_low); +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -499,6 +499,8 @@ enum regfield_ids { + * with SROT only being available to secure boot firmware? + * @has_watchdog: does this IP support watchdog functionality? + * @max_sensors: maximum sensors supported by this version of the IP ++ * @trip_min_temp: minimum trip temperature supported by this version of the IP ++ * @trip_max_temp: maximum trip temperature supported by this version of the IP + */ + struct tsens_features { + unsigned int ver_major; +@@ -508,6 +510,8 @@ struct tsens_features { + unsigned int srot_split:1; + unsigned int has_watchdog:1; + unsigned int max_sensors; ++ int trip_min_temp; ++ int trip_max_temp; + }; + + /** diff --git a/target/linux/ipq807x/patches-6.1/0044-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch b/target/linux/ipq807x/patches-6.1/0044-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch new file mode 100644 index 00000000000..eaea6939591 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0044-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch @@ -0,0 +1,74 @@ +From 0164d794cbc58488a7321272e95958d10cf103a4 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:44 +0200 +Subject: [PATCH] thermal/drivers/tsens: Add IPQ8074 support + +Qualcomm IPQ8074 uses tsens v2.3 IP, however unlike other tsens v2 IP +it only has one IRQ, that is used for up/low as well as critical. +It also does not support negative trip temperatures. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-4-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-v2.c | 17 +++++++++++++++++ + drivers/thermal/qcom/tsens.c | 3 +++ + drivers/thermal/qcom/tsens.h | 2 +- + 3 files changed, 21 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -39,6 +39,17 @@ static struct tsens_features tsens_v2_fe + .trip_max_temp = 120000, + }; + ++static struct tsens_features ipq8074_feat = { ++ .ver_major = VER_2_X, ++ .crit_int = 1, ++ .combo_int = 1, ++ .adc = 0, ++ .srot_split = 1, ++ .max_sensors = 16, ++ .trip_min_temp = 0, ++ .trip_max_temp = 204000, ++}; ++ + static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* VERSION */ +@@ -104,6 +115,12 @@ struct tsens_plat_data data_tsens_v2 = { + .fields = tsens_v2_regfields, + }; + ++struct tsens_plat_data data_ipq8074 = { ++ .ops = &ops_generic_v2, ++ .feat = &ipq8074_feat, ++ .fields = tsens_v2_regfields, ++}; ++ + /* Kept around for backward compatibility with old msm8996.dtsi */ + struct tsens_plat_data data_8996 = { + .num_sensors = 13, +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -981,6 +981,9 @@ static const struct of_device_id tsens_t + .compatible = "qcom,ipq8064-tsens", + .data = &data_8960, + }, { ++ .compatible = "qcom,ipq8074-tsens", ++ .data = &data_ipq8074, ++ }, { + .compatible = "qcom,mdm9607-tsens", + .data = &data_9607, + }, { +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -597,6 +597,6 @@ extern struct tsens_plat_data data_8916, + extern struct tsens_plat_data data_tsens_v1, data_8976, data_8956; + + /* TSENS v2 targets */ +-extern struct tsens_plat_data data_8996, data_tsens_v2; ++extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; + + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq807x/patches-6.1/0045-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch b/target/linux/ipq807x/patches-6.1/0045-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch new file mode 100644 index 00000000000..bad75e4597a --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0045-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch @@ -0,0 +1,130 @@ +From c3cc0c2a17f552be2426200e47a9e2c62cf449ce Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:45 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add thermal nodes + +IPQ8074 has a tsens v2.3.0 peripheral which monitors +temperatures around the various subsystems on the +die. + +So lets add the tsens and thermal zone nodes, passive +CPU cooling will come in later patches after CPU frequency +scaling is supported. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-5-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -274,6 +274,16 @@ + status = "disabled"; + }; + ++ tsens: thermal-sensor@4a9000 { ++ compatible = "qcom,ipq8074-tsens"; ++ reg = <0x4a9000 0x1000>, /* TM */ ++ <0x4a8000 0x1000>; /* SROT */ ++ interrupts = ; ++ interrupt-names = "combined"; ++ #qcom,sensors = <16>; ++ #thermal-sensor-cells = <1>; ++ }; ++ + cryptobam: dma-controller@704000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x00704000 0x20000>; +@@ -874,4 +884,90 @@ + , + ; + }; ++ ++ thermal-zones { ++ nss-top-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 4>; ++ }; ++ ++ nss0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 5>; ++ }; ++ ++ nss1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 6>; ++ }; ++ ++ wcss-phya0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 7>; ++ }; ++ ++ wcss-phya1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 8>; ++ }; ++ ++ cpu0_thermal: cpu0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 9>; ++ }; ++ ++ cpu1_thermal: cpu1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 10>; ++ }; ++ ++ cpu2_thermal: cpu2-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 11>; ++ }; ++ ++ cpu3_thermal: cpu3-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 12>; ++ }; ++ ++ cluster_thermal: cluster-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 13>; ++ }; ++ ++ wcss-phyb0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 14>; ++ }; ++ ++ wcss-phyb1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 15>; ++ }; ++ }; + }; diff --git a/target/linux/ipq807x/patches-6.1/0046-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch b/target/linux/ipq807x/patches-6.1/0046-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch new file mode 100644 index 00000000000..e229851649c --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0046-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch @@ -0,0 +1,29 @@ +From 0df592a0a1a3fff9133977192677aa915afc174f Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:08:49 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add clocks to APCS + +APCS now has support for providing the APSS clocks as the child device +for IPQ8074. + +So, add the A53 PLL and XO clocks in order to use APCS as the CPU +clocksource for APSS scaling. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220849.339732-4-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -680,6 +680,8 @@ + apcs_glb: mailbox@b111000 { + compatible = "qcom,ipq8074-apcs-apps-global"; + reg = <0x0b111000 0x1000>; ++ clocks = <&a53pll>, <&xo>; ++ clock-names = "pll", "xo"; + + #clock-cells = <1>; + #mbox-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0047-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch b/target/linux/ipq807x/patches-6.1/0047-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch new file mode 100644 index 00000000000..9162ea538d3 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0047-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch @@ -0,0 +1,3601 @@ +From e6c5115d6845f25eda7e162dcd783a2044215867 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 30 Oct 2022 18:57:01 +0100 +Subject: [PATCH] clk: qcom: ipq8074: convert to parent data + +Convert the IPQ8074 GCC driver to use parent data instead of global +name matching. + +Utilize ARRAY_SIZE for num_parents instead of hardcoding the value. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221030175703.1103224-1-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 1781 +++++++++++++++----------------- + 1 file changed, 813 insertions(+), 968 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -49,349 +49,6 @@ enum { + P_UNIPHY2_TX, + }; + +-static const char * const gcc_xo_gpll0_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const struct parent_map gcc_xo_gpll0_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll2", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL2, 2 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_sleep_clk[] = { +- "xo", +- "gpll0", +- "sleep_clk", +-}; +- +-static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 2 }, +- { P_SLEEP_CLK, 6 }, +-}; +- +-static const char * const gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { +- "xo", +- "gpll6", +- "gpll0", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL6, 1 }, +- { P_GPLL0, 3 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_out_main_div2_gpll0[] = { +- "xo", +- "gpll0_out_main_div2", +- "gpll0", +-}; +- +-static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { +- { P_XO, 0 }, +- { P_GPLL0_DIV2, 2 }, +- { P_GPLL0, 1 }, +-}; +- +-static const char * const gcc_usb3phy_0_cc_pipe_clk_xo[] = { +- "usb3phy_0_cc_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { +- { P_USB3PHY_0_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_usb3phy_1_cc_pipe_clk_xo[] = { +- "usb3phy_1_cc_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { +- { P_USB3PHY_1_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_pcie20_phy0_pipe_clk_xo[] = { +- "pcie20_phy0_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { +- { P_PCIE20_PHY0_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_pcie20_phy1_pipe_clk_xo[] = { +- "pcie20_phy1_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { +- { P_PCIE20_PHY1_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_div2[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 3 }, +-}; +- +-static const char * const gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { +- "xo", +- "bias_pll_nss_noc_clk", +- "gpll0", +- "gpll2", +-}; +- +-static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { +- { P_XO, 0 }, +- { P_BIAS_PLL_NSS_NOC, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL2, 3 }, +-}; +- +-static const char * const gcc_xo_nss_crypto_pll_gpll0[] = { +- "xo", +- "nss_crypto_pll", +- "gpll0", +-}; +- +-static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { +- { P_XO, 0 }, +- { P_NSS_CRYPTO_PLL, 1 }, +- { P_GPLL0, 2 }, +-}; +- +-static const char * const gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { +- "xo", +- "ubi32_pll", +- "gpll0", +- "gpll2", +- "gpll4", +- "gpll6", +-}; +- +-static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { +- { P_XO, 0 }, +- { P_UBI32_PLL, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL2, 3 }, +- { P_GPLL4, 4 }, +- { P_GPLL6, 5 }, +-}; +- +-static const char * const gcc_xo_gpll0_out_main_div2[] = { +- "xo", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0_DIV2, 1 }, +-}; +- +-static const char * const gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { +- "xo", +- "bias_pll_cc_clk", +- "gpll0", +- "gpll4", +- "nss_crypto_pll", +- "ubi32_pll", +-}; +- +-static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { +- { P_XO, 0 }, +- { P_BIAS_PLL, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL4, 3 }, +- { P_NSS_CRYPTO_PLL, 4 }, +- { P_UBI32_PLL, 5 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll4[] = { +- "xo", +- "gpll0", +- "gpll4", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL4, 2 }, +-}; +- +-static const char * const gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_rx_clk", +- "uniphy0_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_RX, 1 }, +- { P_UNIPHY0_TX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_tx_clk", +- "uniphy0_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_TX, 1 }, +- { P_UNIPHY0_RX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_rx_clk", +- "uniphy0_gcc_tx_clk", +- "uniphy1_gcc_rx_clk", +- "uniphy1_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map +-gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_RX, 1 }, +- { P_UNIPHY0_TX, 2 }, +- { P_UNIPHY1_RX, 3 }, +- { P_UNIPHY1_TX, 4 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_tx_clk", +- "uniphy0_gcc_rx_clk", +- "uniphy1_gcc_tx_clk", +- "uniphy1_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map +-gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_TX, 1 }, +- { P_UNIPHY0_RX, 2 }, +- { P_UNIPHY1_TX, 3 }, +- { P_UNIPHY1_RX, 4 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy2_gcc_rx_clk", +- "uniphy2_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY2_RX, 1 }, +- { P_UNIPHY2_TX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy2_gcc_tx_clk", +- "uniphy2_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY2_TX, 1 }, +- { P_UNIPHY2_RX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +- "sleep_clk", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 4 }, +- { P_SLEEP_CLK, 6 }, +-}; +- + static struct clk_alpha_pll gpll0_main = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], +@@ -400,8 +57,9 @@ static struct clk_alpha_pll gpll0_main = + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -414,9 +72,8 @@ static struct clk_fixed_factor gpll0_out + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "gpll0_out_main_div2", +- .parent_names = (const char *[]){ +- "gpll0_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -429,9 +86,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll0", +- .parent_names = (const char *[]){ +- "gpll0_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +@@ -445,8 +101,9 @@ static struct clk_alpha_pll gpll2_main = + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gpll2_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -461,9 +118,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll2", +- .parent_names = (const char *[]){ +- "gpll2_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll2_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -478,8 +134,9 @@ static struct clk_alpha_pll gpll4_main = + .enable_mask = BIT(5), + .hw.init = &(struct clk_init_data){ + .name = "gpll4_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -494,9 +151,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll4", +- .parent_names = (const char *[]){ +- "gpll4_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll4_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -512,8 +168,9 @@ static struct clk_alpha_pll gpll6_main = + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gpll6_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -528,9 +185,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll6", +- .parent_names = (const char *[]){ +- "gpll6_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll6_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -542,9 +198,8 @@ static struct clk_fixed_factor gpll6_out + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "gpll6_out_main_div2", +- .parent_names = (const char *[]){ +- "gpll6_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll6_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -560,8 +215,9 @@ static struct clk_alpha_pll ubi32_pll_ma + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "ubi32_pll_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, +@@ -575,9 +231,8 @@ static struct clk_alpha_pll_postdiv ubi3 + .width = 2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ubi32_pll", +- .parent_names = (const char *[]){ +- "ubi32_pll_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi32_pll_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -592,8 +247,9 @@ static struct clk_alpha_pll nss_crypto_p + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "nss_crypto_pll_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -607,9 +263,8 @@ static struct clk_alpha_pll_postdiv nss_ + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_crypto_pll", +- .parent_names = (const char *[]){ +- "nss_crypto_pll_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_pll_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -623,6 +278,18 @@ static const struct freq_tbl ftbl_pcnoc_ + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw}, ++ { .hw = &gpll0_out_main_div2.hw}, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 pcnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x27000, + .freq_tbl = ftbl_pcnoc_bfdcd_clk_src, +@@ -630,8 +297,8 @@ static struct clk_rcg2 pcnoc_bfdcd_clk_s + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcnoc_bfdcd_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + .flags = CLK_IS_CRITICAL, + }, +@@ -642,9 +309,8 @@ static struct clk_fixed_factor pcnoc_clk + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "pcnoc_clk_src", +- .parent_names = (const char *[]){ +- "pcnoc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -658,8 +324,9 @@ static struct clk_branch gcc_sleep_clk_s + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sleep_clk_src", +- .parent_names = (const char *[]){ +- "sleep_clk" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "sleep_clk", ++ .name = "sleep_clk", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, +@@ -682,8 +349,8 @@ static struct clk_rcg2 blsp1_qup1_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -708,8 +375,8 @@ static struct clk_rcg2 blsp1_qup1_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -721,8 +388,8 @@ static struct clk_rcg2 blsp1_qup2_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -735,8 +402,8 @@ static struct clk_rcg2 blsp1_qup2_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -748,8 +415,8 @@ static struct clk_rcg2 blsp1_qup3_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -762,8 +429,8 @@ static struct clk_rcg2 blsp1_qup3_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -775,8 +442,8 @@ static struct clk_rcg2 blsp1_qup4_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -789,8 +456,8 @@ static struct clk_rcg2 blsp1_qup4_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -802,8 +469,8 @@ static struct clk_rcg2 blsp1_qup5_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -816,8 +483,8 @@ static struct clk_rcg2 blsp1_qup5_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -829,8 +496,8 @@ static struct clk_rcg2 blsp1_qup6_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -843,8 +510,8 @@ static struct clk_rcg2 blsp1_qup6_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -877,8 +544,8 @@ static struct clk_rcg2 blsp1_uart1_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart1_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -891,8 +558,8 @@ static struct clk_rcg2 blsp1_uart2_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart2_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -905,8 +572,8 @@ static struct clk_rcg2 blsp1_uart3_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart3_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -919,8 +586,8 @@ static struct clk_rcg2 blsp1_uart4_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart4_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -933,8 +600,8 @@ static struct clk_rcg2 blsp1_uart5_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart5_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -947,8 +614,8 @@ static struct clk_rcg2 blsp1_uart6_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart6_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -958,6 +625,11 @@ static const struct clk_parent_data gcc_ + { .hw = &gpll0.clkr.hw }, + }; + ++static const struct parent_map gcc_xo_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++}; ++ + static const struct freq_tbl ftbl_pcie_axi_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(200000000, P_GPLL0, 4, 0, 0), +@@ -972,7 +644,7 @@ static struct clk_rcg2 pcie0_axi_clk_src + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_axi_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -981,6 +653,18 @@ static const struct freq_tbl ftbl_pcie_a + F(19200000, P_XO, 1, 0, 0), + }; + ++static const struct clk_parent_data gcc_xo_gpll0_sleep_clk[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .fw_name = "sleep_clk", .name = "sleep_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 2 }, ++ { P_SLEEP_CLK, 6 }, ++}; ++ + static struct clk_rcg2 pcie0_aux_clk_src = { + .cmd_rcgr = 0x75024, + .freq_tbl = ftbl_pcie_aux_clk_src, +@@ -989,12 +673,22 @@ static struct clk_rcg2 pcie0_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { ++ { .name = "pcie20_phy0_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { ++ { P_PCIE20_PHY0_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux pcie0_pipe_clk_src = { + .reg = 0x7501c, + .shift = 8, +@@ -1003,8 +697,8 @@ static struct clk_regmap_mux pcie0_pipe_ + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "pcie0_pipe_clk_src", +- .parent_names = gcc_pcie20_phy0_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_pcie20_phy0_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_pcie20_phy0_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1019,7 +713,7 @@ static struct clk_rcg2 pcie1_axi_clk_src + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie1_axi_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1032,12 +726,22 @@ static struct clk_rcg2 pcie1_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie1_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { ++ { .name = "pcie20_phy1_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { ++ { P_PCIE20_PHY1_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux pcie1_pipe_clk_src = { + .reg = 0x7601c, + .shift = 8, +@@ -1046,8 +750,8 @@ static struct clk_regmap_mux pcie1_pipe_ + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "pcie1_pipe_clk_src", +- .parent_names = gcc_pcie20_phy1_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_pcie20_phy1_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_pcie20_phy1_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1066,6 +770,20 @@ static const struct freq_tbl ftbl_sdcc_a + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL2, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x42004, + .freq_tbl = ftbl_sdcc_apps_clk_src, +@@ -1074,8 +792,8 @@ static struct clk_rcg2 sdcc1_apps_clk_sr + .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), + .ops = &clk_rcg2_floor_ops, + }, + }; +@@ -1086,6 +804,20 @@ static const struct freq_tbl ftbl_sdcc_i + F(308570000, P_GPLL6, 3.5, 0, 0), + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x5d000, + .freq_tbl = ftbl_sdcc_ice_core_clk_src, +@@ -1094,8 +826,8 @@ static struct clk_rcg2 sdcc1_ice_core_cl + .parent_map = gcc_xo_gpll0_gpll6_gpll0_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_ice_core_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1108,8 +840,8 @@ static struct clk_rcg2 sdcc2_apps_clk_sr + .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc2_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), + .ops = &clk_rcg2_floor_ops, + }, + }; +@@ -1121,6 +853,18 @@ static const struct freq_tbl ftbl_usb_ma + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_out_main_div2_gpll0[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0_out_main_div2.hw }, ++ { .hw = &gpll0.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0_DIV2, 2 }, ++ { P_GPLL0, 1 }, ++}; ++ + static struct clk_rcg2 usb0_master_clk_src = { + .cmd_rcgr = 0x3e00c, + .freq_tbl = ftbl_usb_master_clk_src, +@@ -1129,8 +873,8 @@ static struct clk_rcg2 usb0_master_clk_s + .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_master_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1148,8 +892,8 @@ static struct clk_rcg2 usb0_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1161,6 +905,20 @@ static const struct freq_tbl ftbl_usb_mo + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL6, 1 }, ++ { P_GPLL0, 3 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 usb0_mock_utmi_clk_src = { + .cmd_rcgr = 0x3e020, + .freq_tbl = ftbl_usb_mock_utmi_clk_src, +@@ -1169,12 +927,22 @@ static struct clk_rcg2 usb0_mock_utmi_cl + .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_mock_utmi_clk_src", +- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { ++ { .name = "usb3phy_0_cc_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { ++ { P_USB3PHY_0_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux usb0_pipe_clk_src = { + .reg = 0x3e048, + .shift = 8, +@@ -1183,8 +951,8 @@ static struct clk_regmap_mux usb0_pipe_c + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "usb0_pipe_clk_src", +- .parent_names = gcc_usb3phy_0_cc_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_usb3phy_0_cc_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_usb3phy_0_cc_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1199,8 +967,8 @@ static struct clk_rcg2 usb1_master_clk_s + .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_master_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1213,8 +981,8 @@ static struct clk_rcg2 usb1_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1227,12 +995,22 @@ static struct clk_rcg2 usb1_mock_utmi_cl + .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_mock_utmi_clk_src", +- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { ++ { .name = "usb3phy_1_cc_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { ++ { P_USB3PHY_1_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux usb1_pipe_clk_src = { + .reg = 0x3f048, + .shift = 8, +@@ -1241,8 +1019,8 @@ static struct clk_regmap_mux usb1_pipe_c + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "usb1_pipe_clk_src", +- .parent_names = gcc_usb3phy_1_cc_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_usb3phy_1_cc_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_usb3phy_1_cc_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1256,8 +1034,9 @@ static struct clk_branch gcc_xo_clk_src + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_xo_clk_src", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, +@@ -1271,9 +1050,8 @@ static struct clk_fixed_factor gcc_xo_di + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "gcc_xo_div4_clk_src", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1291,6 +1069,20 @@ static const struct freq_tbl ftbl_system + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 3 }, ++}; ++ + static struct clk_rcg2 system_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x26004, + .freq_tbl = ftbl_system_noc_bfdcd_clk_src, +@@ -1298,8 +1090,8 @@ static struct clk_rcg2 system_noc_bfdcd_ + .parent_map = gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "system_noc_bfdcd_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + .flags = CLK_IS_CRITICAL, + }, +@@ -1310,9 +1102,8 @@ static struct clk_fixed_factor system_no + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "system_noc_clk_src", +- .parent_names = (const char *[]){ +- "system_noc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &system_noc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1333,7 +1124,7 @@ static struct clk_rcg2 nss_ce_clk_src = + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ce_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1344,6 +1135,20 @@ static const struct freq_tbl ftbl_nss_no + { } + }; + ++static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "bias_pll_nss_noc_clk" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { ++ { P_XO, 0 }, ++ { P_BIAS_PLL_NSS_NOC, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL2, 3 }, ++}; ++ + static struct clk_rcg2 nss_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x68088, + .freq_tbl = ftbl_nss_noc_bfdcd_clk_src, +@@ -1351,8 +1156,8 @@ static struct clk_rcg2 nss_noc_bfdcd_clk + .parent_map = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_noc_bfdcd_clk_src", +- .parent_names = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, +- .num_parents = 4, ++ .parent_data = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, ++ .num_parents = ARRAY_SIZE(gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1362,9 +1167,8 @@ static struct clk_fixed_factor nss_noc_c + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "nss_noc_clk_src", +- .parent_names = (const char *[]){ +- "nss_noc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1377,6 +1181,18 @@ static const struct freq_tbl ftbl_nss_cr + { } + }; + ++static const struct clk_parent_data gcc_xo_nss_crypto_pll_gpll0[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &nss_crypto_pll.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_NSS_CRYPTO_PLL, 1 }, ++ { P_GPLL0, 2 }, ++}; ++ + static struct clk_rcg2 nss_crypto_clk_src = { + .cmd_rcgr = 0x68144, + .freq_tbl = ftbl_nss_crypto_clk_src, +@@ -1385,8 +1201,8 @@ static struct clk_rcg2 nss_crypto_clk_sr + .parent_map = gcc_xo_nss_crypto_pll_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_crypto_clk_src", +- .parent_names = gcc_xo_nss_crypto_pll_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_nss_crypto_pll_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_nss_crypto_pll_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1400,6 +1216,24 @@ static const struct freq_tbl ftbl_nss_ub + { } + }; + ++static const struct clk_parent_data gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { ++ { P_XO, 0 }, ++ { P_UBI32_PLL, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL2, 3 }, ++ { P_GPLL4, 4 }, ++ { P_GPLL6, 5 }, ++}; ++ + static struct clk_rcg2 nss_ubi0_clk_src = { + .cmd_rcgr = 0x68104, + .freq_tbl = ftbl_nss_ubi_clk_src, +@@ -1407,8 +1241,8 @@ static struct clk_rcg2 nss_ubi0_clk_src + .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ubi0_clk_src", +- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, +- .num_parents = 6, ++ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, ++ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1421,9 +1255,8 @@ static struct clk_regmap_div nss_ubi0_di + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_ubi0_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_ubi0_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi0_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1438,8 +1271,8 @@ static struct clk_rcg2 nss_ubi1_clk_src + .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ubi1_clk_src", +- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, +- .num_parents = 6, ++ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, ++ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1452,9 +1285,8 @@ static struct clk_regmap_div nss_ubi1_di + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_ubi1_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_ubi1_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi1_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1468,6 +1300,16 @@ static const struct freq_tbl ftbl_ubi_mp + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0_DIV2, 1 }, ++}; ++ + static struct clk_rcg2 ubi_mpt_clk_src = { + .cmd_rcgr = 0x68090, + .freq_tbl = ftbl_ubi_mpt_clk_src, +@@ -1475,8 +1317,8 @@ static struct clk_rcg2 ubi_mpt_clk_src = + .parent_map = gcc_xo_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ubi_mpt_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2, +- .num_parents = 2, ++ .parent_data = gcc_xo_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1487,6 +1329,18 @@ static const struct freq_tbl ftbl_nss_im + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll4[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL4, 2 }, ++}; ++ + static struct clk_rcg2 nss_imem_clk_src = { + .cmd_rcgr = 0x68158, + .freq_tbl = ftbl_nss_imem_clk_src, +@@ -1494,8 +1348,8 @@ static struct clk_rcg2 nss_imem_clk_src + .parent_map = gcc_xo_gpll0_gpll4_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_imem_clk_src", +- .parent_names = gcc_xo_gpll0_gpll4, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll4, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll4), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1506,6 +1360,24 @@ static const struct freq_tbl ftbl_nss_pp + { } + }; + ++static const struct clk_parent_data gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "bias_pll_cc_clk" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++ { .hw = &nss_crypto_pll.clkr.hw }, ++ { .hw = &ubi32_pll.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { ++ { P_XO, 0 }, ++ { P_BIAS_PLL, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL4, 3 }, ++ { P_NSS_CRYPTO_PLL, 4 }, ++ { P_UBI32_PLL, 5 }, ++}; ++ + static struct clk_rcg2 nss_ppe_clk_src = { + .cmd_rcgr = 0x68080, + .freq_tbl = ftbl_nss_ppe_clk_src, +@@ -1513,8 +1385,8 @@ static struct clk_rcg2 nss_ppe_clk_src = + .parent_map = gcc_xo_bias_gpll0_gpll4_nss_ubi32_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ppe_clk_src", +- .parent_names = gcc_xo_bias_gpll0_gpll4_nss_ubi32, +- .num_parents = 6, ++ .parent_data = gcc_xo_bias_gpll0_gpll4_nss_ubi32, ++ .num_parents = ARRAY_SIZE(gcc_xo_bias_gpll0_gpll4_nss_ubi32), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1524,9 +1396,8 @@ static struct clk_fixed_factor nss_ppe_c + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "nss_ppe_cdiv_clk_src", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1540,6 +1411,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_RX, 1 }, ++ { P_UNIPHY0_TX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port1_rx_clk_src = { + .cmd_rcgr = 0x68020, + .freq_tbl = ftbl_nss_port1_rx_clk_src, +@@ -1547,8 +1434,8 @@ static struct clk_rcg2 nss_port1_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port1_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1560,9 +1447,8 @@ static struct clk_regmap_div nss_port1_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port1_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port1_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1577,6 +1463,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_TX, 1 }, ++ { P_UNIPHY0_RX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port1_tx_clk_src = { + .cmd_rcgr = 0x68028, + .freq_tbl = ftbl_nss_port1_tx_clk_src, +@@ -1584,8 +1486,8 @@ static struct clk_rcg2 nss_port1_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port1_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1597,9 +1499,8 @@ static struct clk_regmap_div nss_port1_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port1_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port1_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1614,8 +1515,8 @@ static struct clk_rcg2 nss_port2_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port2_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1627,9 +1528,8 @@ static struct clk_regmap_div nss_port2_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port2_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port2_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1644,8 +1544,8 @@ static struct clk_rcg2 nss_port2_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port2_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1657,9 +1557,8 @@ static struct clk_regmap_div nss_port2_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port2_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port2_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1674,8 +1573,8 @@ static struct clk_rcg2 nss_port3_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port3_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1687,9 +1586,8 @@ static struct clk_regmap_div nss_port3_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port3_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port3_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1704,8 +1602,8 @@ static struct clk_rcg2 nss_port3_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port3_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1717,9 +1615,8 @@ static struct clk_regmap_div nss_port3_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port3_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port3_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1734,8 +1631,8 @@ static struct clk_rcg2 nss_port4_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port4_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1747,9 +1644,8 @@ static struct clk_regmap_div nss_port4_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port4_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port4_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1764,8 +1660,8 @@ static struct clk_rcg2 nss_port4_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port4_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1777,9 +1673,8 @@ static struct clk_regmap_div nss_port4_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port4_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port4_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1799,6 +1694,27 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy1_gcc_rx_clk" }, ++ { .name = "uniphy1_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map ++gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_RX, 1 }, ++ { P_UNIPHY0_TX, 2 }, ++ { P_UNIPHY1_RX, 3 }, ++ { P_UNIPHY1_TX, 4 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port5_rx_clk_src = { + .cmd_rcgr = 0x68060, + .freq_tbl = ftbl_nss_port5_rx_clk_src, +@@ -1806,8 +1722,8 @@ static struct clk_rcg2 nss_port5_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, +- .num_parents = 7, ++ .parent_data = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1819,9 +1735,8 @@ static struct clk_regmap_div nss_port5_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port5_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port5_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1841,6 +1756,27 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy1_gcc_tx_clk" }, ++ { .name = "uniphy1_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map ++gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_TX, 1 }, ++ { P_UNIPHY0_RX, 2 }, ++ { P_UNIPHY1_TX, 3 }, ++ { P_UNIPHY1_RX, 4 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port5_tx_clk_src = { + .cmd_rcgr = 0x68068, + .freq_tbl = ftbl_nss_port5_tx_clk_src, +@@ -1848,8 +1784,8 @@ static struct clk_rcg2 nss_port5_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, +- .num_parents = 7, ++ .parent_data = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1861,9 +1797,8 @@ static struct clk_regmap_div nss_port5_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port5_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port5_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1883,6 +1818,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy2_gcc_rx_clk" }, ++ { .name = "uniphy2_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY2_RX, 1 }, ++ { P_UNIPHY2_TX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port6_rx_clk_src = { + .cmd_rcgr = 0x68070, + .freq_tbl = ftbl_nss_port6_rx_clk_src, +@@ -1890,8 +1841,8 @@ static struct clk_rcg2 nss_port6_rx_clk_ + .parent_map = gcc_xo_uniphy2_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port6_rx_clk_src", +- .parent_names = gcc_xo_uniphy2_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy2_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1903,9 +1854,8 @@ static struct clk_regmap_div nss_port6_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port6_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port6_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1925,6 +1875,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy2_gcc_tx_clk" }, ++ { .name = "uniphy2_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY2_TX, 1 }, ++ { P_UNIPHY2_RX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port6_tx_clk_src = { + .cmd_rcgr = 0x68078, + .freq_tbl = ftbl_nss_port6_tx_clk_src, +@@ -1932,8 +1898,8 @@ static struct clk_rcg2 nss_port6_tx_clk_ + .parent_map = gcc_xo_uniphy2_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port6_tx_clk_src", +- .parent_names = gcc_xo_uniphy2_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy2_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1945,9 +1911,8 @@ static struct clk_regmap_div nss_port6_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port6_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port6_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1970,8 +1935,8 @@ static struct clk_rcg2 crypto_clk_src = + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "crypto_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1981,6 +1946,22 @@ static struct freq_tbl ftbl_gp_clk_src[] + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++ { .fw_name = "sleep_clk", .name = "sleep_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++ { P_SLEEP_CLK, 6 }, ++}; ++ + static struct clk_rcg2 gp1_clk_src = { + .cmd_rcgr = 0x08004, + .freq_tbl = ftbl_gp_clk_src, +@@ -1989,8 +1970,8 @@ static struct clk_rcg2 gp1_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp1_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2003,8 +1984,8 @@ static struct clk_rcg2 gp2_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp2_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2017,8 +1998,8 @@ static struct clk_rcg2 gp3_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp3_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2030,9 +2011,8 @@ static struct clk_branch gcc_blsp1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2047,9 +2027,8 @@ static struct clk_branch gcc_blsp1_qup1_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup1_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup1_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2064,9 +2043,8 @@ static struct clk_branch gcc_blsp1_qup1_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup1_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup1_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2081,9 +2059,8 @@ static struct clk_branch gcc_blsp1_qup2_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup2_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup2_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2098,9 +2075,8 @@ static struct clk_branch gcc_blsp1_qup2_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup2_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup2_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2115,9 +2091,8 @@ static struct clk_branch gcc_blsp1_qup3_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup3_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup3_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2132,9 +2107,8 @@ static struct clk_branch gcc_blsp1_qup3_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup3_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup3_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2149,9 +2123,8 @@ static struct clk_branch gcc_blsp1_qup4_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup4_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup4_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2166,9 +2139,8 @@ static struct clk_branch gcc_blsp1_qup4_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup4_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup4_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2183,9 +2155,8 @@ static struct clk_branch gcc_blsp1_qup5_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup5_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup5_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2200,9 +2171,8 @@ static struct clk_branch gcc_blsp1_qup5_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup5_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup5_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2217,9 +2187,8 @@ static struct clk_branch gcc_blsp1_qup6_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup6_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup6_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2234,9 +2203,8 @@ static struct clk_branch gcc_blsp1_qup6_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup6_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup6_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2251,9 +2219,8 @@ static struct clk_branch gcc_blsp1_uart1 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart1_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart1_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart1_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2268,9 +2235,8 @@ static struct clk_branch gcc_blsp1_uart2 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart2_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart2_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart2_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2285,9 +2251,8 @@ static struct clk_branch gcc_blsp1_uart3 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart3_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart3_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart3_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2302,9 +2267,8 @@ static struct clk_branch gcc_blsp1_uart4 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart4_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart4_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart4_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2319,9 +2283,8 @@ static struct clk_branch gcc_blsp1_uart5 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart5_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart5_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart5_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2336,9 +2299,8 @@ static struct clk_branch gcc_blsp1_uart6 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart6_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart6_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart6_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2354,9 +2316,8 @@ static struct clk_branch gcc_prng_ahb_cl + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2371,9 +2332,8 @@ static struct clk_branch gcc_qpic_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qpic_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2388,9 +2348,8 @@ static struct clk_branch gcc_qpic_clk = + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qpic_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2405,9 +2364,8 @@ static struct clk_branch gcc_pcie0_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2422,9 +2380,8 @@ static struct clk_branch gcc_pcie0_aux_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_aux_clk", +- .parent_names = (const char *[]){ +- "pcie0_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2439,9 +2396,8 @@ static struct clk_branch gcc_pcie0_axi_m + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_m_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2456,9 +2412,8 @@ static struct clk_branch gcc_pcie0_axi_s + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_s_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2474,9 +2429,8 @@ static struct clk_branch gcc_pcie0_pipe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_pipe_clk", +- .parent_names = (const char *[]){ +- "pcie0_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2491,9 +2445,8 @@ static struct clk_branch gcc_sys_noc_pci + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_pcie0_axi_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2508,9 +2461,8 @@ static struct clk_branch gcc_pcie1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2525,9 +2477,8 @@ static struct clk_branch gcc_pcie1_aux_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_aux_clk", +- .parent_names = (const char *[]){ +- "pcie1_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2542,9 +2493,8 @@ static struct clk_branch gcc_pcie1_axi_m + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_axi_m_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2559,9 +2509,8 @@ static struct clk_branch gcc_pcie1_axi_s + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_axi_s_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2577,9 +2526,8 @@ static struct clk_branch gcc_pcie1_pipe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_pipe_clk", +- .parent_names = (const char *[]){ +- "pcie1_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2594,9 +2542,8 @@ static struct clk_branch gcc_sys_noc_pci + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_pcie1_axi_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2611,9 +2558,8 @@ static struct clk_branch gcc_usb0_aux_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_aux_clk", +- .parent_names = (const char *[]){ +- "usb0_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2628,9 +2574,8 @@ static struct clk_branch gcc_sys_noc_usb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb0_axi_clk", +- .parent_names = (const char *[]){ +- "usb0_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2645,9 +2590,8 @@ static struct clk_branch gcc_usb0_master + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_master_clk", +- .parent_names = (const char *[]){ +- "usb0_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2662,9 +2606,8 @@ static struct clk_branch gcc_usb0_mock_u + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_mock_utmi_clk", +- .parent_names = (const char *[]){ +- "usb0_mock_utmi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_mock_utmi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2679,9 +2622,8 @@ static struct clk_branch gcc_usb0_phy_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_phy_cfg_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2697,9 +2639,8 @@ static struct clk_branch gcc_usb0_pipe_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_pipe_clk", +- .parent_names = (const char *[]){ +- "usb0_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2714,9 +2655,8 @@ static struct clk_branch gcc_usb0_sleep_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_sleep_clk", +- .parent_names = (const char *[]){ +- "gcc_sleep_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_sleep_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2731,9 +2671,8 @@ static struct clk_branch gcc_usb1_aux_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_aux_clk", +- .parent_names = (const char *[]){ +- "usb1_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2748,9 +2687,8 @@ static struct clk_branch gcc_sys_noc_usb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb1_axi_clk", +- .parent_names = (const char *[]){ +- "usb1_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2765,9 +2703,8 @@ static struct clk_branch gcc_usb1_master + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_master_clk", +- .parent_names = (const char *[]){ +- "usb1_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2782,9 +2719,8 @@ static struct clk_branch gcc_usb1_mock_u + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_mock_utmi_clk", +- .parent_names = (const char *[]){ +- "usb1_mock_utmi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_mock_utmi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2799,9 +2735,8 @@ static struct clk_branch gcc_usb1_phy_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_phy_cfg_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2817,9 +2752,8 @@ static struct clk_branch gcc_usb1_pipe_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_pipe_clk", +- .parent_names = (const char *[]){ +- "usb1_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2834,9 +2768,8 @@ static struct clk_branch gcc_usb1_sleep_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_sleep_clk", +- .parent_names = (const char *[]){ +- "gcc_sleep_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_sleep_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2851,9 +2784,8 @@ static struct clk_branch gcc_sdcc1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2868,9 +2800,8 @@ static struct clk_branch gcc_sdcc1_apps_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", +- .parent_names = (const char *[]){ +- "sdcc1_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc1_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2885,9 +2816,8 @@ static struct clk_branch gcc_sdcc1_ice_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk", +- .parent_names = (const char *[]){ +- "sdcc1_ice_core_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc1_ice_core_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2902,9 +2832,8 @@ static struct clk_branch gcc_sdcc2_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2919,9 +2848,8 @@ static struct clk_branch gcc_sdcc2_apps_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk", +- .parent_names = (const char *[]){ +- "sdcc2_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc2_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2936,9 +2864,8 @@ static struct clk_branch gcc_mem_noc_nss + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mem_noc_nss_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2953,9 +2880,8 @@ static struct clk_branch gcc_nss_ce_apb_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ce_apb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2970,9 +2896,8 @@ static struct clk_branch gcc_nss_ce_axi_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ce_axi_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2987,9 +2912,8 @@ static struct clk_branch gcc_nss_cfg_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_cfg_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3004,9 +2928,8 @@ static struct clk_branch gcc_nss_crypto_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_crypto_clk", +- .parent_names = (const char *[]){ +- "nss_crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3021,9 +2944,8 @@ static struct clk_branch gcc_nss_csr_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_csr_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3038,9 +2960,8 @@ static struct clk_branch gcc_nss_edma_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_edma_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3055,9 +2976,8 @@ static struct clk_branch gcc_nss_edma_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_edma_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3072,9 +2992,8 @@ static struct clk_branch gcc_nss_imem_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_imem_clk", +- .parent_names = (const char *[]){ +- "nss_imem_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_imem_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3089,9 +3008,8 @@ static struct clk_branch gcc_nss_noc_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_noc_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3106,9 +3024,8 @@ static struct clk_branch gcc_nss_ppe_btq + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_btq_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3123,9 +3040,8 @@ static struct clk_branch gcc_nss_ppe_cfg + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3140,9 +3056,8 @@ static struct clk_branch gcc_nss_ppe_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3157,9 +3072,8 @@ static struct clk_branch gcc_nss_ppe_ipe + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_ipe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3174,9 +3088,8 @@ static struct clk_branch gcc_nss_ptp_ref + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ptp_ref_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_cdiv_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_cdiv_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3192,9 +3105,8 @@ static struct clk_branch gcc_crypto_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3209,9 +3121,8 @@ static struct clk_branch gcc_nssnoc_ce_a + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ce_apb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3226,9 +3137,8 @@ static struct clk_branch gcc_nssnoc_ce_a + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ce_axi_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3243,9 +3153,8 @@ static struct clk_branch gcc_nssnoc_cryp + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_crypto_clk", +- .parent_names = (const char *[]){ +- "nss_crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3260,9 +3169,8 @@ static struct clk_branch gcc_nssnoc_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ppe_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3277,9 +3185,8 @@ static struct clk_branch gcc_nssnoc_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3294,9 +3201,8 @@ static struct clk_branch gcc_nssnoc_qosg + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_qosgen_ref_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3311,9 +3217,8 @@ static struct clk_branch gcc_nssnoc_snoc + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_snoc_clk", +- .parent_names = (const char *[]){ +- "system_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &system_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3328,9 +3233,8 @@ static struct clk_branch gcc_nssnoc_time + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_timeout_ref_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_div4_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_div4_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3345,9 +3249,8 @@ static struct clk_branch gcc_nssnoc_ubi0 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ubi0_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3362,9 +3265,8 @@ static struct clk_branch gcc_nssnoc_ubi1 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ubi1_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3380,9 +3282,8 @@ static struct clk_branch gcc_ubi0_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3398,9 +3299,8 @@ static struct clk_branch gcc_ubi0_axi_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3416,9 +3316,8 @@ static struct clk_branch gcc_ubi0_nc_axi + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_nc_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3434,9 +3333,8 @@ static struct clk_branch gcc_ubi0_core_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_core_clk", +- .parent_names = (const char *[]){ +- "nss_ubi0_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi0_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3452,9 +3350,8 @@ static struct clk_branch gcc_ubi0_mpt_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_mpt_clk", +- .parent_names = (const char *[]){ +- "ubi_mpt_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi_mpt_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3470,9 +3367,8 @@ static struct clk_branch gcc_ubi1_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3488,9 +3384,8 @@ static struct clk_branch gcc_ubi1_axi_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3506,9 +3401,8 @@ static struct clk_branch gcc_ubi1_nc_axi + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_nc_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3524,9 +3418,8 @@ static struct clk_branch gcc_ubi1_core_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_core_clk", +- .parent_names = (const char *[]){ +- "nss_ubi1_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi1_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3542,9 +3435,8 @@ static struct clk_branch gcc_ubi1_mpt_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_mpt_clk", +- .parent_names = (const char *[]){ +- "ubi_mpt_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi_mpt_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3559,9 +3451,8 @@ static struct clk_branch gcc_cmn_12gpll_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cmn_12gpll_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3576,9 +3467,8 @@ static struct clk_branch gcc_cmn_12gpll_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cmn_12gpll_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3593,9 +3483,8 @@ static struct clk_branch gcc_mdio_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdio_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3610,9 +3499,8 @@ static struct clk_branch gcc_uniphy0_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3627,9 +3515,8 @@ static struct clk_branch gcc_uniphy0_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3644,9 +3531,8 @@ static struct clk_branch gcc_uniphy1_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3661,9 +3547,8 @@ static struct clk_branch gcc_uniphy1_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3678,9 +3563,8 @@ static struct clk_branch gcc_uniphy2_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3695,9 +3579,8 @@ static struct clk_branch gcc_uniphy2_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3712,9 +3595,8 @@ static struct clk_branch gcc_nss_port1_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port1_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3729,9 +3611,8 @@ static struct clk_branch gcc_nss_port1_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port1_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3746,9 +3627,8 @@ static struct clk_branch gcc_nss_port2_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port2_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3763,9 +3643,8 @@ static struct clk_branch gcc_nss_port2_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port2_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3780,9 +3659,8 @@ static struct clk_branch gcc_nss_port3_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port3_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3797,9 +3675,8 @@ static struct clk_branch gcc_nss_port3_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port3_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3814,9 +3691,8 @@ static struct clk_branch gcc_nss_port4_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port4_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3831,9 +3707,8 @@ static struct clk_branch gcc_nss_port4_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port4_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3848,9 +3723,8 @@ static struct clk_branch gcc_nss_port5_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3865,9 +3739,8 @@ static struct clk_branch gcc_nss_port5_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3882,9 +3755,8 @@ static struct clk_branch gcc_nss_port6_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port6_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3899,9 +3771,8 @@ static struct clk_branch gcc_nss_port6_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port6_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3916,9 +3787,8 @@ static struct clk_branch gcc_port1_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port1_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3933,9 +3803,8 @@ static struct clk_branch gcc_port2_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port2_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3950,9 +3819,8 @@ static struct clk_branch gcc_port3_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port3_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3967,9 +3835,8 @@ static struct clk_branch gcc_port4_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port4_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3984,9 +3851,8 @@ static struct clk_branch gcc_port5_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port5_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4001,9 +3867,8 @@ static struct clk_branch gcc_port6_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port6_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4018,9 +3883,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port1_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4035,9 +3899,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port1_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4052,9 +3915,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port2_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4069,9 +3931,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port2_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4086,9 +3947,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port3_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4103,9 +3963,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port3_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4120,9 +3979,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port4_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4137,9 +3995,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port4_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4154,9 +4011,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4171,9 +4027,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4188,9 +4043,8 @@ static struct clk_branch gcc_uniphy1_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4205,9 +4059,8 @@ static struct clk_branch gcc_uniphy1_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4222,9 +4075,8 @@ static struct clk_branch gcc_uniphy2_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_port6_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4239,9 +4091,8 @@ static struct clk_branch gcc_uniphy2_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_port6_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4257,9 +4108,8 @@ static struct clk_branch gcc_crypto_ahb_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4275,9 +4125,8 @@ static struct clk_branch gcc_crypto_axi_ + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_axi_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4293,9 +4142,8 @@ static struct clk_branch gcc_crypto_clk + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_clk", +- .parent_names = (const char *[]){ +- "crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4310,9 +4158,8 @@ static struct clk_branch gcc_gp1_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk", +- .parent_names = (const char *[]){ +- "gp1_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp1_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4327,9 +4174,8 @@ static struct clk_branch gcc_gp2_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk", +- .parent_names = (const char *[]){ +- "gp2_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp2_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4344,9 +4190,8 @@ static struct clk_branch gcc_gp3_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk", +- .parent_names = (const char *[]){ +- "gp3_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp3_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4368,7 +4213,7 @@ static struct clk_rcg2 pcie0_rchng_clk_s + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_rchng_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; diff --git a/target/linux/ipq807x/patches-6.1/0049-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch b/target/linux/ipq807x/patches-6.1/0049-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch new file mode 100644 index 00000000000..e0725a66129 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0049-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch @@ -0,0 +1,59 @@ +From 813ba3e427671ba3ff35c825087b03f0ad91cf02 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:28:59 +0100 +Subject: [PATCH] clk: qcom: reset: support resetting multiple bits + +This patch adds the support for giving the complete bitmask +in reset structure and reset operation will use this bitmask +for all reset operations. + +Currently, reset structure only takes a single bit for each reset +and then calculates the bitmask by using the BIT() macro. + +However, this is not sufficient anymore for newer SoC-s like IPQ8074, +IPQ6018 and more, since their networking resets require multiple bits +to be asserted in order to properly reset the HW block completely. + +So, in order to allow asserting multiple bits add "bitmask" field to +qcom_reset_map, and then use that bitmask value if its populated in the +driver, if its not populated, then we just default to existing behaviour +and calculate the bitmask on the fly. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-1-robimarko@gmail.com +--- + drivers/clk/qcom/reset.c | 4 ++-- + drivers/clk/qcom/reset.h | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/reset.c ++++ b/drivers/clk/qcom/reset.c +@@ -30,7 +30,7 @@ qcom_reset_assert(struct reset_controlle + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, mask); + } +@@ -44,7 +44,7 @@ qcom_reset_deassert(struct reset_control + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, 0); + } +--- a/drivers/clk/qcom/reset.h ++++ b/drivers/clk/qcom/reset.h +@@ -12,6 +12,7 @@ struct qcom_reset_map { + unsigned int reg; + u8 bit; + u8 udelay; ++ u32 bitmask; + }; + + struct regmap; diff --git a/target/linux/ipq807x/patches-6.1/0050-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch b/target/linux/ipq807x/patches-6.1/0050-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch new file mode 100644 index 00000000000..75f16a16737 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0050-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch @@ -0,0 +1,39 @@ +From e78a40eb24187a8b4f9b89e2181f674df39c2013 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:29:00 +0100 +Subject: [PATCH] dt-bindings: clock: qcom: ipq8074: add missing networking + resets + +Add bindings for the missing networking resets found in IPQ8074 GCC. + +Signed-off-by: Robert Marko +Acked-by: Krzysztof Kozlowski +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-2-robimarko@gmail.com +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -367,6 +367,20 @@ + #define GCC_PCIE1_AHB_ARES 129 + #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 + #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 ++#define GCC_PPE_FULL_RESET 132 ++#define GCC_UNIPHY0_SOFT_RESET 133 ++#define GCC_UNIPHY0_XPCS_RESET 134 ++#define GCC_UNIPHY1_SOFT_RESET 135 ++#define GCC_UNIPHY1_XPCS_RESET 136 ++#define GCC_UNIPHY2_SOFT_RESET 137 ++#define GCC_UNIPHY2_XPCS_RESET 138 ++#define GCC_EDMA_HW_RESET 139 ++#define GCC_NSSPORT1_RESET 140 ++#define GCC_NSSPORT2_RESET 141 ++#define GCC_NSSPORT3_RESET 142 ++#define GCC_NSSPORT4_RESET 143 ++#define GCC_NSSPORT5_RESET 144 ++#define GCC_NSSPORT6_RESET 145 + + #define USB0_GDSC 0 + #define USB1_GDSC 1 diff --git a/target/linux/ipq807x/patches-6.1/0051-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch b/target/linux/ipq807x/patches-6.1/0051-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch new file mode 100644 index 00000000000..212fc84869c --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0051-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch @@ -0,0 +1,41 @@ +From da76cb63d04dc22ed32123b8c1d084c006d67bfb Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:29:01 +0100 +Subject: [PATCH] clk: qcom: ipq8074: add missing networking resets + +Downstream QCA 5.4 kernel defines networking resets which are not present +in the mainline kernel but are required for the networking drivers. + +So, port the downstream resets and avoid using magic values for mask, +construct mask for resets which require multiple bits to be set/cleared. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-3-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4671,6 +4671,20 @@ static const struct qcom_reset_map gcc_i + [GCC_PCIE1_AXI_SLAVE_ARES] = { 0x76040, 4 }, + [GCC_PCIE1_AHB_ARES] = { 0x76040, 5 }, + [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, ++ [GCC_PPE_FULL_RESET] = { .reg = 0x68014, .bitmask = GENMASK(19, 16) }, ++ [GCC_UNIPHY0_SOFT_RESET] = { .reg = 0x56004, .bitmask = GENMASK(13, 4) | BIT(1) }, ++ [GCC_UNIPHY0_XPCS_RESET] = { 0x56004, 2 }, ++ [GCC_UNIPHY1_SOFT_RESET] = { .reg = 0x56104, .bitmask = GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY1_XPCS_RESET] = { 0x56104, 2 }, ++ [GCC_UNIPHY2_SOFT_RESET] = { .reg = 0x56204, .bitmask = GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY2_XPCS_RESET] = { 0x56204, 2 }, ++ [GCC_EDMA_HW_RESET] = { .reg = 0x68014, .bitmask = GENMASK(21, 20) }, ++ [GCC_NSSPORT1_RESET] = { .reg = 0x68014, .bitmask = BIT(24) | GENMASK(1, 0) }, ++ [GCC_NSSPORT2_RESET] = { .reg = 0x68014, .bitmask = BIT(25) | GENMASK(3, 2) }, ++ [GCC_NSSPORT3_RESET] = { .reg = 0x68014, .bitmask = BIT(26) | GENMASK(5, 4) }, ++ [GCC_NSSPORT4_RESET] = { .reg = 0x68014, .bitmask = BIT(27) | GENMASK(9, 8) }, ++ [GCC_NSSPORT5_RESET] = { .reg = 0x68014, .bitmask = BIT(28) | GENMASK(11, 10) }, ++ [GCC_NSSPORT6_RESET] = { .reg = 0x68014, .bitmask = BIT(29) | GENMASK(13, 12) }, + }; + + static struct gdsc *gcc_ipq8074_gdscs[] = { diff --git a/target/linux/ipq807x/patches-6.1/0052-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch b/target/linux/ipq807x/patches-6.1/0052-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch new file mode 100644 index 00000000000..7372b1da8ed --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0052-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch @@ -0,0 +1,152 @@ +From 78936d46470938caa9a7ea529deeb36777b4f98e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:46:55 +0100 +Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for all parents + +It appears that having only .name populated in parent_data for clocks +which are only globally searchable currently will not work as the clk core +won't copy that name if there is no .fw_name present as well. + +So, populate .fw_name for all parent clocks in parent_data. + +Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") + +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221116214655.1116467-1-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 52 +++++++++++++++++----------------- + 1 file changed, 26 insertions(+), 26 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -680,7 +680,7 @@ static struct clk_rcg2 pcie0_aux_clk_src + }; + + static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { +- { .name = "pcie20_phy0_pipe_clk" }, ++ { .fw_name = "pcie0_pipe", .name = "pcie20_phy0_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -733,7 +733,7 @@ static struct clk_rcg2 pcie1_aux_clk_src + }; + + static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { +- { .name = "pcie20_phy1_pipe_clk" }, ++ { .fw_name = "pcie1_pipe", .name = "pcie20_phy1_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -1137,7 +1137,7 @@ static const struct freq_tbl ftbl_nss_no + + static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "bias_pll_nss_noc_clk" }, ++ { .fw_name = "bias_pll_nss_noc_clk", .name = "bias_pll_nss_noc_clk" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, + }; +@@ -1362,7 +1362,7 @@ static const struct freq_tbl ftbl_nss_pp + + static const struct clk_parent_data gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &nss_crypto_pll.clkr.hw }, +@@ -1413,10 +1413,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .name = "uniphy0_gcc_tx_clk" }, ++ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, ++ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { +@@ -1465,10 +1465,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .name = "uniphy0_gcc_rx_clk" }, ++ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, ++ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { +@@ -1696,12 +1696,12 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .name = "uniphy1_gcc_rx_clk" }, +- { .name = "uniphy1_gcc_tx_clk" }, ++ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, ++ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, ++ { .fw_name = "uniphy1_gcc_rx_clk", .name = "uniphy1_gcc_rx_clk" }, ++ { .fw_name = "uniphy1_gcc_tx_clk", .name = "uniphy1_gcc_tx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map +@@ -1758,12 +1758,12 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .name = "uniphy1_gcc_tx_clk" }, +- { .name = "uniphy1_gcc_rx_clk" }, ++ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, ++ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, ++ { .fw_name = "uniphy1_gcc_tx_clk", .name = "uniphy1_gcc_tx_clk" }, ++ { .fw_name = "uniphy1_gcc_rx_clk", .name = "uniphy1_gcc_rx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map +@@ -1820,10 +1820,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy2_gcc_rx_clk" }, +- { .name = "uniphy2_gcc_tx_clk" }, ++ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, ++ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { +@@ -1877,10 +1877,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy2_gcc_tx_clk" }, +- { .name = "uniphy2_gcc_rx_clk" }, ++ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, ++ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { diff --git a/target/linux/ipq807x/patches-6.1/0053-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch b/target/linux/ipq807x/patches-6.1/0053-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch new file mode 100644 index 00000000000..1f99de002bb --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0053-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch @@ -0,0 +1,36 @@ +From 9033c3c86ea0dd35bd2ab957317573b755967298 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 30 Oct 2022 18:57:03 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: pass XO and sleep clocks to GCC + +Pass XO and sleep clocks to the GCC controller so it does not have to +find them by matching globaly by name. + +If not passed directly, driver maintains backwards compatibility by then +falling back to global lookup. + +Since we are here, set cell numbers in decimal instead of hex. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221030175703.1103224-3-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -361,9 +361,11 @@ + gcc: gcc@1800000 { + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; +- #clock-cells = <0x1>; ++ clocks = <&xo>, <&sleep_clk>; ++ clock-names = "xo", "sleep_clk"; ++ #clock-cells = <1>; + #power-domain-cells = <1>; +- #reset-cells = <0x1>; ++ #reset-cells = <1>; + }; + + tcsr_mutex: hwlock@1905000 { diff --git a/target/linux/ipq807x/patches-6.1/0066-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch b/target/linux/ipq807x/patches-6.1/0066-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch new file mode 100644 index 00000000000..cd146420cf4 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0066-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch @@ -0,0 +1,149 @@ +From fb76b808f8628215afebaf0f8af0bde635302590 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:18:14 +0200 +Subject: [PATCH] arm64: dts: qcom: add PMP8074 DTSI + +PMP8074 is a companion PMIC to the Qualcomm IPQ8074 series that is +controlled via SPMI. + +Add DTSI for it providing GPIO, regulator, RTC and VADC support. + +RTC is disabled by default as there is no built-in battery so it will +loose time unless board vendor added a battery, so make it optional. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818221815.346233-4-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 125 ++++++++++++++++++++++++++ + 1 file changed, 125 insertions(+) + create mode 100644 arch/arm64/boot/dts/qcom/pmp8074.dtsi + +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -0,0 +1,125 @@ ++// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause ++ ++#include ++#include ++ ++&spmi_bus { ++ pmic@0 { ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; ++ reg = <0x0 SPMI_USID>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pmp8074_adc: adc@3100 { ++ compatible = "qcom,spmi-adc-rev2"; ++ reg = <0x3100>; ++ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #io-channel-cells = <1>; ++ ++ ref-gnd@0 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref-1p25@1 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref-vadc@2 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pmic_die: die-temp@6 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ xo_therm: xo-temp@76 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm1: thermistor1@77 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm2: thermistor2@78 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm3: thermistor3@79 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vph-pwr@131 { ++ reg = ; ++ qcom,pre-scaling = <1 3>; ++ }; ++ }; ++ ++ pmp8074_rtc: rtc@6000 { ++ compatible = "qcom,pm8941-rtc"; ++ reg = <0x6000>; ++ reg-names = "rtc", "alarm"; ++ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; ++ allow-set-time; ++ status = "disabled"; ++ }; ++ ++ pmp8074_gpios: gpio@c000 { ++ compatible = "qcom,pmp8074-gpio", "qcom,spmi-gpio"; ++ reg = <0xc000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmp8074_gpios 0 0 12>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; ++ ++ pmic@1 { ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; ++ reg = <0x1 SPMI_USID>; ++ ++ regulators { ++ compatible = "qcom,pmp8074-regulators"; ++ ++ s3: s3 { ++ regulator-name = "vdd_s3"; ++ regulator-min-microvolt = <592000>; ++ regulator-max-microvolt = <1064000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ s4: s4 { ++ regulator-name = "vdd_s4"; ++ regulator-min-microvolt = <712000>; ++ regulator-max-microvolt = <992000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ l11: l11 { ++ regulator-name = "l11"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++ }; ++}; diff --git a/target/linux/ipq807x/patches-6.1/0067-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch b/target/linux/ipq807x/patches-6.1/0067-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch new file mode 100644 index 00000000000..ebd3763a58f --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0067-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch @@ -0,0 +1,37 @@ +From 2c394cfc1779886048feca7dc7f4075da5f6328c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:18:15 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074-hk01: add VQMMC supply + +Since now we have control over the PMP8074 PMIC providing various system +voltages including L11 which provides the SDIO/eMMC I/O voltage set it as +the SDHCI VQMMC supply. + +This allows SDHCI controller to switch to 1.8V I/O mode and support high +speed modes like HS200 and HS400. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818221815.346233-5-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -3,6 +3,7 @@ + /* Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + #include "ipq8074.dtsi" ++#include "pmp8074.dtsi" + + / { + model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; +@@ -84,6 +85,7 @@ + + &sdhc_1 { + status = "okay"; ++ vqmmc-supply = <&l11>; + }; + + &qusb_phy_0 { diff --git a/target/linux/ipq807x/patches-6.1/0068-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch b/target/linux/ipq807x/patches-6.1/0068-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch new file mode 100644 index 00000000000..e08f6d1f3c6 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0068-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch @@ -0,0 +1,42 @@ +From 82ceb86227b1fc15c76d5fc691b2bf425f1a63b3 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 10:29:30 +0100 +Subject: [PATCH] arm64: dts: qcom: hk01: use GPIO flags for tlmm + +Use respective GPIO_ACTIVE_LOW/HIGH flags for tlmm GPIOs instead of +harcoding the cell value. + +Signed-off-by: Robert Marko +Reviewed-by: Krzysztof Kozlowski +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107092930.33325-3-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -4,6 +4,7 @@ + */ + #include "ipq8074.dtsi" + #include "pmp8074.dtsi" ++#include + + / { + model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; +@@ -52,12 +53,12 @@ + + &pcie0 { + status = "okay"; +- perst-gpios = <&tlmm 61 0x1>; ++ perst-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>; + }; + + &pcie1 { + status = "okay"; +- perst-gpios = <&tlmm 58 0x1>; ++ perst-gpios = <&tlmm 58 GPIO_ACTIVE_LOW>; + }; + + &pcie_qmp0 { diff --git a/target/linux/ipq807x/patches-6.1/0069-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch b/target/linux/ipq807x/patches-6.1/0069-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch new file mode 100644 index 00000000000..a8bf2492f4a --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0069-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch @@ -0,0 +1,82 @@ +From 1b1c1423ca3e740984aa883512a72c4ea08fbe28 Mon Sep 17 00:00:00 2001 +From: Konrad Dybcio +Date: Mon, 7 Nov 2022 15:55:17 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074-*: Fix up comments + +Make sure all multiline C-style commends begin with just '/*' with +the comment text starting on a new line. + +Also, fix up some whitespace within comments. + +Signed-off-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107145522.6706-8-konrad.dybcio@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 12 ++++++------ + 4 files changed, 12 insertions(+), 9 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /dts-v1/; +-/* Copyright (c) 2017, The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + #include "ipq8074.dtsi" + #include "pmp8074.dtsi" +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only +-/* Copyright (c) 2020 The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + /dts-v1/; + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /dts-v1/; +-/* Copyright (c) 2020 The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + #include "ipq8074-hk10.dtsi" + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -129,10 +129,10 @@ + status = "disabled"; + + usb1_ssphy: phy@58200 { +- reg = <0x00058200 0x130>, /* Tx */ ++ reg = <0x00058200 0x130>, /* Tx */ + <0x00058400 0x200>, /* Rx */ +- <0x00058800 0x1f8>, /* PCS */ +- <0x00058600 0x044>; /* PCS misc*/ ++ <0x00058800 0x1f8>, /* PCS */ ++ <0x00058600 0x044>; /* PCS misc */ + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_USB1_PIPE_CLK>; +@@ -172,10 +172,10 @@ + status = "disabled"; + + usb0_ssphy: phy@78200 { +- reg = <0x00078200 0x130>, /* Tx */ ++ reg = <0x00078200 0x130>, /* Tx */ + <0x00078400 0x200>, /* Rx */ +- <0x00078800 0x1f8>, /* PCS */ +- <0x00078600 0x044>; /* PCS misc*/ ++ <0x00078800 0x1f8>, /* PCS */ ++ <0x00078600 0x044>; /* PCS misc */ + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_USB0_PIPE_CLK>; diff --git a/target/linux/ipq807x/patches-6.1/0070-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch b/target/linux/ipq807x/patches-6.1/0070-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch new file mode 100644 index 00000000000..1ce11406828 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0070-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch @@ -0,0 +1,60 @@ +From 5f20690f77878b1ba24ec88df01b92d5131a6780 Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Tue, 8 Nov 2022 15:23:57 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: align TLMM pin configuration with + DT schema + +DT schema expects TLMM pin configuration nodes to be named with +'-state' suffix and their optional children with '-pins' suffix. + +Signed-off-by: Krzysztof Kozlowski +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221108142357.67202-2-krzysztof.kozlowski@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -318,35 +318,35 @@ + interrupt-controller; + #interrupt-cells = <0x2>; + +- serial_4_pins: serial4-pinmux { ++ serial_4_pins: serial4-state { + pins = "gpio23", "gpio24"; + function = "blsp4_uart1"; + drive-strength = <8>; + bias-disable; + }; + +- i2c_0_pins: i2c-0-pinmux { ++ i2c_0_pins: i2c-0-state { + pins = "gpio42", "gpio43"; + function = "blsp1_i2c"; + drive-strength = <8>; + bias-disable; + }; + +- spi_0_pins: spi-0-pins { ++ spi_0_pins: spi-0-state { + pins = "gpio38", "gpio39", "gpio40", "gpio41"; + function = "blsp0_spi"; + drive-strength = <8>; + bias-disable; + }; + +- hsuart_pins: hsuart-pins { ++ hsuart_pins: hsuart-state { + pins = "gpio46", "gpio47", "gpio48", "gpio49"; + function = "blsp2_uart"; + drive-strength = <8>; + bias-disable; + }; + +- qpic_pins: qpic-pins { ++ qpic_pins: qpic-state { + pins = "gpio1", "gpio3", "gpio4", + "gpio5", "gpio6", "gpio7", + "gpio8", "gpio10", "gpio11", diff --git a/target/linux/ipq807x/patches-6.1/0079-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch b/target/linux/ipq807x/patches-6.1/0079-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch new file mode 100644 index 00000000000..3319f431bac --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0079-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch @@ -0,0 +1,207 @@ +From 77faa07c185c969e742cbb3e6aa487a11b0b616c Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Tue, 30 Aug 2022 09:57:42 +0300 +Subject: [PATCH] dt-bindings: arm: qcom: document qcom,msm-id and + qcom,board-id + +The top level qcom,msm-id and qcom,board-id properties are utilized by +bootloaders on Qualcomm MSM platforms to determine which device tree +should be used and passed to the kernel. + +The commit b32e592d3c28 ("devicetree: bindings: Document qcom board +compatible format") from 2015 was a consensus during discussion about +upstreaming qcom,msm-id and qcom,board-id fields. There are however still +problems with that consensus: +1. It was reached 7 years ago but it turned out its implementation did + not reach all possible products. + +2. Initially additional tool (dtbTool) was needed for parsing these + fields to create a QCDT image consisting of multiple DTBs, later the + bootloaders were improved and they use these qcom,msm-id and + qcom,board-id properties directly. + +3. Extracting relevant information from the board compatible requires + this additional tool (dtbTool), which makes the build process more + complicated and not easily reproducible (DTBs are modified after the + kernel build). + +4. Some versions of Qualcomm bootloaders expect these properties even + when booting with a single DTB. The community is stuck with these + bootloaders thus they require properties in the DTBs. + +Since several upstreamed Qualcomm SoC-based boards require these +properties to properly boot and the properties are reportedly used by +bootloaders, document them along with the bindings header with constants +used by: bootloader, some DTS and socinfo driver. + +Link: https://lore.kernel.org/r/a3c932d1-a102-ce18-deea-18cbbd05ecab@linaro.org/ +Co-developed-by: Kumar Gala +Signed-off-by: Kumar Gala +Signed-off-by: Krzysztof Kozlowski +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Rob Herring +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220830065744.161163-2-krzysztof.kozlowski@linaro.org +--- + include/dt-bindings/arm/qcom,ids.h | 155 +++++++++++++++++++++++++++++ + 1 file changed, 155 insertions(+) + create mode 100644 include/dt-bindings/arm/qcom,ids.h + +--- /dev/null ++++ b/include/dt-bindings/arm/qcom,ids.h +@@ -0,0 +1,155 @@ ++/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022 Linaro Ltd ++ * Author: Krzysztof Kozlowski based on previous work of Kumar Gala. ++ */ ++#ifndef _DT_BINDINGS_ARM_QCOM_IDS_H ++#define _DT_BINDINGS_ARM_QCOM_IDS_H ++ ++/* ++ * The MSM chipset and hardware revision used by Qualcomm bootloaders, DTS for ++ * older chipsets (qcom,msm-id) and in socinfo driver: ++ */ ++#define QCOM_ID_MSM8960 87 ++#define QCOM_ID_APQ8064 109 ++#define QCOM_ID_MSM8660A 122 ++#define QCOM_ID_MSM8260A 123 ++#define QCOM_ID_APQ8060A 124 ++#define QCOM_ID_MSM8974 126 ++#define QCOM_ID_MPQ8064 130 ++#define QCOM_ID_MSM8960AB 138 ++#define QCOM_ID_APQ8060AB 139 ++#define QCOM_ID_MSM8260AB 140 ++#define QCOM_ID_MSM8660AB 141 ++#define QCOM_ID_MSM8626 145 ++#define QCOM_ID_MSM8610 147 ++#define QCOM_ID_APQ8064AB 153 ++#define QCOM_ID_MSM8226 158 ++#define QCOM_ID_MSM8526 159 ++#define QCOM_ID_MSM8110 161 ++#define QCOM_ID_MSM8210 162 ++#define QCOM_ID_MSM8810 163 ++#define QCOM_ID_MSM8212 164 ++#define QCOM_ID_MSM8612 165 ++#define QCOM_ID_MSM8112 166 ++#define QCOM_ID_MSM8225Q 168 ++#define QCOM_ID_MSM8625Q 169 ++#define QCOM_ID_MSM8125Q 170 ++#define QCOM_ID_APQ8064AA 172 ++#define QCOM_ID_APQ8084 178 ++#define QCOM_ID_APQ8074 184 ++#define QCOM_ID_MSM8274 185 ++#define QCOM_ID_MSM8674 186 ++#define QCOM_ID_MSM8974PRO_AC 194 ++#define QCOM_ID_MSM8126 198 ++#define QCOM_ID_APQ8026 199 ++#define QCOM_ID_MSM8926 200 ++#define QCOM_ID_MSM8326 205 ++#define QCOM_ID_MSM8916 206 ++#define QCOM_ID_MSM8994 207 ++#define QCOM_ID_APQ8074PRO_AA 208 ++#define QCOM_ID_APQ8074PRO_AB 209 ++#define QCOM_ID_APQ8074PRO_AC 210 ++#define QCOM_ID_MSM8274PRO_AA 211 ++#define QCOM_ID_MSM8274PRO_AB 212 ++#define QCOM_ID_MSM8274PRO_AC 213 ++#define QCOM_ID_MSM8674PRO_AA 214 ++#define QCOM_ID_MSM8674PRO_AB 215 ++#define QCOM_ID_MSM8674PRO_AC 216 ++#define QCOM_ID_MSM8974PRO_AA 217 ++#define QCOM_ID_MSM8974PRO_AB 218 ++#define QCOM_ID_APQ8028 219 ++#define QCOM_ID_MSM8128 220 ++#define QCOM_ID_MSM8228 221 ++#define QCOM_ID_MSM8528 222 ++#define QCOM_ID_MSM8628 223 ++#define QCOM_ID_MSM8928 224 ++#define QCOM_ID_MSM8510 225 ++#define QCOM_ID_MSM8512 226 ++#define QCOM_ID_MSM8936 233 ++#define QCOM_ID_MSM8939 239 ++#define QCOM_ID_APQ8036 240 ++#define QCOM_ID_APQ8039 241 ++#define QCOM_ID_MSM8996 246 ++#define QCOM_ID_APQ8016 247 ++#define QCOM_ID_MSM8216 248 ++#define QCOM_ID_MSM8116 249 ++#define QCOM_ID_MSM8616 250 ++#define QCOM_ID_MSM8992 251 ++#define QCOM_ID_APQ8094 253 ++#define QCOM_ID_MDM9607 290 ++#define QCOM_ID_APQ8096 291 ++#define QCOM_ID_MSM8998 292 ++#define QCOM_ID_MSM8953 293 ++#define QCOM_ID_MDM8207 296 ++#define QCOM_ID_MDM9207 297 ++#define QCOM_ID_MDM9307 298 ++#define QCOM_ID_MDM9628 299 ++#define QCOM_ID_APQ8053 304 ++#define QCOM_ID_MSM8996SG 305 ++#define QCOM_ID_MSM8996AU 310 ++#define QCOM_ID_APQ8096AU 311 ++#define QCOM_ID_APQ8096SG 312 ++#define QCOM_ID_SDM660 317 ++#define QCOM_ID_SDM630 318 ++#define QCOM_ID_APQ8098 319 ++#define QCOM_ID_SDM845 321 ++#define QCOM_ID_MDM9206 322 ++#define QCOM_ID_IPQ8074 323 ++#define QCOM_ID_SDA660 324 ++#define QCOM_ID_SDM658 325 ++#define QCOM_ID_SDA658 326 ++#define QCOM_ID_SDA630 327 ++#define QCOM_ID_SDM450 338 ++#define QCOM_ID_SDA845 341 ++#define QCOM_ID_IPQ8072 342 ++#define QCOM_ID_IPQ8076 343 ++#define QCOM_ID_IPQ8078 344 ++#define QCOM_ID_SDM636 345 ++#define QCOM_ID_SDA636 346 ++#define QCOM_ID_SDM632 349 ++#define QCOM_ID_SDA632 350 ++#define QCOM_ID_SDA450 351 ++#define QCOM_ID_SM8250 356 ++#define QCOM_ID_IPQ8070 375 ++#define QCOM_ID_IPQ8071 376 ++#define QCOM_ID_IPQ8072A 389 ++#define QCOM_ID_IPQ8074A 390 ++#define QCOM_ID_IPQ8076A 391 ++#define QCOM_ID_IPQ8078A 392 ++#define QCOM_ID_SM6125 394 ++#define QCOM_ID_IPQ8070A 395 ++#define QCOM_ID_IPQ8071A 396 ++#define QCOM_ID_IPQ6018 402 ++#define QCOM_ID_IPQ6028 403 ++#define QCOM_ID_IPQ6000 421 ++#define QCOM_ID_IPQ6010 422 ++#define QCOM_ID_SC7180 425 ++#define QCOM_ID_SM6350 434 ++#define QCOM_ID_SM8350 439 ++#define QCOM_ID_SC8280XP 449 ++#define QCOM_ID_IPQ6005 453 ++#define QCOM_ID_QRB5165 455 ++#define QCOM_ID_SM8450 457 ++#define QCOM_ID_SM7225 459 ++#define QCOM_ID_SA8295P 460 ++#define QCOM_ID_SA8540P 461 ++#define QCOM_ID_SM8450_2 480 ++#define QCOM_ID_SM8450_3 482 ++#define QCOM_ID_SC7280 487 ++#define QCOM_ID_SC7180P 495 ++#define QCOM_ID_SM6375 507 ++ ++/* ++ * The board type and revision information, used by Qualcomm bootloaders and ++ * DTS for older chipsets (qcom,board-id): ++ */ ++#define QCOM_BOARD_ID(a, major, minor) \ ++ (((major & 0xff) << 16) | ((minor & 0xff) << 8) | QCOM_BOARD_ID_##a) ++ ++#define QCOM_BOARD_ID_MTP 8 ++#define QCOM_BOARD_ID_DRAGONBOARD 10 ++#define QCOM_BOARD_ID_SBC 24 ++ ++#endif /* _DT_BINDINGS_ARM_QCOM_IDS_H */ diff --git a/target/linux/ipq807x/patches-6.1/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch b/target/linux/ipq807x/patches-6.1/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch new file mode 100644 index 00000000000..8719bf74c4d --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0080-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch @@ -0,0 +1,24 @@ +From a4748d2850783d36f77ccf2b5fcc86ccf1800ef1 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:48:36 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: set Gen2 PCIe pcie max-link-speed + +Add the generic 'max-link-speed' property to describe the Gen2 PCIe link +generation limit. +This allows the generic DWC code to configure the link speed correctly. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -766,6 +766,7 @@ + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; ++ max-link-speed = <2>; + #address-cells = <3>; + #size-cells = <2>; + diff --git a/target/linux/ipq807x/patches-6.1/0081-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch b/target/linux/ipq807x/patches-6.1/0081-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch new file mode 100644 index 00000000000..60caee46cab --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0081-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch @@ -0,0 +1,26 @@ +From f356132229b18ceef5d5ef9103bbaa9bdeb84c8d Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 13 Jan 2023 17:44:47 +0100 +Subject: [PATCH] PCI: qcom: Add IPQ8074 Gen3 port support + +IPQ8074 has one Gen2 and one Gen3 port, with Gen2 port already supported. +Add compatible for Gen3 port which uses the same controller as IPQ6018. + +Link: https://lore.kernel.org/r/20230113164449.906002-7-robimarko@gmail.com +Signed-off-by: Robert Marko +Signed-off-by: Lorenzo Pieralisi +Signed-off-by: Bjorn Helgaas +--- + drivers/pci/controller/dwc/pcie-qcom.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -1745,6 +1745,7 @@ static const struct of_device_id qcom_pc + { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-ipq8074", .data = &cfg_2_3_3 }, ++ { .compatible = "qcom,pcie-ipq8074-gen3", .data = &cfg_2_9_0 }, + { .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 }, + { .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, + { .compatible = "qcom,pcie-sa8540p", .data = &cfg_1_9_0 }, diff --git a/target/linux/ipq807x/patches-6.1/0082-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch b/target/linux/ipq807x/patches-6.1/0082-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch new file mode 100644 index 00000000000..eb772be4cee --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0082-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch @@ -0,0 +1,38 @@ +From 614d31c231c7707322b643f409eeb7e28adc7f8c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 8 Jan 2023 13:36:28 +0100 +Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for usb3phy-s + +Having only .name populated in parent_data for clocks which are only +globally searchable currently will not work as the clk core won't copy +that name if there is no .fw_name present as well. + +So, populate .fw_name for usb3phy clocks in parent_data as they were +missed by me in ("clk: qcom: ipq8074: populate fw_name for all parents"). + +Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -934,7 +934,7 @@ static struct clk_rcg2 usb0_mock_utmi_cl + }; + + static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { +- { .name = "usb3phy_0_cc_pipe_clk" }, ++ { .fw_name = "usb3phy_0_cc_pipe_clk", .name = "usb3phy_0_cc_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -1002,7 +1002,7 @@ static struct clk_rcg2 usb1_mock_utmi_cl + }; + + static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { +- { .name = "usb3phy_1_cc_pipe_clk" }, ++ { .fw_name = "usb3phy_1_cc_pipe_clk", .name = "usb3phy_1_cc_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + diff --git a/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch b/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch new file mode 100644 index 00000000000..5713775948d --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch @@ -0,0 +1,203 @@ +From 032be4f49dda786fea9e1501212f6cd09a7ded96 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Nov 2022 14:49:43 +0100 +Subject: [PATCH] clk: qcom: clk-rcg2: introduce support for multiple conf for + same freq + +Some RCG frequency can be reached by multiple configuration. + +We currently declare multiple configuration for the same frequency but +that is not supported and always the first configuration will be taken. + +These multiple configuration are needed as based on the current parent +configuration, it may be needed to use a different configuration to +reach the same frequency. + +To handle this introduce 2 new macro, FM and C. + +- FM is used to declare an empty freq_tbl with just the frequency and an + array of confs to insert all the config for the provided frequency. + +- C is used to declare a fre_conf where src, pre_div, m and n are + provided. + +The driver is changed to handle this special freq_tbl and select the +correct config by calculating the final rate and deciding based on the +one that is less different than the requested one. + +Tested-by: Robert Marko +Signed-off-by: Christian Marangi +--- + drivers/clk/qcom/clk-rcg.h | 14 ++++++- + drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++---- + 2 files changed, 88 insertions(+), 10 deletions(-) + +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -7,7 +7,17 @@ + #include + #include "clk-regmap.h" + +-#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } ++#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL } ++ ++#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) } ++#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) } ++ ++struct freq_conf { ++ u8 src; ++ u8 pre_div; ++ u16 m; ++ u16 n; ++}; + + struct freq_tbl { + unsigned long freq; +@@ -15,6 +25,8 @@ struct freq_tbl { + u8 pre_div; + u16 m; + u16 n; ++ int confs_num; ++ const struct freq_conf *confs; + }; + + /** +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, + return __clk_rcg2_recalc_rate(hw, parent_rate, cfg); + } + ++static void ++clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl, ++ const struct freq_tbl *f, unsigned long req_rate) ++{ ++ unsigned long best_rate = 0, parent_rate, rate; ++ const struct freq_conf *conf, *best_conf; ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct clk_hw *p; ++ int index, i; ++ ++ /* Search in each provided config the one that is near the wanted rate */ ++ for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) { ++ index = qcom_find_src_index(hw, rcg->parent_map, conf->src); ++ if (index < 0) ++ continue; ++ ++ p = clk_hw_get_parent_by_index(hw, index); ++ if (!p) ++ continue; ++ ++ parent_rate = clk_hw_get_rate(p); ++ rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div); ++ ++ if (rate == req_rate) { ++ best_conf = conf; ++ break; ++ } ++ ++ if (abs(req_rate - rate) < abs(best_rate - rate)) { ++ best_rate = rate; ++ best_conf = conf; ++ } ++ } ++ ++ /* ++ * Very unlikely. ++ * Force the first conf if we can't find a correct config. ++ */ ++ if (unlikely(i == f->confs_num)) ++ best_conf = f->confs; ++ ++ /* Apply the config */ ++ f_tbl->src = best_conf->src; ++ f_tbl->pre_div = best_conf->pre_div; ++ f_tbl->m = best_conf->m; ++ f_tbl->n = best_conf->n; ++} ++ + static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, + struct clk_rate_request *req, + enum freq_policy policy) + { + unsigned long clk_flags, rate = req->rate; ++ struct freq_tbl f_tbl; + struct clk_hw *p; + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int index; +@@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(stru + if (!f) + return -EINVAL; + +- index = qcom_find_src_index(hw, rcg->parent_map, f->src); ++ f_tbl = *f; ++ /* ++ * A single freq may be reached by multiple configuration. ++ * Try to find the bast one if we have this kind of freq_table. ++ */ ++ if (f->confs) ++ clk_rcg2_select_conf(hw, &f_tbl, f, rate); ++ ++ index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src); + if (index < 0) + return index; + +@@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(stru + return -EINVAL; + + if (clk_flags & CLK_SET_RATE_PARENT) { +- rate = f->freq; +- if (f->pre_div) { ++ rate = f_tbl.freq; ++ if (f_tbl.pre_div) { + if (!rate) + rate = req->rate; + rate /= 2; +- rate *= f->pre_div + 1; ++ rate *= f_tbl.pre_div + 1; + } + +- if (f->n) { ++ if (f_tbl.n) { + u64 tmp = rate; +- tmp = tmp * f->n; +- do_div(tmp, f->m); ++ tmp = tmp * f_tbl.n; ++ do_div(tmp, f_tbl.m); + rate = tmp; + } + } else { +@@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(stru + } + req->best_parent_hw = p; + req->best_parent_rate = rate; +- req->rate = f->freq; ++ req->rate = f_tbl.freq; + + return 0; + } +@@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct cl + { + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f; ++ struct freq_tbl f_tbl; + + switch (policy) { + case FLOOR: +@@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct cl + if (!f) + return -EINVAL; + +- return clk_rcg2_configure(rcg, f); ++ f_tbl = *f; ++ /* ++ * A single freq may be reached by multiple configuration. ++ * Try to find the best one if we have this kind of freq_table. ++ */ ++ if (f->confs) ++ clk_rcg2_select_conf(hw, &f_tbl, f, rate); ++ ++ return clk_rcg2_configure(rcg, &f_tbl); + } + + static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch b/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch new file mode 100644 index 00000000000..32fb2d9d872 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch @@ -0,0 +1,129 @@ +From f778553f296792f4d1e8b3552603ad6116ea3eb3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Nov 2022 14:49:44 +0100 +Subject: [PATCH] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple + conf + +Rework nss_port5/6 to use the new multiple configuration implementation +and correctly fix the clocks for these port under some corner case. + +This is particularly relevant for device that have 2.5G or 10G port +connected to port5 or port 6 on ipq8074. As the parent are shared +across multiple port it may be required to select the correct +configuration to accomplish the desired clock. Without this patch such +port doesn't work in some specific ethernet speed as the clock will be +set to the wrong frequency as we just select the first configuration for +the related frequency instead of selecting the best one. + +Tested-by: Robert Marko # ipq8074 Qnap QHora-301W +Signed-off-by: Christian Marangi +--- + drivers/clk/qcom/gcc-ipq8074.c | 64 +++++++++++++++++++++++++--------- + 1 file changed, 48 insertions(+), 16 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -1682,13 +1682,21 @@ static struct clk_regmap_div nss_port4_t + }, + }; + ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_25[] = { ++ C(P_UNIPHY1_RX, 12.5, 0, 0), ++ C(P_UNIPHY0_RX, 5, 0, 0), ++}; ++ ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_125[] = { ++ C(P_UNIPHY1_RX, 2.5, 0, 0), ++ C(P_UNIPHY0_RX, 1, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_RX, 5, 0, 0), ++ FM(25000000, ftbl_nss_port5_rx_clk_src_25), + F(78125000, P_UNIPHY1_RX, 4, 0, 0), +- F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_RX, 1, 0, 0), ++ FM(125000000, ftbl_nss_port5_rx_clk_src_125), + F(156250000, P_UNIPHY1_RX, 2, 0, 0), + F(312500000, P_UNIPHY1_RX, 1, 0, 0), + { } +@@ -1744,13 +1752,21 @@ static struct clk_regmap_div nss_port5_r + }, + }; + ++static struct freq_conf ftbl_nss_port5_tx_clk_src_25[] = { ++ C(P_UNIPHY1_TX, 12.5, 0, 0), ++ C(P_UNIPHY0_TX, 5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port5_tx_clk_src_125[] = { ++ C(P_UNIPHY1_TX, 2.5, 0, 0), ++ C(P_UNIPHY0_TX, 1, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_TX, 5, 0, 0), ++ FM(25000000, ftbl_nss_port5_tx_clk_src_25), + F(78125000, P_UNIPHY1_TX, 4, 0, 0), +- F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_TX, 1, 0, 0), ++ FM(125000000, ftbl_nss_port5_tx_clk_src_125), + F(156250000, P_UNIPHY1_TX, 2, 0, 0), + F(312500000, P_UNIPHY1_TX, 1, 0, 0), + { } +@@ -1806,13 +1822,21 @@ static struct clk_regmap_div nss_port5_t + }, + }; + ++static struct freq_conf ftbl_nss_port6_rx_clk_src_25[] = { ++ C(P_UNIPHY2_RX, 5, 0, 0), ++ C(P_UNIPHY2_RX, 12.5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port6_rx_clk_src_125[] = { ++ C(P_UNIPHY2_RX, 1, 0, 0), ++ C(P_UNIPHY2_RX, 2.5, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY2_RX, 5, 0, 0), +- F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), ++ FM(25000000, ftbl_nss_port6_rx_clk_src_25), + F(78125000, P_UNIPHY2_RX, 4, 0, 0), +- F(125000000, P_UNIPHY2_RX, 1, 0, 0), +- F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), ++ FM(125000000, ftbl_nss_port6_rx_clk_src_125), + F(156250000, P_UNIPHY2_RX, 2, 0, 0), + F(312500000, P_UNIPHY2_RX, 1, 0, 0), + { } +@@ -1863,13 +1887,21 @@ static struct clk_regmap_div nss_port6_r + }, + }; + ++static struct freq_conf ftbl_nss_port6_tx_clk_src_25[] = { ++ C(P_UNIPHY2_TX, 5, 0, 0), ++ C(P_UNIPHY2_TX, 12.5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port6_tx_clk_src_125[] = { ++ C(P_UNIPHY2_TX, 1, 0, 0), ++ C(P_UNIPHY2_TX, 2.5, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY2_TX, 5, 0, 0), +- F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), ++ FM(25000000, ftbl_nss_port6_tx_clk_src_25), + F(78125000, P_UNIPHY2_TX, 4, 0, 0), +- F(125000000, P_UNIPHY2_TX, 1, 0, 0), +- F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), ++ FM(125000000, ftbl_nss_port6_tx_clk_src_125), + F(156250000, P_UNIPHY2_TX, 2, 0, 0), + F(312500000, P_UNIPHY2_TX, 1, 0, 0), + { } diff --git a/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch new file mode 100644 index 00000000000..af53c077ff1 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch @@ -0,0 +1,70 @@ +From ad2d07f71739351eeea1d8a120c0918e2c4b265f Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 22 Dec 2021 12:23:34 +0100 +Subject: [PATCH] arm64: dts: ipq8074: add reserved memory nodes + +IPQ8074 has multiple reserved memory ranges, if they are not defined +then weird things tend to happen, board hangs and resets when PCI or +WLAN is used etc. + +So, to avoid all of that add the reserved memory nodes from the downstream +5.4 kernel from QCA. +This is their default layout meant for devices with 1GB of RAM, but +devices with lower ammounts can override the Q6 node. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 35 +++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -85,6 +85,26 @@ + #size-cells = <2>; + ranges; + ++ nss@40000000 { ++ no-map; ++ reg = <0x0 0x40000000 0x0 0x01000000>; ++ }; ++ ++ tzapp_region: tzapp@4a400000 { ++ no-map; ++ reg = <0x0 0x4a400000 0x0 0x00200000>; ++ }; ++ ++ uboot@4a600000 { ++ no-map; ++ reg = <0x0 0x4a600000 0x0 0x00400000>; ++ }; ++ ++ sbl@4aa00000 { ++ no-map; ++ reg = <0x0 0x4aa00000 0x0 0x00100000>; ++ }; ++ + smem@4ab00000 { + compatible = "qcom,smem"; + reg = <0x0 0x4ab00000 0x0 0x00100000>; +@@ -97,6 +117,21 @@ + no-map; + reg = <0x0 0x4ac00000 0x0 0x00400000>; + }; ++ ++ q6_region: wcnss@4b000000 { ++ no-map; ++ reg = <0x0 0x4b000000 0x0 0x05f00000>; ++ }; ++ ++ q6_etr_region: q6_etr_dump@50f00000 { ++ no-map; ++ reg = <0x0 0x50f00000 0x0 0x00100000>; ++ }; ++ ++ m3_dump_region: m3_dump@51000000 { ++ no-map; ++ reg = <0x0 0x51000000 0x0 0x100000>; ++ }; + }; + + firmware { diff --git a/target/linux/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch b/target/linux/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch new file mode 100644 index 00000000000..69d4126f763 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch @@ -0,0 +1,30 @@ +From 8a576b5bc9f0555d1d970cacabcaa24a3b74fa57 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:15:01 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: pass QMP PCI PHY PIPE clocks to + GCC + +Pass QMP PCI PHY PIPE clocks to the GCC controller so it does not have to +find them by matching globaly by name. + +If not passed directly, driver maintains backwards compatibility by then +falling back to global lookup. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -396,8 +396,8 @@ + gcc: gcc@1800000 { + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; +- clocks = <&xo>, <&sleep_clk>; +- clock-names = "xo", "sleep_clk"; ++ clocks = <&xo>, <&sleep_clk>, <&pcie_phy0>, <&pcie_phy1>; ++ clock-names = "xo", "sleep_clk", "pcie0_pipe", "pcie1_pipe"; + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch b/target/linux/ipq807x/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch new file mode 100644 index 00000000000..9fefd8852ac --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch @@ -0,0 +1,43 @@ +From fb1f6850be00d8dd8a54017be4c1336e224069ac Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:26:25 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: use msi-parent for PCIe + +Instead of hardcoding the IRQ, simply use msi-parent instead. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -699,7 +699,7 @@ + reg = <0x0b000000 0x1000>, <0x0b002000 0x1000>; + ranges = <0 0xb00a000 0xffd>; + +- v2m@0 { ++ gic_v2m0: v2m@0 { + compatible = "arm,gic-v2m-frame"; + msi-controller; + reg = <0x0 0xffd>; +@@ -811,8 +811,7 @@ + ranges = <0x81000000 0x0 0x00000000 0x10200000 0x0 0x10000>, /* I/O */ + <0x82000000 0x0 0x10220000 0x10220000 0x0 0xfde0000>; /* MEM */ + +- interrupts = ; +- interrupt-names = "msi"; ++ msi-parent = <&gic_v2m0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 142 +@@ -873,8 +872,7 @@ + ranges = <0x81000000 0x0 0x00000000 0x20200000 0x0 0x10000>, /* I/O */ + <0x82000000 0x0 0x20220000 0x20220000 0x0 0xfde0000>; /* MEM */ + +- interrupts = ; +- interrupt-names = "msi"; ++ msi-parent = <&gic_v2m0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 75 diff --git a/target/linux/ipq807x/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch b/target/linux/ipq807x/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch new file mode 100644 index 00000000000..0a984948b53 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch @@ -0,0 +1,155 @@ +From 125681433c8e526356947acf572fe8ca8ad32291 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:05 +0530 +Subject: [PATCH] remoteproc: qcom: Add PRNG proxy clock + +PRNG clock is needed by the secure PIL, support for the same +is added in subsequent patches. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 65 +++++++++++++++++++++-------- + 1 file changed, 47 insertions(+), 18 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -91,19 +91,6 @@ enum { + WCSS_QCS404, + }; + +-struct wcss_data { +- const char *firmware_name; +- unsigned int crash_reason_smem; +- u32 version; +- bool aon_reset_required; +- bool wcss_q6_reset_required; +- const char *ssr_name; +- const char *sysmon_name; +- int ssctl_id; +- const struct rproc_ops *ops; +- bool requires_force_stop; +-}; +- + struct q6v5_wcss { + struct device *dev; + +@@ -128,6 +115,7 @@ struct q6v5_wcss { + struct clk *qdsp6ss_xo_cbcr; + struct clk *qdsp6ss_core_gfmux; + struct clk *lcc_bcr_sleep; ++ struct clk *prng_clk; + struct regulator *cx_supply; + struct qcom_sysmon *sysmon; + +@@ -151,6 +139,21 @@ struct q6v5_wcss { + struct qcom_rproc_ssr ssr_subdev; + }; + ++struct wcss_data { ++ int (*init_clock)(struct q6v5_wcss *wcss); ++ int (*init_regulator)(struct q6v5_wcss *wcss); ++ const char *firmware_name; ++ unsigned int crash_reason_smem; ++ u32 version; ++ bool aon_reset_required; ++ bool wcss_q6_reset_required; ++ const char *ssr_name; ++ const char *sysmon_name; ++ int ssctl_id; ++ const struct rproc_ops *ops; ++ bool requires_force_stop; ++}; ++ + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) + { + int ret; +@@ -240,6 +243,12 @@ static int q6v5_wcss_start(struct rproc + struct q6v5_wcss *wcss = rproc->priv; + int ret; + ++ ret = clk_prepare_enable(wcss->prng_clk); ++ if (ret) { ++ dev_err(wcss->dev, "prng clock enable failed\n"); ++ return ret; ++ } ++ + qcom_q6v5_prepare(&wcss->q6v5); + + /* Release Q6 and WCSS reset */ +@@ -733,6 +742,7 @@ static int q6v5_wcss_stop(struct rproc * + return ret; + } + ++ clk_disable_unprepare(wcss->prng_clk); + qcom_q6v5_unprepare(&wcss->q6v5); + + return 0; +@@ -900,7 +910,21 @@ static int q6v5_alloc_memory_region(stru + return 0; + } + +-static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss) ++static int ipq8074_init_clock(struct q6v5_wcss *wcss) ++{ ++ int ret; ++ ++ wcss->prng_clk = devm_clk_get(wcss->dev, "prng"); ++ if (IS_ERR(wcss->prng_clk)) { ++ ret = PTR_ERR(wcss->prng_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(wcss->dev, "Failed to get prng clock\n"); ++ return ret; ++ } ++ return 0; ++} ++ ++static int qcs404_init_clock(struct q6v5_wcss *wcss) + { + int ret; + +@@ -990,7 +1014,7 @@ static int q6v5_wcss_init_clock(struct q + return 0; + } + +-static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss) ++static int qcs404_init_regulator(struct q6v5_wcss *wcss) + { + wcss->cx_supply = devm_regulator_get(wcss->dev, "cx"); + if (IS_ERR(wcss->cx_supply)) +@@ -1034,12 +1058,14 @@ static int q6v5_wcss_probe(struct platfo + if (ret) + goto free_rproc; + +- if (wcss->version == WCSS_QCS404) { +- ret = q6v5_wcss_init_clock(wcss); ++ if (desc->init_clock) { ++ ret = desc->init_clock(wcss); + if (ret) + goto free_rproc; ++ } + +- ret = q6v5_wcss_init_regulator(wcss); ++ if (desc->init_regulator) { ++ ret = desc->init_regulator(wcss); + if (ret) + goto free_rproc; + } +@@ -1087,6 +1113,7 @@ static int q6v5_wcss_remove(struct platf + } + + static const struct wcss_data wcss_ipq8074_res_init = { ++ .init_clock = ipq8074_init_clock, + .firmware_name = "IPQ8074/q6_fw.mdt", + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, +@@ -1096,6 +1123,8 @@ static const struct wcss_data wcss_ipq80 + }; + + static const struct wcss_data wcss_qcs404_res_init = { ++ .init_clock = qcs404_init_clock, ++ .init_regulator = qcs404_init_regulator, + .crash_reason_smem = WCSS_CRASH_REASON, + .firmware_name = "wcnss.mdt", + .version = WCSS_QCS404, diff --git a/target/linux/ipq807x/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch b/target/linux/ipq807x/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch new file mode 100644 index 00000000000..0328efc041b --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch @@ -0,0 +1,143 @@ +From 7358d42dfbdfdb5d4f1d0d4c2e5c2bb4143a29b0 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:06 +0530 +Subject: [PATCH] remoteproc: qcom: Add secure PIL support + +IPQ8074 uses secure PIL. Hence, adding the support for the same. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 43 +++++++++++++++++++++++++++-- + 1 file changed, 40 insertions(+), 3 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include "qcom_common.h" + #include "qcom_pil_info.h" + #include "qcom_q6v5.h" +@@ -86,6 +87,9 @@ + #define TCSR_WCSS_CLK_ENABLE 0x14 + + #define MAX_HALT_REG 3 ++ ++#define WCNSS_PAS_ID 6 ++ + enum { + WCSS_IPQ8074, + WCSS_QCS404, +@@ -134,6 +138,7 @@ struct q6v5_wcss { + unsigned int crash_reason_smem; + u32 version; + bool requires_force_stop; ++ bool need_mem_protection; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; +@@ -152,6 +157,7 @@ struct wcss_data { + int ssctl_id; + const struct rproc_ops *ops; + bool requires_force_stop; ++ bool need_mem_protection; + }; + + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) +@@ -251,6 +257,15 @@ static int q6v5_wcss_start(struct rproc + + qcom_q6v5_prepare(&wcss->q6v5); + ++ if (wcss->need_mem_protection) { ++ ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); ++ if (ret) { ++ dev_err(wcss->dev, "wcss_reset failed\n"); ++ return ret; ++ } ++ goto wait_for_reset; ++ } ++ + /* Release Q6 and WCSS reset */ + ret = reset_control_deassert(wcss->wcss_reset); + if (ret) { +@@ -285,6 +300,7 @@ static int q6v5_wcss_start(struct rproc + if (ret) + goto wcss_q6_reset; + ++wait_for_reset: + ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ); + if (ret == -ETIMEDOUT) + dev_err(wcss->dev, "start timed out\n"); +@@ -718,6 +734,15 @@ static int q6v5_wcss_stop(struct rproc * + struct q6v5_wcss *wcss = rproc->priv; + int ret; + ++ if (wcss->need_mem_protection) { ++ ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); ++ if (ret) { ++ dev_err(wcss->dev, "not able to shutdown\n"); ++ return ret; ++ } ++ goto pas_done; ++ } ++ + /* WCSS powerdown */ + if (wcss->requires_force_stop) { + ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL); +@@ -742,6 +767,7 @@ static int q6v5_wcss_stop(struct rproc * + return ret; + } + ++pas_done: + clk_disable_unprepare(wcss->prng_clk); + qcom_q6v5_unprepare(&wcss->q6v5); + +@@ -765,9 +791,15 @@ static int q6v5_wcss_load(struct rproc * + struct q6v5_wcss *wcss = rproc->priv; + int ret; + +- ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, +- 0, wcss->mem_region, wcss->mem_phys, +- wcss->mem_size, &wcss->mem_reloc); ++ if (wcss->need_mem_protection) ++ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, ++ WCNSS_PAS_ID, wcss->mem_region, ++ wcss->mem_phys, wcss->mem_size, ++ &wcss->mem_reloc); ++ else ++ ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, ++ 0, wcss->mem_region, wcss->mem_phys, ++ wcss->mem_size, &wcss->mem_reloc); + if (ret) + return ret; + +@@ -1036,6 +1068,9 @@ static int q6v5_wcss_probe(struct platfo + if (!desc) + return -EINVAL; + ++ if (desc->need_mem_protection && !qcom_scm_is_available()) ++ return -EPROBE_DEFER; ++ + rproc = rproc_alloc(&pdev->dev, pdev->name, desc->ops, + desc->firmware_name, sizeof(*wcss)); + if (!rproc) { +@@ -1049,6 +1084,7 @@ static int q6v5_wcss_probe(struct platfo + + wcss->version = desc->version; + wcss->requires_force_stop = desc->requires_force_stop; ++ wcss->need_mem_protection = desc->need_mem_protection; + + ret = q6v5_wcss_init_mmio(wcss, pdev); + if (ret) +@@ -1120,6 +1156,7 @@ static const struct wcss_data wcss_ipq80 + .wcss_q6_reset_required = true, + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, ++ .need_mem_protection = true, + }; + + static const struct wcss_data wcss_qcs404_res_init = { diff --git a/target/linux/ipq807x/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch b/target/linux/ipq807x/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch new file mode 100644 index 00000000000..e5c9506c89c --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch @@ -0,0 +1,103 @@ +From b422c9d4f048b086ce83f44a7cfcddcce162897f Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:07 +0530 +Subject: [PATCH] remoteproc: qcom: Add support for split q6 + m3 wlan firmware + +IPQ8074 supports split firmware for q6 and m3 as well. +So add support for loading the m3 firmware before q6. +Now the drivers works fine for both split and unified +firmwares. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 33 +++++++++++++++++++++++++---- + 1 file changed, 29 insertions(+), 4 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -139,6 +139,7 @@ struct q6v5_wcss { + u32 version; + bool requires_force_stop; + bool need_mem_protection; ++ const char *m3_firmware_name; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; +@@ -147,7 +148,8 @@ struct q6v5_wcss { + struct wcss_data { + int (*init_clock)(struct q6v5_wcss *wcss); + int (*init_regulator)(struct q6v5_wcss *wcss); +- const char *firmware_name; ++ const char *q6_firmware_name; ++ const char *m3_firmware_name; + unsigned int crash_reason_smem; + u32 version; + bool aon_reset_required; +@@ -789,8 +791,29 @@ static void *q6v5_wcss_da_to_va(struct r + static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) + { + struct q6v5_wcss *wcss = rproc->priv; ++ const struct firmware *m3_fw; + int ret; + ++ if (wcss->m3_firmware_name) { ++ ret = request_firmware(&m3_fw, wcss->m3_firmware_name, ++ wcss->dev); ++ if (ret) ++ goto skip_m3; ++ ++ ret = qcom_mdt_load_no_init(wcss->dev, m3_fw, ++ wcss->m3_firmware_name, 0, ++ wcss->mem_region, wcss->mem_phys, ++ wcss->mem_size, &wcss->mem_reloc); ++ ++ release_firmware(m3_fw); ++ ++ if (ret) { ++ dev_err(wcss->dev, "can't load m3_fw.bXX\n"); ++ return ret; ++ } ++ } ++ ++skip_m3: + if (wcss->need_mem_protection) + ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, + WCNSS_PAS_ID, wcss->mem_region, +@@ -1072,7 +1095,7 @@ static int q6v5_wcss_probe(struct platfo + return -EPROBE_DEFER; + + rproc = rproc_alloc(&pdev->dev, pdev->name, desc->ops, +- desc->firmware_name, sizeof(*wcss)); ++ desc->q6_firmware_name, sizeof(*wcss)); + if (!rproc) { + dev_err(&pdev->dev, "failed to allocate rproc\n"); + return -ENOMEM; +@@ -1085,6 +1108,7 @@ static int q6v5_wcss_probe(struct platfo + wcss->version = desc->version; + wcss->requires_force_stop = desc->requires_force_stop; + wcss->need_mem_protection = desc->need_mem_protection; ++ wcss->m3_firmware_name = desc->m3_firmware_name; + + ret = q6v5_wcss_init_mmio(wcss, pdev); + if (ret) +@@ -1150,7 +1174,8 @@ static int q6v5_wcss_remove(struct platf + + static const struct wcss_data wcss_ipq8074_res_init = { + .init_clock = ipq8074_init_clock, +- .firmware_name = "IPQ8074/q6_fw.mdt", ++ .q6_firmware_name = "IPQ8074/q6_fw.mdt", ++ .m3_firmware_name = "IPQ8074/m3_fw.mdt", + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, +@@ -1163,7 +1188,7 @@ static const struct wcss_data wcss_qcs40 + .init_clock = qcs404_init_clock, + .init_regulator = qcs404_init_regulator, + .crash_reason_smem = WCSS_CRASH_REASON, +- .firmware_name = "wcnss.mdt", ++ .q6_firmware_name = "wcnss.mdt", + .version = WCSS_QCS404, + .aon_reset_required = false, + .wcss_q6_reset_required = false, diff --git a/target/linux/ipq807x/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch b/target/linux/ipq807x/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch new file mode 100644 index 00000000000..be63d46e8e4 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch @@ -0,0 +1,24 @@ +From 3a8f67b4770c817b04794c9a02e3f88f85d86280 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:08 +0530 +Subject: [PATCH] remoteproc: qcom: Add ssr subdevice identifier + +Add name for ssr subdevice on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -1179,6 +1179,7 @@ static const struct wcss_data wcss_ipq80 + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, ++ .ssr_name = "q6wcss", + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, + .need_mem_protection = true, diff --git a/target/linux/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch b/target/linux/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch new file mode 100644 index 00000000000..f0b717210fb --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch @@ -0,0 +1,79 @@ +From 8c73af6e8d78c66cfef0f551b00d375ec0b67ff3 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:09 +0530 +Subject: [PATCH] remoteproc: qcom: Update regmap offsets for halt register + +Fixed issue in reading halt-regs parameter from device-tree. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 22 ++++++++++++++-------- + 1 file changed, 14 insertions(+), 8 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -86,7 +86,7 @@ + #define TCSR_WCSS_CLK_MASK 0x1F + #define TCSR_WCSS_CLK_ENABLE 0x14 + +-#define MAX_HALT_REG 3 ++#define MAX_HALT_REG 4 + + #define WCNSS_PAS_ID 6 + +@@ -154,6 +154,7 @@ struct wcss_data { + u32 version; + bool aon_reset_required; + bool wcss_q6_reset_required; ++ bool bcr_reset_required; + const char *ssr_name; + const char *sysmon_name; + int ssctl_id; +@@ -875,10 +876,13 @@ static int q6v5_wcss_init_reset(struct q + } + } + +- wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset"); +- if (IS_ERR(wcss->wcss_q6_bcr_reset)) { +- dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); +- return PTR_ERR(wcss->wcss_q6_bcr_reset); ++ if (desc->bcr_reset_required) { ++ wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, ++ "wcss_q6_bcr_reset"); ++ if (IS_ERR(wcss->wcss_q6_bcr_reset)) { ++ dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); ++ return PTR_ERR(wcss->wcss_q6_bcr_reset); ++ } + } + + return 0; +@@ -929,9 +933,9 @@ static int q6v5_wcss_init_mmio(struct q6 + return -EINVAL; + } + +- wcss->halt_q6 = halt_reg[0]; +- wcss->halt_wcss = halt_reg[1]; +- wcss->halt_nc = halt_reg[2]; ++ wcss->halt_q6 = halt_reg[1]; ++ wcss->halt_wcss = halt_reg[2]; ++ wcss->halt_nc = halt_reg[3]; + + return 0; + } +@@ -1179,6 +1183,7 @@ static const struct wcss_data wcss_ipq80 + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, ++ .bcr_reset_required = false, + .ssr_name = "q6wcss", + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, +@@ -1193,6 +1198,7 @@ static const struct wcss_data wcss_qcs40 + .version = WCSS_QCS404, + .aon_reset_required = false, + .wcss_q6_reset_required = false, ++ .bcr_reset_required = true, + .ssr_name = "mpss", + .sysmon_name = "wcnss", + .ssctl_id = 0x12, diff --git a/target/linux/ipq807x/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch b/target/linux/ipq807x/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch new file mode 100644 index 00000000000..fe0e0f9e0bf --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch @@ -0,0 +1,26 @@ +From ff7c6533ed8c4de58ed6c8aab03ea59c03eb4f31 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:10 +0530 +Subject: [PATCH] dt-bindings: clock: qcom: Add reset for WCSSAON + +Add binding for WCSSAON reset required for Q6v5 reset on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Acked-by: Rob Herring +Acked-by: Stephen Boyd +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -381,6 +381,7 @@ + #define GCC_NSSPORT4_RESET 143 + #define GCC_NSSPORT5_RESET 144 + #define GCC_NSSPORT6_RESET 145 ++#define GCC_WCSSAON_RESET 146 + + #define USB0_GDSC 0 + #define USB1_GDSC 1 diff --git a/target/linux/ipq807x/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch b/target/linux/ipq807x/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch new file mode 100644 index 00000000000..791531775ef --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch @@ -0,0 +1,25 @@ +From 43d9788f546d24df22d8ba3fcc2497d7ccc198f3 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:11 +0530 +Subject: [PATCH] clk: qcom: Add WCSSAON reset + +Add WCSSAON reset required for Q6v5 on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Acked-by: Stephen Boyd +--- + drivers/clk/qcom/gcc-ipq8074.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4717,6 +4717,7 @@ static const struct qcom_reset_map gcc_i + [GCC_NSSPORT4_RESET] = { .reg = 0x68014, .bitmask = BIT(27) | GENMASK(9, 8) }, + [GCC_NSSPORT5_RESET] = { .reg = 0x68014, .bitmask = BIT(28) | GENMASK(11, 10) }, + [GCC_NSSPORT6_RESET] = { .reg = 0x68014, .bitmask = BIT(29) | GENMASK(13, 12) }, ++ [GCC_WCSSAON_RESET] = { 0x59010, 0 }, + }; + + static struct gdsc *gcc_ipq8074_gdscs[] = { diff --git a/target/linux/ipq807x/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch b/target/linux/ipq807x/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch new file mode 100644 index 00000000000..8674522a112 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch @@ -0,0 +1,48 @@ +From 406a332fd1bcc4e18d73cce390f56272fe9111d7 Mon Sep 17 00:00:00 2001 +From: Sivaprakash Murugesan +Date: Fri, 17 Apr 2020 16:37:10 +0530 +Subject: [PATCH] remoteproc: wcss: disable auto boot for IPQ8074 + +There is no need for remoteproc to boot automatically, ath11k will trigger +booting when its probing. + +Signed-off-by: Sivaprakash Murugesan +Signed-off-by: Robert Marko +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -161,6 +161,7 @@ struct wcss_data { + const struct rproc_ops *ops; + bool requires_force_stop; + bool need_mem_protection; ++ bool need_auto_boot; + }; + + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) +@@ -1150,6 +1151,7 @@ static int q6v5_wcss_probe(struct platfo + desc->sysmon_name, + desc->ssctl_id); + ++ rproc->auto_boot = desc->need_auto_boot; + ret = rproc_add(rproc); + if (ret) + goto free_rproc; +@@ -1188,6 +1190,7 @@ static const struct wcss_data wcss_ipq80 + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, + .need_mem_protection = true, ++ .need_auto_boot = false, + }; + + static const struct wcss_data wcss_qcs404_res_init = { +@@ -1204,6 +1207,7 @@ static const struct wcss_data wcss_qcs40 + .ssctl_id = 0x12, + .ops = &q6v5_wcss_qcs404_ops, + .requires_force_stop = false, ++ .need_auto_boot = true, + }; + + static const struct of_device_id q6v5_wcss_of_match[] = { diff --git a/target/linux/ipq807x/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch b/target/linux/ipq807x/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch new file mode 100644 index 00000000000..e37ba37e960 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch @@ -0,0 +1,120 @@ +From 7388400b8bd42f71d040dbf2fdbdcb834fcc0ede Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:13 +0530 +Subject: [PATCH] arm64: dts: qcom: Enable Q6v5 WCSS for ipq8074 SoC + +Enable remoteproc WCSS PIL driver with glink and ssr subdevices. +Also enables smp2p and mailboxes required for IPC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 81 +++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -140,6 +140,32 @@ + }; + }; + ++ wcss: smp2p-wcss { ++ compatible = "qcom,smp2p"; ++ qcom,smem = <435>, <428>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <0 322 1>; ++ ++ mboxes = <&apcs_glb 9>; ++ ++ qcom,local-pid = <0>; ++ qcom,remote-pid = <1>; ++ ++ wcss_smp2p_out: master-kernel { ++ qcom,entry-name = "master-kernel"; ++ qcom,smp2p-feature-ssr-ack; ++ #qcom,smem-state-cells = <1>; ++ }; ++ ++ wcss_smp2p_in: slave-kernel { ++ qcom,entry-name = "slave-kernel"; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; ++ + soc: soc { + #address-cells = <0x1>; + #size-cells = <0x1>; +@@ -409,6 +435,11 @@ + #hwlock-cells = <1>; + }; + ++ tcsr_q6: syscon@1945000 { ++ compatible = "syscon"; ++ reg = <0x01945000 0xe000>; ++ }; ++ + spmi_bus: spmi@200f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x0200f000 0x001000>, +@@ -913,6 +944,56 @@ + "axi_s_sticky"; + status = "disabled"; + }; ++ ++ q6v5_wcss: q6v5_wcss@cd00000 { ++ compatible = "qcom,ipq8074-wcss-pil"; ++ reg = <0x0cd00000 0x4040>, ++ <0x004ab000 0x20>; ++ reg-names = "qdsp6", ++ "rmb"; ++ qca,auto-restart; ++ qca,extended-intc; ++ interrupts-extended = <&intc 0 325 1>, ++ <&wcss_smp2p_in 0 0>, ++ <&wcss_smp2p_in 1 0>, ++ <&wcss_smp2p_in 2 0>, ++ <&wcss_smp2p_in 3 0>; ++ interrupt-names = "wdog", ++ "fatal", ++ "ready", ++ "handover", ++ "stop-ack"; ++ ++ resets = <&gcc GCC_WCSSAON_RESET>, ++ <&gcc GCC_WCSS_BCR>, ++ <&gcc GCC_WCSS_Q6_BCR>; ++ ++ reset-names = "wcss_aon_reset", ++ "wcss_reset", ++ "wcss_q6_reset"; ++ ++ clocks = <&gcc GCC_PRNG_AHB_CLK>; ++ clock-names = "prng"; ++ ++ qcom,halt-regs = <&tcsr_q6 0xa000 0xd000 0x0>; ++ ++ qcom,smem-states = <&wcss_smp2p_out 0>, ++ <&wcss_smp2p_out 1>; ++ qcom,smem-state-names = "shutdown", ++ "stop"; ++ ++ memory-region = <&q6_region>; ++ ++ glink-edge { ++ interrupts = ; ++ qcom,remote-pid = <1>; ++ mboxes = <&apcs_glb 8>; ++ ++ rpm_requests { ++ qcom,glink-channels = "IPCRTR"; ++ }; ++ }; ++ }; + }; + + timer { diff --git a/target/linux/ipq807x/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch b/target/linux/ipq807x/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch new file mode 100644 index 00000000000..bd5410c934a --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch @@ -0,0 +1,135 @@ +From a67d1901741c162645eda0dbdc3a2c0c2aff5cf4 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 21 Dec 2021 14:49:36 +0100 +Subject: [PATCH] arm64: dts: ipq8074: Add WLAN node + +IPQ8074 has a AHB based Q6v5 802.11ax radios that are supported +by the ath11k. + +Add the required DT node to enable the built-in radios. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 111 ++++++++++++++++++++++++++ + 1 file changed, 111 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -994,6 +994,117 @@ + }; + }; + }; ++ ++ wifi: wifi@c0000000 { ++ compatible = "qcom,ipq8074-wifi"; ++ reg = <0xc000000 0x2000000>; ++ ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ ++ interrupt-names = "misc-pulse1", ++ "misc-latch", ++ "sw-exception", ++ "ce0", ++ "ce1", ++ "ce2", ++ "ce3", ++ "ce4", ++ "ce5", ++ "ce6", ++ "ce7", ++ "ce8", ++ "ce9", ++ "ce10", ++ "ce11", ++ "host2wbm-desc-feed", ++ "host2reo-re-injection", ++ "host2reo-command", ++ "host2rxdma-monitor-ring3", ++ "host2rxdma-monitor-ring2", ++ "host2rxdma-monitor-ring1", ++ "reo2ost-exception", ++ "wbm2host-rx-release", ++ "reo2host-status", ++ "reo2host-destination-ring4", ++ "reo2host-destination-ring3", ++ "reo2host-destination-ring2", ++ "reo2host-destination-ring1", ++ "rxdma2host-monitor-destination-mac3", ++ "rxdma2host-monitor-destination-mac2", ++ "rxdma2host-monitor-destination-mac1", ++ "ppdu-end-interrupts-mac3", ++ "ppdu-end-interrupts-mac2", ++ "ppdu-end-interrupts-mac1", ++ "rxdma2host-monitor-status-ring-mac3", ++ "rxdma2host-monitor-status-ring-mac2", ++ "rxdma2host-monitor-status-ring-mac1", ++ "host2rxdma-host-buf-ring-mac3", ++ "host2rxdma-host-buf-ring-mac2", ++ "host2rxdma-host-buf-ring-mac1", ++ "rxdma2host-destination-ring-mac3", ++ "rxdma2host-destination-ring-mac2", ++ "rxdma2host-destination-ring-mac1", ++ "host2tcl-input-ring4", ++ "host2tcl-input-ring3", ++ "host2tcl-input-ring2", ++ "host2tcl-input-ring1", ++ "wbm2host-tx-completions-ring3", ++ "wbm2host-tx-completions-ring2", ++ "wbm2host-tx-completions-ring1", ++ "tcl2host-status-ring"; ++ qcom,rproc = <&q6v5_wcss>; ++ status = "disabled"; ++ }; + }; + + timer { diff --git a/target/linux/ipq807x/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch b/target/linux/ipq807x/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch new file mode 100644 index 00000000000..a3c5f344ab3 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch @@ -0,0 +1,59 @@ +From cb3ef99c1553565e1dc0301ccd5c1c0fa2d15c15 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 31 Dec 2021 17:56:14 +0100 +Subject: [PATCH] arm64: dts: ipq8074: add CPU clock + +Now that CPU clock is exposed and can be controlled, add the necessary +properties to the CPU nodes. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + / { + #address-cells = <2>; +@@ -38,6 +39,8 @@ + reg = <0x0>; + next-level-cache = <&L2_0>; + enable-method = "psci"; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU1: cpu@1 { +@@ -46,6 +49,8 @@ + enable-method = "psci"; + reg = <0x1>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU2: cpu@2 { +@@ -54,6 +59,8 @@ + enable-method = "psci"; + reg = <0x2>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU3: cpu@3 { +@@ -62,6 +69,8 @@ + enable-method = "psci"; + reg = <0x3>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + L2_0: l2-cache { diff --git a/target/linux/ipq807x/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch b/target/linux/ipq807x/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch new file mode 100644 index 00000000000..3520b381345 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch @@ -0,0 +1,48 @@ +From 347ca56e86c99021fad059b9a8ef101245b8507e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 31 Dec 2021 20:38:06 +0100 +Subject: [PATCH] arm64: dts: ipq8074: add cooling cells to CPU nodes + +Since there is CPU Freq support as well as thermal sensor support +now for the IPQ8074, add cooling cells to CPU nodes so that they can +be used as cooling devices using CPU Freq. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -41,6 +41,7 @@ + enable-method = "psci"; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU1: cpu@1 { +@@ -51,6 +52,7 @@ + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU2: cpu@2 { +@@ -61,6 +63,7 @@ + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU3: cpu@3 { +@@ -71,6 +74,7 @@ + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + L2_0: l2-cache { diff --git a/target/linux/ipq807x/patches-6.1/0124-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch b/target/linux/ipq807x/patches-6.1/0124-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch new file mode 100644 index 00000000000..4a744490cfe --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0124-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch @@ -0,0 +1,168 @@ +From 97505f4c049fa2e8c86a53411a9e599033898533 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 31 Dec 2022 00:27:42 +0100 +Subject: [PATCH] soc: qcom: socinfo: move SMEM item struct and defines to a + header + +Move SMEM item struct and related defines to a header in order to be able +to reuse them in the Qualcomm NVMEM CPUFreq driver instead of duplicating +them. + +Signed-off-by: Robert Marko +--- + drivers/soc/qcom/socinfo.c | 58 +-------------------------- + include/linux/soc/qcom/socinfo.h | 67 ++++++++++++++++++++++++++++++++ + 2 files changed, 68 insertions(+), 57 deletions(-) + create mode 100644 include/linux/soc/qcom/socinfo.h + +--- a/drivers/soc/qcom/socinfo.c ++++ b/drivers/soc/qcom/socinfo.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -25,15 +26,6 @@ + #define SOCINFO_MINOR(ver) ((ver) & 0xffff) + #define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) + +-#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 +-#define SMEM_SOCINFO_CHIP_ID_LENGTH 32 +- +-/* +- * SMEM item id, used to acquire handles to respective +- * SMEM region. +- */ +-#define SMEM_HW_SW_BUILD_ID 137 +- + #ifdef CONFIG_DEBUG_FS + #define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32 + #define SMEM_IMAGE_VERSION_SIZE 4096 +@@ -116,54 +108,6 @@ static const char *const pmic_models[] = + }; + #endif /* CONFIG_DEBUG_FS */ + +-/* Socinfo SMEM item structure */ +-struct socinfo { +- __le32 fmt; +- __le32 id; +- __le32 ver; +- char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; +- /* Version 2 */ +- __le32 raw_id; +- __le32 raw_ver; +- /* Version 3 */ +- __le32 hw_plat; +- /* Version 4 */ +- __le32 plat_ver; +- /* Version 5 */ +- __le32 accessory_chip; +- /* Version 6 */ +- __le32 hw_plat_subtype; +- /* Version 7 */ +- __le32 pmic_model; +- __le32 pmic_die_rev; +- /* Version 8 */ +- __le32 pmic_model_1; +- __le32 pmic_die_rev_1; +- __le32 pmic_model_2; +- __le32 pmic_die_rev_2; +- /* Version 9 */ +- __le32 foundry_id; +- /* Version 10 */ +- __le32 serial_num; +- /* Version 11 */ +- __le32 num_pmics; +- __le32 pmic_array_offset; +- /* Version 12 */ +- __le32 chip_family; +- __le32 raw_device_family; +- __le32 raw_device_num; +- /* Version 13 */ +- __le32 nproduct_id; +- char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH]; +- /* Version 14 */ +- __le32 num_clusters; +- __le32 ncluster_array_offset; +- __le32 num_defective_parts; +- __le32 ndefective_parts_array_offset; +- /* Version 15 */ +- __le32 nmodem_supported; +-}; +- + #ifdef CONFIG_DEBUG_FS + struct socinfo_params { + u32 raw_device_family; +--- /dev/null ++++ b/include/linux/soc/qcom/socinfo.h +@@ -0,0 +1,67 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2017-2019, Linaro Ltd. ++ */ ++ ++#ifndef __QCOM_SOCINFO_H__ ++#define __QCOM_SOCINFO_H__ ++ ++/* ++ * SMEM item id, used to acquire handles to respective ++ * SMEM region. ++ */ ++#define SMEM_HW_SW_BUILD_ID 137 ++ ++#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 ++#define SMEM_SOCINFO_CHIP_ID_LENGTH 32 ++ ++/* Socinfo SMEM item structure */ ++struct socinfo { ++ __le32 fmt; ++ __le32 id; ++ __le32 ver; ++ char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; ++ /* Version 2 */ ++ __le32 raw_id; ++ __le32 raw_ver; ++ /* Version 3 */ ++ __le32 hw_plat; ++ /* Version 4 */ ++ __le32 plat_ver; ++ /* Version 5 */ ++ __le32 accessory_chip; ++ /* Version 6 */ ++ __le32 hw_plat_subtype; ++ /* Version 7 */ ++ __le32 pmic_model; ++ __le32 pmic_die_rev; ++ /* Version 8 */ ++ __le32 pmic_model_1; ++ __le32 pmic_die_rev_1; ++ __le32 pmic_model_2; ++ __le32 pmic_die_rev_2; ++ /* Version 9 */ ++ __le32 foundry_id; ++ /* Version 10 */ ++ __le32 serial_num; ++ /* Version 11 */ ++ __le32 num_pmics; ++ __le32 pmic_array_offset; ++ /* Version 12 */ ++ __le32 chip_family; ++ __le32 raw_device_family; ++ __le32 raw_device_num; ++ /* Version 13 */ ++ __le32 nproduct_id; ++ char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH]; ++ /* Version 14 */ ++ __le32 num_clusters; ++ __le32 ncluster_array_offset; ++ __le32 num_defective_parts; ++ __le32 ndefective_parts_array_offset; ++ /* Version 15 */ ++ __le32 nmodem_supported; ++}; ++ ++#endif diff --git a/target/linux/ipq807x/patches-6.1/0125-cpufreq-qcom-nvmem-reuse-socinfo-SMEM-item-struct.patch b/target/linux/ipq807x/patches-6.1/0125-cpufreq-qcom-nvmem-reuse-socinfo-SMEM-item-struct.patch new file mode 100644 index 00000000000..5776614463e --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0125-cpufreq-qcom-nvmem-reuse-socinfo-SMEM-item-struct.patch @@ -0,0 +1,50 @@ +From b7b7ea3a0cab42d4f1d4c9ae9eb7c7a3d03e7982 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 30 Dec 2022 22:51:47 +0100 +Subject: [PATCH] cpufreq: qcom-nvmem: reuse socinfo SMEM item struct + +Now that socinfo SMEM item struct and defines have been moved to a header +so we can utilize that instead. + +Now the SMEM value can be accesed directly, there is no need for defining +the ID for the SMEM request as well. + +Signed-off-by: Robert Marko +--- + drivers/cpufreq/qcom-cpufreq-nvmem.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -28,8 +28,7 @@ + #include + #include + #include +- +-#define MSM_ID_SMEM 137 ++#include + + enum _msm_id { + MSM8996V3 = 0xF6ul, +@@ -143,17 +142,14 @@ static void get_krait_bin_format_b(struc + static enum _msm8996_version qcom_cpufreq_get_msm_id(void) + { + size_t len; +- u32 *msm_id; ++ struct socinfo *info; + enum _msm8996_version version; + +- msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len); +- if (IS_ERR(msm_id)) ++ info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, &len); ++ if (IS_ERR(info)) + return NUM_OF_MSM8996_VERSIONS; + +- /* The first 4 bytes are format, next to them is the actual msm-id */ +- msm_id++; +- +- switch ((enum _msm_id)*msm_id) { ++ switch (info->id) { + case MSM8996V3: + case APQ8096V3: + version = MSM8996_V3; diff --git a/target/linux/ipq807x/patches-6.1/0126-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch b/target/linux/ipq807x/patches-6.1/0126-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch new file mode 100644 index 00000000000..bdac41a0281 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0126-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch @@ -0,0 +1,46 @@ +From 132b2f15b8ae3f848b3e6f2962f409cfab0ca759 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 30 Dec 2022 23:33:47 +0100 +Subject: [PATCH] cpufreq: qcom-nvmem: use SoC ID-s from bindings + +SMEM SoC ID-s are now stored in DT bindings so lets use those instead of +defining them in the driver again. + +Signed-off-by: Robert Marko +--- + drivers/cpufreq/qcom-cpufreq-nvmem.c | 15 +++++---------- + 1 file changed, 5 insertions(+), 10 deletions(-) + +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -30,12 +30,7 @@ + #include + #include + +-enum _msm_id { +- MSM8996V3 = 0xF6ul, +- APQ8096V3 = 0x123ul, +- MSM8996SG = 0x131ul, +- APQ8096SG = 0x138ul, +-}; ++#include + + enum _msm8996_version { + MSM8996_V3, +@@ -150,12 +145,12 @@ static enum _msm8996_version qcom_cpufre + return NUM_OF_MSM8996_VERSIONS; + + switch (info->id) { +- case MSM8996V3: +- case APQ8096V3: ++ case QCOM_ID_MSM8996: ++ case QCOM_ID_APQ8096: + version = MSM8996_V3; + break; +- case MSM8996SG: +- case APQ8096SG: ++ case QCOM_ID_MSM8996SG: ++ case QCOM_ID_APQ8096SG: + version = MSM8996_SG; + break; + default: diff --git a/target/linux/ipq807x/patches-6.1/0127-cpufreq-qcom-nvmem-make-qcom_cpufreq_get_msm_id-retu.patch b/target/linux/ipq807x/patches-6.1/0127-cpufreq-qcom-nvmem-make-qcom_cpufreq_get_msm_id-retu.patch new file mode 100644 index 00000000000..0a33c5c66d9 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0127-cpufreq-qcom-nvmem-make-qcom_cpufreq_get_msm_id-retu.patch @@ -0,0 +1,106 @@ +From 85bf71b130ab0e939f53ec9cf1131d67d148bc9a Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 31 Dec 2022 12:45:31 +0100 +Subject: [PATCH] cpufreq: qcom-nvmem: make qcom_cpufreq_get_msm_id() return + the SoC ID + +Currently, qcom_cpufreq_get_msm_id() does not simply return the SoC ID +after getting it via SMEM call but instead uses an enum to encode the +matched SMEM ID to 2 variants of MSM8996 which are then used in +qcom_cpufreq_kryo_name_version() to set the supported version. + +This prevents qcom_cpufreq_get_msm_id() from being universal and its doing +more than its name suggests, so lets make it just return the SoC ID +directly which allows matching directly on the SoC ID and removes the need +for msm8996_version enum which simplifies the driver. +It also allows reusing the qcom_cpufreq_get_msm_id() for new SoC-s. + +Signed-off-by: Robert Marko +--- + drivers/cpufreq/qcom-cpufreq-nvmem.c | 44 ++++++++-------------------- + 1 file changed, 12 insertions(+), 32 deletions(-) + +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -32,12 +32,6 @@ + + #include + +-enum _msm8996_version { +- MSM8996_V3, +- MSM8996_SG, +- NUM_OF_MSM8996_VERSIONS, +-}; +- + struct qcom_cpufreq_drv; + + struct qcom_cpufreq_match_data { +@@ -134,30 +128,16 @@ static void get_krait_bin_format_b(struc + dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver); + } + +-static enum _msm8996_version qcom_cpufreq_get_msm_id(void) ++static int qcom_cpufreq_get_msm_id(void) + { + size_t len; + struct socinfo *info; +- enum _msm8996_version version; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, &len); + if (IS_ERR(info)) +- return NUM_OF_MSM8996_VERSIONS; +- +- switch (info->id) { +- case QCOM_ID_MSM8996: +- case QCOM_ID_APQ8096: +- version = MSM8996_V3; +- break; +- case QCOM_ID_MSM8996SG: +- case QCOM_ID_APQ8096SG: +- version = MSM8996_SG; +- break; +- default: +- version = NUM_OF_MSM8996_VERSIONS; +- } ++ return PTR_ERR(info); + +- return version; ++ return info->id; + } + + static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, +@@ -166,25 +146,25 @@ static int qcom_cpufreq_kryo_name_versio + struct qcom_cpufreq_drv *drv) + { + size_t len; ++ int msm_id; + u8 *speedbin; +- enum _msm8996_version msm8996_version; + *pvs_name = NULL; + +- msm8996_version = qcom_cpufreq_get_msm_id(); +- if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { +- dev_err(cpu_dev, "Not Snapdragon 820/821!"); +- return -ENODEV; +- } ++ msm_id = qcom_cpufreq_get_msm_id(); ++ if (msm_id < 0) ++ return msm_id; + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + +- switch (msm8996_version) { +- case MSM8996_V3: ++ switch (msm_id) { ++ case QCOM_ID_MSM8996: ++ case QCOM_ID_APQ8096: + drv->versions = 1 << (unsigned int)(*speedbin); + break; +- case MSM8996_SG: ++ case QCOM_ID_MSM8996SG: ++ case QCOM_ID_APQ8096SG: + drv->versions = 1 << ((unsigned int)(*speedbin) + 4); + break; + default: diff --git a/target/linux/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch b/target/linux/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch new file mode 100644 index 00000000000..d2653e52d67 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch @@ -0,0 +1,100 @@ +From 813f2b5ad002e691b92154037f154b4444eedd54 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 31 Dec 2022 13:03:41 +0100 +Subject: [PATCH] cpufreq: qcom-nvmem: add support for IPQ8074 + +IPQ8074 comes in 2 families: +* IPQ8070A/IPQ8071A (Acorn) up to 1.4GHz +* IPQ8072A/IPQ8074A/IPQ8076A/IPQ8078A (Hawkeye) up to 2.2GHz + +So, in order to be able to share one OPP table lets add support for IPQ8074 +family based of SMEM SoC ID-s as speedbin fuse is always 0 on IPQ8074. + +IPQ8074 compatible is blacklisted from DT platdev as the cpufreq device +will get created by NVMEM CPUFreq driver. + +Signed-off-by: Robert Marko +--- + drivers/cpufreq/cpufreq-dt-platdev.c | 1 + + drivers/cpufreq/qcom-cpufreq-nvmem.c | 39 ++++++++++++++++++++++++++++ + 2 files changed, 40 insertions(+) + +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -164,6 +164,7 @@ static const struct of_device_id blockli + { .compatible = "ti,omap3", }, + + { .compatible = "qcom,ipq8064", }, ++ { .compatible = "qcom,ipq8074", }, + { .compatible = "qcom,apq8064", }, + { .compatible = "qcom,msm8974", }, + { .compatible = "qcom,msm8960", }, +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -32,6 +32,9 @@ + + #include + ++#define IPQ8074_HAWKEYE_VERSION BIT(0) ++#define IPQ8074_ACORN_VERSION BIT(1) ++ + struct qcom_cpufreq_drv; + + struct qcom_cpufreq_match_data { +@@ -216,6 +219,37 @@ len_error: + return ret; + } + ++static int qcom_cpufreq_ipq8074_name_version(struct device *cpu_dev, ++ struct nvmem_cell *speedbin_nvmem, ++ char **pvs_name, ++ struct qcom_cpufreq_drv *drv) ++{ ++ int msm_id; ++ *pvs_name = NULL; ++ ++ msm_id = qcom_cpufreq_get_msm_id(); ++ if (msm_id < 0) ++ return msm_id; ++ ++ switch (msm_id) { ++ case QCOM_ID_IPQ8070A: ++ case QCOM_ID_IPQ8071A: ++ drv->versions = IPQ8074_ACORN_VERSION; ++ break; ++ case QCOM_ID_IPQ8072A: ++ case QCOM_ID_IPQ8074A: ++ case QCOM_ID_IPQ8076A: ++ case QCOM_ID_IPQ8078A: ++ drv->versions = IPQ8074_HAWKEYE_VERSION; ++ break; ++ default: ++ BUG(); ++ break; ++ } ++ ++ return 0; ++} ++ + static const struct qcom_cpufreq_match_data match_data_kryo = { + .get_version = qcom_cpufreq_kryo_name_version, + }; +@@ -230,6 +264,10 @@ static const struct qcom_cpufreq_match_d + .genpd_names = qcs404_genpd_names, + }; + ++static const struct qcom_cpufreq_match_data match_data_ipq8074 = { ++ .get_version = qcom_cpufreq_ipq8074_name_version, ++}; ++ + static int qcom_cpufreq_probe(struct platform_device *pdev) + { + struct qcom_cpufreq_drv *drv; +@@ -375,6 +413,7 @@ static const struct of_device_id qcom_cp + { .compatible = "qcom,msm8996", .data = &match_data_kryo }, + { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, + { .compatible = "qcom,ipq8064", .data = &match_data_krait }, ++ { .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 }, + { .compatible = "qcom,apq8064", .data = &match_data_krait }, + { .compatible = "qcom,msm8974", .data = &match_data_krait }, + { .compatible = "qcom,msm8960", .data = &match_data_krait }, diff --git a/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch b/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch new file mode 100644 index 00000000000..3a6f4e9c874 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch @@ -0,0 +1,128 @@ +From 04d2fc6a551bbd972a6428059b45ce79cb9de9d7 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 6 May 2022 22:38:24 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add QFPROM fuses + +Add the QFPROM node and CPR fuses. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 107 ++++++++++++++++++++++++++ + 1 file changed, 107 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -340,6 +340,113 @@ + status = "disabled"; + }; + ++ qfprom: efuse@a4000 { ++ compatible = "qcom,ipq8074-qfprom", "qcom,qfprom"; ++ reg = <0x000a4000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ cpr_efuse_speedbin: speedbin@125 { ++ reg = <0x125 0x1>; ++ bits = <0 3>; ++ }; ++ ++ cpr_efuse_boost_cfg: boost_cfg@125 { ++ reg = <0x125 0x1>; ++ bits = <3 3>; ++ }; ++ ++ cpr_efuse_misc_volt_adj: misc_volt_adj@125 { ++ reg = <0x125 0x1>; ++ bits = <3 3>; ++ }; ++ ++ cpr_efuse_boost_volt: boost_volt@126 { ++ reg = <0x126 0x1>; ++ bits = <6 1>; ++ }; ++ ++ cpr_efuse_revision: revision@23e { ++ reg = <0x23e 0x1>; ++ bits = <5 3>; ++ }; ++ ++ cpr_efuse_ro_sel0: rosel0@249 { ++ reg = <0x249 0x1>; ++ bits = <0 4>; ++ }; ++ ++ cpr_efuse_ro_sel1: rosel1@248 { ++ reg = <0x248 0x1>; ++ bits = <4 4>; ++ }; ++ ++ cpr_efuse_ro_sel2: rosel2@248 { ++ reg = <0x248 0x2>; ++ bits = <0 4>; ++ }; ++ ++ cpr_efuse_ro_sel3: rosel3@249 { ++ reg = <0x249 0x1>; ++ bits = <4 4>; ++ }; ++ ++ cpr_efuse_init_voltage0: ivoltage0@23a { ++ reg = <0x23a 0x1>; ++ bits = <2 6>; ++ }; ++ ++ cpr_efuse_init_voltage1: ivoltage1@239 { ++ reg = <0x239 0x2>; ++ bits = <4 6>; ++ }; ++ ++ cpr_efuse_init_voltage2: ivoltage2@238 { ++ reg = <0x238 0x2>; ++ bits = <6 6>; ++ }; ++ ++ cpr_efuse_init_voltage3: ivoltage3@238 { ++ reg = <0x238 0x1>; ++ bits = <0 6>; ++ }; ++ ++ cpr_efuse_quot0: quot0@244 { ++ reg = <0x244 0x2>; ++ bits = <0 12>; ++ }; ++ ++ cpr_efuse_quot1: quot1@242 { ++ reg = <0x242 0x2>; ++ bits = <4 12>; ++ }; ++ ++ cpr_efuse_quot2: quot2@241 { ++ reg = <0x241 0x2>; ++ bits = <0 12>; ++ }; ++ ++ cpr_efuse_quot3: quot3@245 { ++ reg = <0x245 0x2>; ++ bits = <4 12>; ++ }; ++ ++ cpr_efuse_quot0_offset: quot0_offset@23d { ++ reg = <0x23d 0x2>; ++ bits = <6 7>; ++ }; ++ ++ cpr_efuse_quot1_offset: quot1_offset@23c { ++ reg = <0x23c 0x2>; ++ bits = <7 7>; ++ }; ++ ++ cpr_efuse_quot2_offset: quot2_offset@23c { ++ reg = <0x23c 0x1>; ++ bits = <0 7>; ++ }; ++ }; ++ + prng: rng@e3000 { + compatible = "qcom,prng-ee"; + reg = <0x000e3000 0x1000>; diff --git a/target/linux/ipq807x/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch b/target/linux/ipq807x/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch new file mode 100644 index 00000000000..9c1e7b9d29c --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch @@ -0,0 +1,102 @@ +From a20c4e8738a00087aa5d53fe5148ed484e23d229 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 31 Dec 2022 13:56:26 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add CPU OPP table + +Now that there is NVMEM CPUFreq support for IPQ8074, we can add the OPP +table for SoC. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 52 +++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -42,6 +42,7 @@ + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; + #cooling-cells = <2>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + CPU1: cpu@1 { +@@ -53,6 +54,7 @@ + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; + #cooling-cells = <2>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + CPU2: cpu@2 { +@@ -64,6 +66,7 @@ + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; + #cooling-cells = <2>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + CPU3: cpu@3 { +@@ -75,6 +78,7 @@ + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; + #cooling-cells = <2>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + L2_0: l2-cache { +@@ -83,6 +87,54 @@ + }; + }; + ++ cpu_opp_table: opp-table { ++ compatible = "operating-points-v2-kryo-cpu"; ++ nvmem-cells = <&cpr_efuse_speedbin>; ++ opp-shared; ++ ++ opp-1017600000 { ++ opp-hz = /bits/ 64 <1017600000>; ++ opp-microvolt = <1>; ++ opp-supported-hw = <0xf>; ++ clock-latency-ns = <200000>; ++ }; ++ ++ opp-1382400000 { ++ opp-hz = /bits/ 64 <1382400000>; ++ opp-microvolt = <2>; ++ opp-supported-hw = <0xf>; ++ clock-latency-ns = <200000>; ++ }; ++ ++ opp-1651200000 { ++ opp-hz = /bits/ 64 <1651200000>; ++ opp-microvolt = <3>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <200000>; ++ }; ++ ++ opp-1843200000 { ++ opp-hz = /bits/ 64 <1843200000>; ++ opp-microvolt = <4>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <200000>; ++ }; ++ ++ opp-1920000000 { ++ opp-hz = /bits/ 64 <1920000000>; ++ opp-microvolt = <5>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <200000>; ++ }; ++ ++ opp-2208000000 { ++ opp-hz = /bits/ 64 <2208000000>; ++ opp-microvolt = <6>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <200000>; ++ }; ++ }; ++ + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = ; diff --git a/target/linux/ipq807x/patches-6.1/0131-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch b/target/linux/ipq807x/patches-6.1/0131-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch new file mode 100644 index 00000000000..72b38270c46 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0131-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch @@ -0,0 +1,77 @@ +From b8295c6eb276b60f7b75c53a9703ca8fee01eba2 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 26 May 2023 13:09:17 +0200 +Subject: [PATCH] soc: qcom: mdt_loader: Fix unconditional call to + scm_pas_mem_setup + +Commit ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS +mem_setup") dropped the relocate check and made pas_mem_setup run +unconditionally. The code was later moved with commit f4e526ff7e38 +("soc: qcom: mdt_loader: Extract PAS operations") to +qcom_mdt_pas_init() effectively losing track of what was actually +done. + +The assumption that PAS mem_setup can be done anytime was effectively +wrong, with no good reason and this caused regression on some SoC +that use remoteproc to bringup ath11k. One example is IPQ8074 SoC that +effectively broke resulting in remoteproc silently die and ath11k not +working. + +On this SoC FW relocate is not enabled and PAS mem_setup was correctly +skipped in previous kernel version resulting in correct bringup and +function of remoteproc and ath11k. + +To fix the regression, reintroduce the relocate check in +qcom_mdt_pas_init() and correctly skip PAS mem_setup where relocate is +not enabled. + +Fixes: ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS mem_setup") +Reported-by: Robert Marko +Tested-by: Robert Marko +Co-developed-by: Robert Marko +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Cc: stable@vger.kernel.org +--- + drivers/soc/qcom/mdt_loader.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/drivers/soc/qcom/mdt_loader.c ++++ b/drivers/soc/qcom/mdt_loader.c +@@ -210,6 +210,7 @@ int qcom_mdt_pas_init(struct device *dev + const struct elf32_hdr *ehdr; + phys_addr_t min_addr = PHYS_ADDR_MAX; + phys_addr_t max_addr = 0; ++ bool relocate = false; + size_t metadata_len; + void *metadata; + int ret; +@@ -224,6 +225,9 @@ int qcom_mdt_pas_init(struct device *dev + if (!mdt_phdr_valid(phdr)) + continue; + ++ if (phdr->p_flags & QCOM_MDT_RELOCATABLE) ++ relocate = true; ++ + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + +@@ -246,11 +250,13 @@ int qcom_mdt_pas_init(struct device *dev + goto out; + } + +- ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); +- if (ret) { +- /* Unable to set up relocation */ +- dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); +- goto out; ++ if (relocate) { ++ ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); ++ if (ret) { ++ /* Unable to set up relocation */ ++ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); ++ goto out; ++ } + } + + out: diff --git a/target/linux/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch b/target/linux/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch new file mode 100644 index 00000000000..8c2e59eeb48 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch @@ -0,0 +1,36 @@ +From f5aaf6669bd4f1f0218dd7fd5dc90941267ad860 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 30 May 2023 23:26:30 +0200 +Subject: [PATCH] mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M + +It seems that Micron MTFC4GACAJCN-1M despite advertising TRIM support does +not work when the core is trying to use REQ_OP_WRITE_ZEROES. + +We are seeing the following errors in OpenWrt under 6.1 on Qnap Qhora 301W +that we did not previously have and tracked it down to REQ_OP_WRITE_ZEROES: +[ 18.085950] I/O error, dev loop0, sector 596 op 0x9:(WRITE_ZEROES) flags 0x800 phys_seg 0 prio class 2 + +Disabling TRIM makes the error go away, so lets add a quirk for this eMMC +to disable TRIM. + +Signed-off-by: Robert Marko +--- + drivers/mmc/core/quirks.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -101,6 +101,13 @@ static const struct mmc_fixup __maybe_un + MMC_QUIRK_TRIM_BROKEN), + + /* ++ * Micron MTFC4GACAJCN-1M advertises TRIM but it does not seems to ++ * support being used to offload WRITE_ZEROES. ++ */ ++ MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc, ++ MMC_QUIRK_TRIM_BROKEN), ++ ++ /* + * Some SD cards reports discard support while they don't + */ + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch b/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch new file mode 100644 index 00000000000..ac7af52a74e --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch @@ -0,0 +1,38 @@ +From 26c97b6fb7d291f55e0e4a410d266d1355118ed9 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 31 May 2023 20:21:26 +0200 +Subject: [PATCH] mmc: core: disable TRIM on Kingston EMMC04G-M627 + +It seems that Kingston EMMC04G-M627 despite advertising TRIM support does +not work when the core is trying to use REQ_OP_WRITE_ZEROES. + +We are seeing I/O errors in OpenWrt under 6.1 on Zyxel NBG7815 that we did +not previously have and tracked it down to REQ_OP_WRITE_ZEROES. + +Trying to use fstrim seems to also throw errors like: +[93010.835112] I/O error, dev loop0, sector 16902 op 0x3:(DISCARD) flags 0x800 phys_seg 1 prio class 2 + +Disabling TRIM makes the error go away, so lets add a quirk for this eMMC +to disable TRIM. + +Signed-off-by: Robert Marko +--- + drivers/mmc/core/quirks.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -108,6 +108,13 @@ static const struct mmc_fixup __maybe_un + MMC_QUIRK_TRIM_BROKEN), + + /* ++ * Kingston EMMC04G-M627 advertises TRIM but it does not seems to ++ * support being used to offload WRITE_ZEROES. ++ */ ++ MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, ++ MMC_QUIRK_TRIM_BROKEN), ++ ++ /* + * Some SD cards reports discard support while they don't + */ + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch b/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch new file mode 100644 index 00000000000..2e5c72b7d1f --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch @@ -0,0 +1,1047 @@ +From 6c98adf98236b8644b8f5e1aa7af9f1a88ea2766 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 11 Apr 2022 14:38:08 +0200 +Subject: [PATCH] power: Add Qualcomm APM + +Add Qualcomm APM driver, which allows scaling cache and memory fabrics. + +Signed-off-by: Robert Marko +--- + drivers/power/Kconfig | 1 + + drivers/power/Makefile | 1 + + drivers/power/qcom/Kconfig | 7 + + drivers/power/qcom/Makefile | 1 + + drivers/power/qcom/apm.c | 944 +++++++++++++++++++++++++++++++++ + include/linux/power/qcom/apm.h | 48 ++ + 6 files changed, 1002 insertions(+) + create mode 100644 drivers/power/qcom/Kconfig + create mode 100644 drivers/power/qcom/Makefile + create mode 100644 drivers/power/qcom/apm.c + create mode 100644 include/linux/power/qcom/apm.h + +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + source "drivers/power/reset/Kconfig" + source "drivers/power/supply/Kconfig" ++source "drivers/power/qcom/Kconfig" +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_POWER_RESET) += reset/ + obj-$(CONFIG_POWER_SUPPLY) += supply/ ++obj-$(CONFIG_QCOM_APM) += qcom/ +--- /dev/null ++++ b/drivers/power/qcom/Kconfig +@@ -0,0 +1,7 @@ ++config QCOM_APM ++ bool "Qualcomm Technologies Inc platform specific APM driver" ++ help ++ Platform specific driver to manage the power source of ++ memory arrays. Interfaces with regulator drivers to ensure ++ SRAM Vmin requirements are met across different performance ++ levels. +--- /dev/null ++++ b/drivers/power/qcom/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_QCOM_APM) += apm.o +--- /dev/null ++++ b/drivers/power/qcom/apm.c +@@ -0,0 +1,944 @@ ++/* ++ * Copyright (c) 2015-2016, 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. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * VDD_APCC ++ * ============================================================= ++ * | VDD_MX | | ++ * | ==========================|============= | ++ * ___|___ ___|___ ___|___ ___|___ ___|___ ___|___ ++ * | | | | | | | | | | | | ++ * | APCC | | MX HS | | MX HS | | APCC | | MX HS | | APCC | ++ * | HS | | | | | | HS | | | | HS | ++ * |_______| |_______| |_______| |_______| |_______| |_______| ++ * |_________| |_________| |__________| ++ * | | | ++ * ______|_____ ______|_____ _______|_____ ++ * | | | | | | ++ * | | | | | | ++ * | CPU MEM | | L2 MEM | | L3 MEM | ++ * | Arrays | | Arrays | | Arrays | ++ * | | | | | | ++ * |____________| |____________| |_____________| ++ * ++ */ ++ ++/* Register value definitions */ ++#define APCS_GFMUXA_SEL_VAL 0x13 ++#define APCS_GFMUXA_DESEL_VAL 0x03 ++#define MSM_APM_MX_MODE_VAL 0x00 ++#define MSM_APM_APCC_MODE_VAL 0x10 ++#define MSM_APM_MX_DONE_VAL 0x00 ++#define MSM_APM_APCC_DONE_VAL 0x03 ++#define MSM_APM_OVERRIDE_SEL_VAL 0xb0 ++#define MSM_APM_SEC_CLK_SEL_VAL 0x30 ++#define SPM_EVENT_SET_VAL 0x01 ++#define SPM_EVENT_CLEAR_VAL 0x00 ++ ++/* Register bit mask definitions */ ++#define MSM_APM_CTL_STS_MASK 0x0f ++ ++/* Register offset definitions */ ++#define APCC_APM_MODE 0x00000098 ++#define APCC_APM_CTL_STS 0x000000a8 ++#define APCS_SPARE 0x00000068 ++#define APCS_VERSION 0x00000fd0 ++ ++#define HMSS_VERSION_1P2 0x10020000 ++ ++#define MSM_APM_SWITCH_TIMEOUT_US 10 ++#define SPM_WAKEUP_DELAY_US 2 ++#define SPM_EVENT_NUM 6 ++ ++#define MSM_APM_DRIVER_NAME "qcom,msm-apm" ++ ++enum { ++ MSM8996_ID, ++ MSM8953_ID, ++ IPQ807x_ID, ++}; ++ ++struct msm_apm_ctrl_dev { ++ struct list_head list; ++ struct device *dev; ++ enum msm_apm_supply supply; ++ spinlock_t lock; ++ void __iomem *reg_base; ++ void __iomem *apcs_csr_base; ++ void __iomem **apcs_spm_events_addr; ++ void __iomem *apc0_pll_ctl_addr; ++ void __iomem *apc1_pll_ctl_addr; ++ u32 version; ++ struct dentry *debugfs; ++ u32 msm_id; ++}; ++ ++#if defined(CONFIG_DEBUG_FS) ++static struct dentry *apm_debugfs_base; ++#endif ++ ++static DEFINE_MUTEX(apm_ctrl_list_mutex); ++static LIST_HEAD(apm_ctrl_list); ++ ++/* ++ * Get the resources associated with the APM controller from device tree ++ * and remap all I/O addresses that are relevant to this HW revision. ++ */ ++static int msm_apm_ctrl_devm_ioremap(struct platform_device *pdev, ++ struct msm_apm_ctrl_dev *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ static const char *res_name[SPM_EVENT_NUM] = { ++ "apc0-l2-spm", ++ "apc1-l2-spm", ++ "apc0-cpu0-spm", ++ "apc0-cpu1-spm", ++ "apc1-cpu0-spm", ++ "apc1-cpu1-spm" ++ }; ++ int i, ret = 0; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); ++ if (!res) { ++ dev_err(dev, "Missing PM APCC Global register physical address"); ++ return -EINVAL; ++ } ++ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->reg_base) { ++ dev_err(dev, "Failed to map PM APCC Global registers\n"); ++ return -ENOMEM; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs-csr"); ++ if (!res) { ++ dev_err(dev, "Missing APCS CSR physical base address"); ++ return -EINVAL; ++ } ++ ctrl->apcs_csr_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->apcs_csr_base) { ++ dev_err(dev, "Failed to map APCS CSR registers\n"); ++ return -ENOMEM; ++ } ++ ++ ctrl->version = readl_relaxed(ctrl->apcs_csr_base + APCS_VERSION); ++ ++ if (ctrl->version >= HMSS_VERSION_1P2) ++ return ret; ++ ++ ctrl->apcs_spm_events_addr = devm_kzalloc(&pdev->dev, ++ SPM_EVENT_NUM ++ * sizeof(void __iomem *), ++ GFP_KERNEL); ++ if (!ctrl->apcs_spm_events_addr) { ++ dev_err(dev, "Failed to allocate memory for APCS SPM event registers\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < SPM_EVENT_NUM; i++) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ res_name[i]); ++ if (!res) { ++ dev_err(dev, "Missing address for %s\n", res_name[i]); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apcs_spm_events_addr[i] = devm_ioremap(dev, res->start, ++ resource_size(res)); ++ if (!ctrl->apcs_spm_events_addr[i]) { ++ dev_err(dev, "Failed to map %s\n", res_name[i]); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ dev_dbg(dev, "%s event phys: %pa virt:0x%p\n", res_name[i], ++ &res->start, ctrl->apcs_spm_events_addr[i]); ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "apc0-pll-ctl"); ++ if (!res) { ++ dev_err(dev, "Missing APC0 PLL CTL physical address\n"); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apc0_pll_ctl_addr = devm_ioremap(dev, ++ res->start, ++ resource_size(res)); ++ if (!ctrl->apc0_pll_ctl_addr) { ++ dev_err(dev, "Failed to map APC0 PLL CTL register\n"); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "apc1-pll-ctl"); ++ if (!res) { ++ dev_err(dev, "Missing APC1 PLL CTL physical address\n"); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apc1_pll_ctl_addr = devm_ioremap(dev, ++ res->start, ++ resource_size(res)); ++ if (!ctrl->apc1_pll_ctl_addr) { ++ dev_err(dev, "Failed to map APC1 PLL CTL register\n"); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ return ret; ++ ++free_events: ++ devm_kfree(dev, ctrl->apcs_spm_events_addr); ++ return ret; ++} ++ ++/* 8953 register offset definition */ ++#define MSM8953_APM_DLY_CNTR 0x2ac ++ ++/* Register field shift definitions */ ++#define APM_CTL_SEL_SWITCH_DLY_SHIFT 0 ++#define APM_CTL_RESUME_CLK_DLY_SHIFT 8 ++#define APM_CTL_HALT_CLK_DLY_SHIFT 16 ++#define APM_CTL_POST_HALT_DLY_SHIFT 24 ++ ++/* Register field mask definitions */ ++#define APM_CTL_SEL_SWITCH_DLY_MASK GENMASK(7, 0) ++#define APM_CTL_RESUME_CLK_DLY_MASK GENMASK(15, 8) ++#define APM_CTL_HALT_CLK_DLY_MASK GENMASK(23, 16) ++#define APM_CTL_POST_HALT_DLY_MASK GENMASK(31, 24) ++ ++/* ++ * Get the resources associated with the msm8953 APM controller from ++ * device tree, remap all I/O addresses, and program the initial ++ * register configuration required for the 8953 APM controller device. ++ */ ++static int msm8953_apm_ctrl_init(struct platform_device *pdev, ++ struct msm_apm_ctrl_dev *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ u32 delay_counter, val = 0, regval = 0; ++ int rc = 0; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); ++ if (!res) { ++ dev_err(dev, "Missing PM APCC Global register physical address\n"); ++ return -ENODEV; ++ } ++ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->reg_base) { ++ dev_err(dev, "Failed to map PM APCC Global registers\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Initial APM register configuration required before starting ++ * APM HW controller. ++ */ ++ regval = readl_relaxed(ctrl->reg_base + MSM8953_APM_DLY_CNTR); ++ val = regval; ++ ++ if (of_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-post-halt-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-post-halt-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_POST_HALT_DLY_MASK; ++ val |= (delay_counter << APM_CTL_POST_HALT_DLY_SHIFT) ++ & APM_CTL_POST_HALT_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-halt-clk-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-halt-clk-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_HALT_CLK_DLY_MASK; ++ val |= (delay_counter << APM_CTL_HALT_CLK_DLY_SHIFT) ++ & APM_CTL_HALT_CLK_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-resume-clk-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-resume-clk-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_RESUME_CLK_DLY_MASK; ++ val |= (delay_counter << APM_CTL_RESUME_CLK_DLY_SHIFT) ++ & APM_CTL_RESUME_CLK_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-sel-switch-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-sel-switch-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_SEL_SWITCH_DLY_MASK; ++ val |= (delay_counter << APM_CTL_SEL_SWITCH_DLY_SHIFT) ++ & APM_CTL_SEL_SWITCH_DLY_MASK; ++ } ++ ++ if (val != regval) { ++ writel_relaxed(val, ctrl->reg_base + MSM8953_APM_DLY_CNTR); ++ /* make sure write completes before return */ ++ mb(); ++ } ++ ++ return rc; ++} ++ ++static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Clear SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_CLEAR_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ udelay(SPM_WAKEUP_DELAY_US); ++ ++ /* Switch APC/CBF to GPLL0 clock */ ++ writel_relaxed(APCS_GFMUXA_SEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Ensure writes complete before proceeding */ ++ mb(); ++ } ++ ++ /* Switch arrays to MX supply and wait for its completion */ ++ writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base + ++ APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); ++ if ((regval & MSM_APM_CTL_STS_MASK) == ++ MSM_APM_MX_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Switch APC/CBF clocks to original source */ ++ writel_relaxed(APCS_GFMUXA_DESEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Complete clock source switch before SPM event sequence */ ++ mb(); ++ ++ /* Set SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_SET_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ } ++ ++ if (!ret) { ++ ctrl_dev->supply = MSM_APM_SUPPLY_MX; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Clear SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_CLEAR_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ udelay(SPM_WAKEUP_DELAY_US); ++ ++ /* Switch APC/CBF to GPLL0 clock */ ++ writel_relaxed(APCS_GFMUXA_SEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Ensure previous writes complete before proceeding */ ++ mb(); ++ } ++ ++ /* Switch arrays to APCC supply and wait for its completion */ ++ writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base + ++ APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); ++ if ((regval & MSM_APM_CTL_STS_MASK) == ++ MSM_APM_APCC_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Set SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_SET_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ /* Complete SPM event sequence before clock source switch */ ++ mb(); ++ ++ /* Switch APC/CBF clocks to original source */ ++ writel_relaxed(APCS_GFMUXA_DESEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ } ++ ++ if (!ret) { ++ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++/* 8953 register value definitions */ ++#define MSM8953_APM_MX_MODE_VAL 0x00 ++#define MSM8953_APM_APCC_MODE_VAL 0x02 ++#define MSM8953_APM_MX_DONE_VAL 0x00 ++#define MSM8953_APM_APCC_DONE_VAL 0x03 ++ ++/* 8953 register offset definitions */ ++#define MSM8953_APCC_APM_MODE 0x000002a8 ++#define MSM8953_APCC_APM_CTL_STS 0x000002b0 ++ ++/* 8953 constants */ ++#define MSM8953_APM_SWITCH_TIMEOUT_US 500 ++ ++/* Register bit mask definitions */ ++#define MSM8953_APM_CTL_STS_MASK 0x1f ++ ++static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Switch arrays to MX supply and wait for its completion */ ++ writel_relaxed(MSM8953_APM_MX_MODE_VAL, ctrl_dev->reg_base + ++ MSM8953_APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + ++ MSM8953_APCC_APM_CTL_STS); ++ if ((regval & MSM8953_APM_CTL_STS_MASK) == ++ MSM8953_APM_MX_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } else { ++ ctrl_dev->supply = MSM_APM_SUPPLY_MX; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Switch arrays to APCC supply and wait for its completion */ ++ writel_relaxed(MSM8953_APM_APCC_MODE_VAL, ctrl_dev->reg_base + ++ MSM8953_APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + ++ MSM8953_APCC_APM_CTL_STS); ++ if ((regval & MSM8953_APM_CTL_STS_MASK) == ++ MSM8953_APM_APCC_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } else { ++ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int ret = 0; ++ ++ switch (ctrl_dev->msm_id) { ++ case MSM8996_ID: ++ ret = msm8996_apm_switch_to_mx(ctrl_dev); ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_switch_to_mx(ctrl_dev); ++ break; ++ } ++ ++ return ret; ++} ++ ++static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int ret = 0; ++ ++ switch (ctrl_dev->msm_id) { ++ case MSM8996_ID: ++ ret = msm8996_apm_switch_to_apcc(ctrl_dev); ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_switch_to_apcc(ctrl_dev); ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * msm_apm_get_supply() - Returns the supply that is currently ++ * powering the memory arrays ++ * @ctrl_dev: Pointer to an MSM APM controller device ++ * ++ * Returns the supply currently selected by the APM. ++ */ ++int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ return ctrl_dev->supply; ++} ++EXPORT_SYMBOL(msm_apm_get_supply); ++ ++/** ++ * msm_apm_set_supply() - Perform the necessary steps to switch the voltage ++ * source of the memory arrays to a given supply ++ * @ctrl_dev: Pointer to an MSM APM controller device ++ * @supply: Power rail to use as supply for the memory ++ * arrays ++ * ++ * Returns 0 on success, -ETIMEDOUT on APM switch timeout, or -EPERM if ++ * the supply is not supported. ++ */ ++int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply) ++{ ++ int ret; ++ ++ switch (supply) { ++ case MSM_APM_SUPPLY_APCC: ++ ret = msm_apm_switch_to_apcc(ctrl_dev); ++ break; ++ case MSM_APM_SUPPLY_MX: ++ ret = msm_apm_switch_to_mx(ctrl_dev); ++ break; ++ default: ++ ret = -EPERM; ++ break; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(msm_apm_set_supply); ++ ++/** ++ * msm_apm_ctrl_dev_get() - get a handle to the MSM APM controller linked to ++ * the device in device tree ++ * @dev: Pointer to the device ++ * ++ * The device must specify "qcom,apm-ctrl" property in its device tree ++ * node which points to an MSM APM controller device node. ++ * ++ * Returns an MSM APM controller handle if successful or ERR_PTR on any error. ++ * If the APM controller device hasn't probed yet, ERR_PTR(-EPROBE_DEFER) is ++ * returned. ++ */ ++struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev = NULL; ++ struct msm_apm_ctrl_dev *dev_found = ERR_PTR(-EPROBE_DEFER); ++ struct device_node *ctrl_node; ++ ++ if (!dev || !dev->of_node) { ++ pr_err("Invalid device node\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ ctrl_node = of_parse_phandle(dev->of_node, "qcom,apm-ctrl", 0); ++ if (!ctrl_node) { ++ pr_err("Could not find qcom,apm-ctrl property in %s\n", ++ dev->of_node->full_name); ++ return ERR_PTR(-ENXIO); ++ } ++ ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_for_each_entry(ctrl_dev, &apm_ctrl_list, list) { ++ if (ctrl_dev->dev && ctrl_dev->dev->of_node == ctrl_node) { ++ dev_found = ctrl_dev; ++ break; ++ } ++ } ++ mutex_unlock(&apm_ctrl_list_mutex); ++ ++ of_node_put(ctrl_node); ++ return dev_found; ++} ++EXPORT_SYMBOL(msm_apm_ctrl_dev_get); ++ ++#if defined(CONFIG_DEBUG_FS) ++ ++static int apm_supply_dbg_open(struct inode *inode, struct file *filep) ++{ ++ filep->private_data = inode->i_private; ++ ++ return 0; ++} ++ ++static ssize_t apm_supply_dbg_read(struct file *filep, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev = filep->private_data; ++ char buf[10]; ++ int len; ++ ++ if (!ctrl_dev) { ++ pr_err("invalid apm ctrl handle\n"); ++ return -ENODEV; ++ } ++ ++ if (ctrl_dev->supply == MSM_APM_SUPPLY_APCC) ++ len = snprintf(buf, sizeof(buf), "APCC\n"); ++ else if (ctrl_dev->supply == MSM_APM_SUPPLY_MX) ++ len = snprintf(buf, sizeof(buf), "MX\n"); ++ else ++ len = snprintf(buf, sizeof(buf), "ERR\n"); ++ ++ return simple_read_from_buffer(ubuf, count, ppos, buf, len); ++} ++ ++static const struct file_operations apm_supply_fops = { ++ .open = apm_supply_dbg_open, ++ .read = apm_supply_dbg_read, ++}; ++ ++static void apm_debugfs_base_init(void) ++{ ++ apm_debugfs_base = debugfs_create_dir("msm-apm", NULL); ++ ++ if (IS_ERR_OR_NULL(apm_debugfs_base)) ++ pr_err("msm-apm debugfs base directory creation failed\n"); ++} ++ ++static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ struct dentry *temp; ++ ++ if (IS_ERR_OR_NULL(apm_debugfs_base)) { ++ pr_err("Base directory missing, cannot create apm debugfs nodes\n"); ++ return; ++ } ++ ++ ctrl_dev->debugfs = debugfs_create_dir(dev_name(ctrl_dev->dev), ++ apm_debugfs_base); ++ if (IS_ERR_OR_NULL(ctrl_dev->debugfs)) { ++ pr_err("%s debugfs directory creation failed\n", ++ dev_name(ctrl_dev->dev)); ++ return; ++ } ++ ++ temp = debugfs_create_file("supply", S_IRUGO, ctrl_dev->debugfs, ++ ctrl_dev, &apm_supply_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ pr_err("supply mode creation failed\n"); ++ return; ++ } ++} ++ ++static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ if (!IS_ERR_OR_NULL(ctrl_dev->debugfs)) ++ debugfs_remove_recursive(ctrl_dev->debugfs); ++} ++ ++static void apm_debugfs_base_remove(void) ++{ ++ debugfs_remove_recursive(apm_debugfs_base); ++} ++#else ++ ++static void apm_debugfs_base_init(void) ++{} ++ ++static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) ++{} ++ ++static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) ++{} ++ ++static void apm_debugfs_base_remove(void) ++{} ++ ++#endif ++ ++static struct of_device_id msm_apm_match_table[] = { ++ { ++ .compatible = "qcom,msm-apm", ++ .data = (void *)(uintptr_t)MSM8996_ID, ++ }, ++ { ++ .compatible = "qcom,msm8953-apm", ++ .data = (void *)(uintptr_t)MSM8953_ID, ++ }, ++ { ++ .compatible = "qcom,ipq807x-apm", ++ .data = (void *)(uintptr_t)IPQ807x_ID, ++ }, ++ {} ++}; ++ ++static int msm_apm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct msm_apm_ctrl_dev *ctrl; ++ const struct of_device_id *match; ++ int ret = 0; ++ ++ dev_dbg(dev, "probing MSM Array Power Mux driver\n"); ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -ENODEV; ++ } ++ ++ match = of_match_device(msm_apm_match_table, dev); ++ if (!match) ++ return -ENODEV; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) { ++ dev_err(dev, "MSM APM controller memory allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ INIT_LIST_HEAD(&ctrl->list); ++ spin_lock_init(&ctrl->lock); ++ ctrl->dev = dev; ++ ctrl->msm_id = (uintptr_t)match->data; ++ platform_set_drvdata(pdev, ctrl); ++ ++ switch (ctrl->msm_id) { ++ case MSM8996_ID: ++ ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl); ++ if (ret) { ++ dev_err(dev, "Failed to add APM controller device\n"); ++ return ret; ++ } ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_ctrl_init(pdev, ctrl); ++ if (ret) { ++ dev_err(dev, "Failed to initialize APM controller device: ret=%d\n", ++ ret); ++ return ret; ++ } ++ break; ++ default: ++ dev_err(dev, "unable to add APM controller device for msm_id:%d\n", ++ ctrl->msm_id); ++ return -ENODEV; ++ } ++ ++ apm_debugfs_init(ctrl); ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_add_tail(&ctrl->list, &apm_ctrl_list); ++ mutex_unlock(&apm_ctrl_list_mutex); ++ ++ dev_dbg(dev, "MSM Array Power Mux driver probe successful"); ++ ++ return ret; ++} ++ ++static int msm_apm_remove(struct platform_device *pdev) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev; ++ ++ ctrl_dev = platform_get_drvdata(pdev); ++ if (ctrl_dev) { ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_del(&ctrl_dev->list); ++ mutex_unlock(&apm_ctrl_list_mutex); ++ apm_debugfs_deinit(ctrl_dev); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver msm_apm_driver = { ++ .driver = { ++ .name = MSM_APM_DRIVER_NAME, ++ .of_match_table = msm_apm_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = msm_apm_probe, ++ .remove = msm_apm_remove, ++}; ++ ++static int __init msm_apm_init(void) ++{ ++ apm_debugfs_base_init(); ++ return platform_driver_register(&msm_apm_driver); ++} ++ ++static void __exit msm_apm_exit(void) ++{ ++ platform_driver_unregister(&msm_apm_driver); ++ apm_debugfs_base_remove(); ++} ++ ++arch_initcall(msm_apm_init); ++module_exit(msm_apm_exit); ++ ++MODULE_DESCRIPTION("MSM Array Power Mux driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/power/qcom/apm.h +@@ -0,0 +1,48 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef __LINUX_POWER_QCOM_APM_H__ ++#define __LINUX_POWER_QCOM_APM_H__ ++ ++#include ++#include ++ ++/** ++ * enum msm_apm_supply - supported power rails to supply memory arrays ++ * %MSM_APM_SUPPLY_APCC: to enable selection of VDD_APCC rail as supply ++ * %MSM_APM_SUPPLY_MX: to enable selection of VDD_MX rail as supply ++ */ ++enum msm_apm_supply { ++ MSM_APM_SUPPLY_APCC, ++ MSM_APM_SUPPLY_MX, ++}; ++ ++/* Handle used to identify an APM controller device */ ++struct msm_apm_ctrl_dev; ++ ++#ifdef CONFIG_QCOM_APM ++struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev); ++int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply); ++int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev); ++ ++#else ++static inline struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) ++{ return ERR_PTR(-EPERM); } ++static inline int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply) ++{ return -EPERM; } ++static inline int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) ++{ return -EPERM; } ++#endif ++#endif diff --git a/target/linux/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch b/target/linux/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch new file mode 100644 index 00000000000..9b9f7159f35 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch @@ -0,0 +1,12144 @@ +From c9df32c057e43e38c8113199e64f7a64f8d341df Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 11 Apr 2022 14:35:36 +0200 +Subject: [PATCH] regulator: add Qualcomm CPR regulators + +Allow building Qualcomm CPR regulators. + +Signed-off-by: Robert Marko +--- + drivers/regulator/Kconfig | 33 + + drivers/regulator/Makefile | 3 + + drivers/regulator/cpr3-npu-regulator.c | 695 +++ + drivers/regulator/cpr3-regulator.c | 5111 +++++++++++++++++++++++ + drivers/regulator/cpr3-regulator.h | 1211 ++++++ + drivers/regulator/cpr3-util.c | 2750 ++++++++++++ + drivers/regulator/cpr4-apss-regulator.c | 1819 ++++++++ + include/soc/qcom/socinfo.h | 463 ++ + 8 files changed, 12085 insertions(+) + create mode 100644 drivers/regulator/cpr3-npu-regulator.c + create mode 100644 drivers/regulator/cpr3-regulator.c + create mode 100644 drivers/regulator/cpr3-regulator.h + create mode 100644 drivers/regulator/cpr3-util.c + create mode 100644 drivers/regulator/cpr4-apss-regulator.c + create mode 100644 include/soc/qcom/socinfo.h + +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1524,4 +1524,37 @@ config REGULATOR_QCOM_LABIBB + boost regulator and IBB can be used as a negative boost regulator + for LCD display panel. + ++config REGULATOR_CPR3 ++ bool "QCOM CPR3 regulator core support" ++ help ++ This driver supports Core Power Reduction (CPR) version 3 controllers ++ which are used by some Qualcomm Technologies, Inc. SoCs to ++ manage important voltage regulators. CPR3 controllers are capable of ++ monitoring several ring oscillator sensing loops simultaneously. The ++ CPR3 controller informs software when the silicon conditions require ++ the supply voltage to be increased or decreased. On certain supply ++ rails, the CPR3 controller is able to propagate the voltage increase ++ or decrease requests all the way to the PMIC without software ++ involvement. ++ ++config REGULATOR_CPR3_NPU ++ bool "QCOM CPR3 regulator for NPU" ++ depends on OF && REGULATOR_CPR3 ++ help ++ This driver supports Qualcomm Technologies, Inc. NPU CPR3 ++ regulator Which will always operate in open loop. ++ ++config REGULATOR_CPR4_APSS ++ bool "QCOM CPR4 regulator for APSS" ++ depends on OF && REGULATOR_CPR3 ++ help ++ This driver supports Qualcomm Technologies, Inc. APSS application ++ processor specific features including memory array power mux (APM) ++ switching, one CPR4 thread which monitor the two APSS clusters that ++ are both powered by a shared supply, hardware closed-loop auto ++ voltage stepping, voltage adjustments based on online core count, ++ voltage adjustments based on temperature readings, and voltage ++ adjustments for performance boost mode. This driver reads both initial ++ voltage and CPR target quotient values out of hardware fuses. ++ + endif +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -110,6 +110,9 @@ obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qco + obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o + obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o + obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o ++obj-$(CONFIG_REGULATOR_CPR3) += cpr3-regulator.o cpr3-util.o ++obj-$(CONFIG_REGULATOR_CPR3_NPU) += cpr3-npu-regulator.o ++obj-$(CONFIG_REGULATOR_CPR4_APSS) += cpr4-apss-regulator.o + obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o + obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o + obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o +--- /dev/null ++++ b/drivers/regulator/cpr3-npu-regulator.c +@@ -0,0 +1,695 @@ ++/* ++ * Copyright (c) 2017, 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 "cpr3-regulator.h" ++ ++#define IPQ807x_NPU_FUSE_CORNERS 2 ++#define IPQ817x_NPU_FUSE_CORNERS 1 ++#define IPQ807x_NPU_FUSE_STEP_VOLT 8000 ++#define IPQ807x_NPU_VOLTAGE_FUSE_SIZE 6 ++#define IPQ807x_NPU_CPR_CLOCK_RATE 19200000 ++ ++#define IPQ807x_NPU_CPR_TCSR_START 6 ++#define IPQ807x_NPU_CPR_TCSR_END 7 ++ ++#define NPU_TSENS 5 ++ ++u32 g_valid_npu_fuse_count = IPQ807x_NPU_FUSE_CORNERS; ++/** ++ * struct cpr3_ipq807x_npu_fuses - NPU specific fuse data for IPQ807x ++ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value ++ * for each fuse corner (raw, not converted to a voltage) ++ * This struct holds the values for all of the fuses read from memory. ++ */ ++struct cpr3_ipq807x_npu_fuses { ++ u64 init_voltage[IPQ807x_NPU_FUSE_CORNERS]; ++}; ++ ++/* ++ * Constants which define the name of each fuse corner. ++ */ ++enum cpr3_ipq807x_npu_fuse_corner { ++ CPR3_IPQ807x_NPU_FUSE_CORNER_NOM = 0, ++ CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO = 1, ++}; ++ ++static const char * const cpr3_ipq807x_npu_fuse_corner_name[] = { ++ [CPR3_IPQ807x_NPU_FUSE_CORNER_NOM] = "NOM", ++ [CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO] = "TURBO", ++}; ++ ++/* ++ * IPQ807x NPU fuse parameter locations: ++ * ++ * Structs are organized with the following dimensions: ++ * Outer: 0 to 1 for fuse corners from lowest to highest corner ++ * Inner: large enough to hold the longest set of parameter segments which ++ * fully defines a fuse parameter, +1 (for NULL termination). ++ * Each segment corresponds to a contiguous group of bits from a ++ * single fuse row. These segments are concatentated together in ++ * order to form the full fuse parameter value. The segments for ++ * a given parameter may correspond to different fuse rows. ++ */ ++static struct cpr3_fuse_param ++ipq807x_npu_init_voltage_param[IPQ807x_NPU_FUSE_CORNERS][2] = { ++ {{73, 22, 27}, {} }, ++ {{73, 16, 21}, {} }, ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ++ipq807x_npu_fuse_ref_volt [IPQ807x_NPU_FUSE_CORNERS] = { ++ 912000, ++ 992000, ++}; ++ ++/* ++ * IPQ9574 (Few parameters are changed, remaining are same as IPQ807x) ++ */ ++#define IPQ9574_NPU_FUSE_CORNERS 2 ++#define IPQ9574_NPU_FUSE_STEP_VOLT 10000 ++#define IPQ9574_NPU_CPR_CLOCK_RATE 24000000 ++ ++/* ++ * fues parameters for IPQ9574 ++ */ ++static struct cpr3_fuse_param ++ipq9574_npu_init_voltage_param[IPQ9574_NPU_FUSE_CORNERS][2] = { ++ {{105, 12, 17}, {} }, ++ {{105, 6, 11}, {} }, ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 ++ */ ++static int ++ipq9574_npu_fuse_ref_volt [IPQ9574_NPU_FUSE_CORNERS] = { ++ 862500, ++ 987500, ++}; ++ ++struct cpr3_controller *g_ctrl; ++ ++void cpr3_npu_temp_notify(int sensor, int temp, int low_notif) ++{ ++ u32 prev_sensor_state; ++ ++ if (sensor != NPU_TSENS) ++ return; ++ ++ prev_sensor_state = g_ctrl->cur_sensor_state; ++ if (low_notif) ++ g_ctrl->cur_sensor_state |= BIT(sensor); ++ else ++ g_ctrl->cur_sensor_state &= ~BIT(sensor); ++ ++ if (!prev_sensor_state && g_ctrl->cur_sensor_state) ++ cpr3_handle_temp_open_loop_adjustment(g_ctrl, true); ++ else if (prev_sensor_state && !g_ctrl->cur_sensor_state) ++ cpr3_handle_temp_open_loop_adjustment(g_ctrl, false); ++} ++ ++/** ++ * cpr3_ipq807x_npu_read_fuse_data() - load NPU specific fuse parameter values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function allocates a cpr3_ipq807x_npu_fuses struct, fills it with ++ * values read out of hardware fuses, and finally copies common fuse values ++ * into the CPR3 regulator struct. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_ipq807x_npu_read_fuse_data(struct cpr3_regulator *vreg) ++{ ++ void __iomem *base = vreg->thread->ctrl->fuse_base; ++ struct cpr3_ipq807x_npu_fuses *fuse; ++ int i, rc; ++ ++ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); ++ if (!fuse) ++ return -ENOMEM; ++ ++ for (i = 0; i < g_valid_npu_fuse_count; i++) { ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr3_regulator_data->init_voltage_param[i], ++ &fuse->init_voltage[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ } ++ ++ vreg->fuse_corner_count = g_valid_npu_fuse_count; ++ vreg->platform_fuses = fuse; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_npu_parse_corner_data() - parse NPU corner data from device tree ++ * properties of the CPR3 regulator's device node ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_parse_corner_data(struct cpr3_regulator *vreg) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_ipq807x_npu_calculate_open_loop_voltages() - calculate the open-loop ++ * voltage for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @temp_correction: Temperature based correction ++ * ++ * If open-loop voltage interpolation is allowed in device tree, then ++ * this function calculates the open-loop voltage for a given corner using ++ * linear interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with their fused ++ * open-loop voltages. ++ * ++ * If open-loop voltage interpolation is not allowed, then this function uses ++ * the Fmax fused open-loop voltage for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_ipq807x_npu_calculate_open_loop_voltages( ++ struct cpr3_regulator *vreg, bool temp_correction) ++{ ++ struct cpr3_ipq807x_npu_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int i, j, rc = 0; ++ u64 freq_low, volt_low, freq_high, volt_high; ++ int *fuse_volt; ++ int *fmax_corner; ++ ++ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ if (!fuse_volt || !fmax_corner) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (ctrl->cpr_global_setting == CPR_DISABLED) ++ fuse_volt[i] = vreg->cpr3_regulator_data->fuse_ref_volt[i]; ++ else ++ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr3_regulator_data->fuse_ref_volt[i], ++ vreg->cpr3_regulator_data->fuse_step_volt, ++ fuse->init_voltage[i], ++ IPQ807x_NPU_VOLTAGE_FUSE_SIZE); ++ ++ /* Log fused open-loop voltage values for debugging purposes. */ ++ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", ++ cpr3_ipq807x_npu_fuse_corner_name[i], ++ fuse_volt[i]); ++ } ++ ++ rc = cpr3_determine_part_type(vreg, ++ fuse_volt[CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO]); ++ if (rc) { ++ cpr3_err(vreg, ++ "fused part type detection failed failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, ++ "fused open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ if (temp_correction) { ++ rc = cpr3_determine_temp_base_open_loop_correction(vreg, ++ fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, ++ "temp open-loop voltage adj. failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ if (fuse_volt[i] < fuse_volt[i - 1]) { ++ cpr3_info(vreg, ++ "fuse corner %d voltage=%d uV < fuse corner %d \ ++ voltage=%d uV; overriding: fuse corner %d \ ++ voltage=%d\n", ++ i, fuse_volt[i], i - 1, fuse_volt[i - 1], ++ i, fuse_volt[i - 1]); ++ fuse_volt[i] = fuse_volt[i - 1]; ++ } ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ for (i = 0; i <= fmax_corner[0]; i++) ++ vreg->corner[i].open_loop_volt = fuse_volt[0]; ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ volt_low = fuse_volt[i - 1]; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ volt_high = fuse_volt[i]; ++ ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].open_loop_volt = cpr3_interpolate( ++ freq_low, volt_low, freq_high, volt_high, ++ vreg->corner[j].proc_freq); ++ } ++ ++done: ++ if (rc == 0) { ++ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); ++ for (i = 0; i < vreg->corner_count; i++) ++ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, ++ vreg->corner[i].open_loop_volt); ++ ++ rc = cpr3_adjust_open_loop_voltages(vreg); ++ if (rc) ++ cpr3_err(vreg, ++ "open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ } ++ ++ kfree(fuse_volt); ++ kfree(fmax_corner); ++ return rc; ++} ++ ++/** ++ * cpr3_npu_print_settings() - print out NPU CPR configuration settings into ++ * the kernel log for debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++static void cpr3_npu_print_settings(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_corner *corner; ++ int i; ++ ++ cpr3_debug(vreg, ++ "Corner: Frequency (Hz), Fuse Corner, Floor (uV), \ ++ Open-Loop (uV), Ceiling (uV)\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", ++ i, corner->proc_freq, corner->cpr_fuse_corner, ++ corner->floor_volt, corner->open_loop_volt, ++ corner->ceiling_volt); ++ } ++ ++ if (vreg->thread->ctrl->apm) ++ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", ++ vreg->thread->ctrl->apm_threshold_volt, ++ vreg->thread->ctrl->apm_adj_volt); ++} ++ ++/** ++ * cpr3_ipq807x_npu_calc_temp_based_ol_voltages() - Calculate the open loop ++ * voltages based on temperature based correction margins ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++ ++static int ++cpr3_ipq807x_npu_calc_temp_based_ol_voltages(struct cpr3_regulator *vreg, ++ bool temp_correction) ++{ ++ int rc, i; ++ ++ rc = cpr3_ipq807x_npu_calculate_open_loop_voltages(vreg, ++ temp_correction); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr3_limit_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr3_open_loop_voltage_as_ceiling(vreg); ++ ++ rc = cpr3_limit_floor_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (temp_correction) ++ vreg->corner[i].cold_temp_open_loop_volt = ++ vreg->corner[i].open_loop_volt; ++ else ++ vreg->corner[i].normal_temp_open_loop_volt = ++ vreg->corner[i].open_loop_volt; ++ } ++ ++ cpr3_npu_print_settings(vreg); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_npu_init_thread() - perform steps necessary to initialize the ++ * configuration data for a CPR3 thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_thread(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_thread_data(thread); ++ if (rc) { ++ cpr3_err(thread->ctrl, ++ "thread %u CPR thread data from DT- failed, rc=%d\n", ++ thread->thread_id, rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_npu_init_regulator() - perform all steps necessary to initialize the ++ * configuration data for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_regulator(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_ipq807x_npu_fuses *fuse; ++ int rc, cold_temp = 0; ++ bool can_adj_cold_temp = cpr3_can_adjust_cold_temp(vreg); ++ ++ rc = cpr3_ipq807x_npu_read_fuse_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ fuse = vreg->platform_fuses; ++ ++ rc = cpr3_npu_parse_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, ++ "Cannot read CPR corner data from DT, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr3_mem_acc_init(vreg); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(vreg, ++ "Cannot initialize mem-acc regulator settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (can_adj_cold_temp) { ++ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, true); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, false); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (can_adj_cold_temp) { ++ cpr3_info(vreg, ++ "Normal and Cold condition init done. Default to normal.\n"); ++ ++ rc = cpr3_get_cold_temp_threshold(vreg, &cold_temp); ++ if (rc) { ++ cpr3_err(vreg, ++ "Get cold temperature threshold failed, rc=%d\n", rc); ++ return rc; ++ } ++ register_low_temp_notif(NPU_TSENS, cold_temp, ++ cpr3_npu_temp_notify); ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_npu_init_controller() - perform NPU CPR3 controller specific ++ * initializations ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_controller(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_open_loop_common_ctrl_data(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3; ++ ctrl->supports_hw_closed_loop = false; ++ ++ return 0; ++} ++ ++static const struct cpr3_reg_data ipq807x_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ807x_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq807x_npu_init_voltage_param, ++ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, ++}; ++ ++static const struct cpr3_reg_data ipq817x_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ817x_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq807x_npu_init_voltage_param, ++ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, ++}; ++ ++static const struct cpr3_reg_data ipq9574_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ9574_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq9574_npu_init_voltage_param, ++ .fuse_ref_volt = ipq9574_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ9574_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ9574_NPU_CPR_CLOCK_RATE, ++}; ++ ++static struct of_device_id cpr3_regulator_match_table[] = { ++ { ++ .compatible = "qcom,cpr3-ipq807x-npu-regulator", ++ .data = &ipq807x_cpr_npu ++ }, ++ { ++ .compatible = "qcom,cpr3-ipq817x-npu-regulator", ++ .data = &ipq817x_cpr_npu ++ }, ++ { ++ .compatible = "qcom,cpr3-ipq9574-npu-regulator", ++ .data = &ipq9574_cpr_npu ++ }, ++ {} ++}; ++ ++static int cpr3_npu_regulator_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_controller *ctrl; ++ int i, rc; ++ const struct of_device_id *match; ++ struct cpr3_reg_data *cpr_data; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -EINVAL; ++ } ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ g_ctrl = ctrl; ++ ++ match = of_match_device(cpr3_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr3_reg_data *)match->data; ++ g_valid_npu_fuse_count = cpr_data->cpr_valid_fuse_count; ++ dev_info(dev, "NPU CPR valid fuse count: %d\n", g_valid_npu_fuse_count); ++ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; ++ ++ ctrl->dev = dev; ++ /* Set to false later if anything precludes CPR operation. */ ++ ctrl->cpr_allowed_hw = true; ++ ++ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", ++ &ctrl->name); ++ if (rc) { ++ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_map_fuse_base(ctrl, pdev); ++ if (rc) { ++ cpr3_err(ctrl, "could not map fuse base address\n"); ++ return rc; ++ } ++ ++ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_NPU_CPR_TCSR_START, ++ IPQ807x_NPU_CPR_TCSR_END); ++ if (rc) { ++ cpr3_err(ctrl, "could not read CPR tcsr rsetting\n"); ++ return rc; ++ } ++ ++ rc = cpr3_allocate_threads(ctrl, 0, 0); ++ if (rc) { ++ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread_count != 1) { ++ cpr3_err(ctrl, "expected 1 thread but found %d\n", ++ ctrl->thread_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_npu_init_controller(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_npu_init_thread(&ctrl->thread[0]); ++ if (rc) { ++ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { ++ ctrl->thread[0].vreg[i].cpr3_regulator_data = cpr_data; ++ rc = cpr3_npu_init_regulator(&ctrl->thread[0].vreg[i]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ platform_set_drvdata(pdev, ctrl); ++ ++ return cpr3_open_loop_regulator_register(pdev, ctrl); ++} ++ ++static int cpr3_npu_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_open_loop_regulator_unregister(ctrl); ++} ++ ++static struct platform_driver cpr3_npu_regulator_driver = { ++ .driver = { ++ .name = "qcom,cpr3-npu-regulator", ++ .of_match_table = cpr3_regulator_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = cpr3_npu_regulator_probe, ++ .remove = cpr3_npu_regulator_remove, ++}; ++ ++static int cpr3_regulator_init(void) ++{ ++ return platform_driver_register(&cpr3_npu_regulator_driver); ++} ++arch_initcall(cpr3_regulator_init); ++ ++static void cpr3_regulator_exit(void) ++{ ++ platform_driver_unregister(&cpr3_npu_regulator_driver); ++} ++module_exit(cpr3_regulator_exit); ++ ++MODULE_DESCRIPTION("QCOM CPR3 NPU regulator driver"); ++MODULE_LICENSE("Dual BSD/GPLv2"); ++MODULE_ALIAS("platform:npu-ipq807x"); +--- /dev/null ++++ b/drivers/regulator/cpr3-regulator.c +@@ -0,0 +1,5111 @@ ++/* ++ * Copyright (c) 2015-2017, 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. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define CPR3_REGULATOR_CORNER_INVALID (-1) ++#define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0) ++ ++/* CPR3 registers */ ++#define CPR3_REG_CPR_CTL 0x4 ++#define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0) ++#define CPR3_CPR_CTL_LOOP_ENABLE BIT(0) ++#define CPR3_CPR_CTL_LOOP_DISABLE 0 ++#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1) ++#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1 ++#define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6) ++#define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1 ++#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3 ++#define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9) ++#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9 ++ ++#define CPR3_REG_CPR_STATUS 0x8 ++#define CPR3_CPR_STATUS_BUSY_MASK BIT(0) ++#define CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK BIT(1) ++ ++/* ++ * This register is not present on controllers that support HW closed-loop ++ * except CPR4 APSS controller. ++ */ ++#define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC ++ ++#define CPR3_REG_CPR_STEP_QUOT 0x14 ++#define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0) ++#define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0 ++#define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6) ++#define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6 ++ ++#define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro)) ++ ++#define CPR3_REG_SENSOR_BYPASS_WRITE(sensor) (0xE0 + 0x4 * ((sensor) / 32)) ++#define CPR3_REG_SENSOR_BYPASS_WRITE_BANK(bank) (0xE0 + 0x4 * (bank)) ++ ++#define CPR3_REG_SENSOR_MASK_WRITE(sensor) (0x120 + 0x4 * ((sensor) / 32)) ++#define CPR3_REG_SENSOR_MASK_WRITE_BANK(bank) (0x120 + 0x4 * (bank)) ++#define CPR3_REG_SENSOR_MASK_READ(sensor) (0x140 + 0x4 * ((sensor) / 32)) ++ ++#define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor)) ++ ++#define CPR3_REG_CONT_CMD 0x800 ++#define CPR3_CONT_CMD_ACK 0x1 ++#define CPR3_CONT_CMD_NACK 0x0 ++ ++#define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread)) ++#define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0) ++#define CPR3_THRESH_CONS_DOWN_SHIFT 0 ++#define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4) ++#define CPR3_THRESH_CONS_UP_SHIFT 4 ++#define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8) ++#define CPR3_THRESH_DOWN_THRESH_SHIFT 8 ++#define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13) ++#define CPR3_THRESH_UP_THRESH_SHIFT 13 ++ ++#define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread)) ++ ++#define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread)) ++#define CPR3_RESULT0_BUSY_MASK BIT(0) ++#define CPR3_RESULT0_STEP_DN_MASK BIT(1) ++#define CPR3_RESULT0_STEP_UP_MASK BIT(2) ++#define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3) ++#define CPR3_RESULT0_ERROR_STEPS_SHIFT 3 ++#define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8) ++#define CPR3_RESULT0_ERROR_SHIFT 8 ++#define CPR3_RESULT0_NEGATIVE_MASK BIT(20) ++ ++#define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread)) ++#define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0) ++#define CPR3_RESULT1_QUOT_MIN_SHIFT 0 ++#define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12) ++#define CPR3_RESULT1_QUOT_MAX_SHIFT 12 ++#define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24) ++#define CPR3_RESULT1_RO_MIN_SHIFT 24 ++#define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28) ++#define CPR3_RESULT1_RO_MAX_SHIFT 28 ++ ++#define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread)) ++#define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0) ++#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0 ++#define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6) ++#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6 ++#define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16) ++#define CPR3_RESULT2_SENSOR_MIN_SHIFT 16 ++#define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24) ++#define CPR3_RESULT2_SENSOR_MAX_SHIFT 24 ++ ++#define CPR3_REG_IRQ_EN 0x81C ++#define CPR3_REG_IRQ_CLEAR 0x820 ++#define CPR3_REG_IRQ_STATUS 0x824 ++#define CPR3_IRQ_UP BIT(3) ++#define CPR3_IRQ_MID BIT(2) ++#define CPR3_IRQ_DOWN BIT(1) ++ ++#define CPR3_REG_TARGET_QUOT(thread, ro) \ ++ (0x840 + 0x440 * (thread) + 0x4 * (ro)) ++ ++/* Registers found only on controllers that support HW closed-loop. */ ++#define CPR3_REG_PD_THROTTLE 0xE8 ++#define CPR3_PD_THROTTLE_DISABLE 0x0 ++ ++#define CPR3_REG_HW_CLOSED_LOOP 0x3000 ++#define CPR3_HW_CLOSED_LOOP_ENABLE 0x0 ++#define CPR3_HW_CLOSED_LOOP_DISABLE 0x1 ++ ++#define CPR3_REG_CPR_TIMER_MID_CONT 0x3004 ++#define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008 ++ ++#define CPR3_REG_LAST_MEASUREMENT 0x7F8 ++#define CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT 0 ++#define CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT 4 ++#define CPR3_LAST_MEASUREMENT_THREAD_DN(thread) \ ++ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT) ++#define CPR3_LAST_MEASUREMENT_THREAD_UP(thread) \ ++ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT) ++#define CPR3_LAST_MEASUREMENT_AGGR_DN BIT(8) ++#define CPR3_LAST_MEASUREMENT_AGGR_MID BIT(9) ++#define CPR3_LAST_MEASUREMENT_AGGR_UP BIT(10) ++#define CPR3_LAST_MEASUREMENT_VALID BIT(11) ++#define CPR3_LAST_MEASUREMENT_SAW_ERROR BIT(12) ++#define CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK GENMASK(23, 16) ++#define CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT 16 ++ ++/* CPR4 controller specific registers and bit definitions */ ++#define CPR4_REG_CPR_TIMER_CLAMP 0x10 ++#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) ++ ++#define CPR4_REG_MISC 0x700 ++#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20) ++#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20 ++#define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24) ++#define CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT 24 ++#define CPR4_MISC_TEMP_SENSOR_ID_END_MASK GENMASK(31, 28) ++#define CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT 28 ++ ++#define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4 ++#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0) ++#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0 ++#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5) ++#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5 ++ ++#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8 ++#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18) ++#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT 18 ++ ++#define CPR4_REG_MARGIN_TEMP_CORE(core) (0x7AC + 0x4 * (core)) ++#define CPR4_MARGIN_TEMP_CORE_ADJ_MASK GENMASK(7, 0) ++#define CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT 8 ++ ++#define CPR4_REG_MARGIN_TEMP_POINT0N1 0x7F0 ++#define CPR4_MARGIN_TEMP_POINT0_MASK GENMASK(11, 0) ++#define CPR4_MARGIN_TEMP_POINT0_SHIFT 0 ++#define CPR4_MARGIN_TEMP_POINT1_MASK GENMASK(23, 12) ++#define CPR4_MARGIN_TEMP_POINT1_SHIFT 12 ++#define CPR4_REG_MARGIN_TEMP_POINT2 0x7F4 ++#define CPR4_MARGIN_TEMP_POINT2_MASK GENMASK(11, 0) ++#define CPR4_MARGIN_TEMP_POINT2_SHIFT 0 ++ ++#define CPR4_REG_MARGIN_ADJ_CTL 0x7F8 ++#define CPR4_MARGIN_ADJ_CTL_BOOST_EN BIT(0) ++#define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN BIT(1) ++#define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN BIT(2) ++#define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN BIT(3) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK BIT(4) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE BIT(4) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE 0 ++#define CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN BIT(7) ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN BIT(8) ++#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK GENMASK(16, 12) ++#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT 12 ++#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK GENMASK(21, 19) ++#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT 19 ++#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK GENMASK(25, 22) ++#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT 22 ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26) ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26 ++ ++#define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread)) ++#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31) ++#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0) ++ ++/* ++ * The amount of time to wait for the CPR controller to become idle when ++ * performing an aging measurement. ++ */ ++#define CPR3_AGING_MEASUREMENT_TIMEOUT_NS 5000000 ++ ++/* ++ * The number of individual aging measurements to perform which are then ++ * averaged together in order to determine the final aging adjustment value. ++ */ ++#define CPR3_AGING_MEASUREMENT_ITERATIONS 16 ++ ++/* ++ * Aging measurements for the aged and unaged ring oscillators take place a few ++ * microseconds apart. If the vdd-supply voltage fluctuates between the two ++ * measurements, then the difference between them will be incorrect. The ++ * difference could end up too high or too low. This constant defines the ++ * number of lowest and highest measurements to ignore when averaging. ++ */ ++#define CPR3_AGING_MEASUREMENT_FILTER 3 ++ ++/* ++ * The number of times to attempt the full aging measurement sequence before ++ * declaring a measurement failure. ++ */ ++#define CPR3_AGING_RETRY_COUNT 5 ++ ++/* ++ * The maximum time to wait in microseconds for a CPR register write to ++ * complete. ++ */ ++#define CPR3_REGISTER_WRITE_DELAY_US 200 ++ ++static DEFINE_MUTEX(cpr3_controller_list_mutex); ++static LIST_HEAD(cpr3_controller_list); ++static struct dentry *cpr3_debugfs_base; ++ ++/** ++ * cpr3_read() - read four bytes from the memory address specified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * ++ * Return: memory address value ++ */ ++static inline u32 cpr3_read(struct cpr3_controller *ctrl, u32 offset) ++{ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register reads are not possible when CPR clocks are disabled\n"); ++ return 0; ++ } ++ ++ return readl_relaxed(ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_write() - write four bytes to the memory address specified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * @value: Value to write to the memory address ++ * ++ * Return: none ++ */ ++static inline void cpr3_write(struct cpr3_controller *ctrl, u32 offset, ++ u32 value) ++{ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); ++ return; ++ } ++ ++ writel_relaxed(value, ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_masked_write() - perform a read-modify-write sequence so that only ++ * masked bits are modified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * @mask: Mask identifying the bits that should be modified ++ * @value: Value to write to the memory address ++ * ++ * Return: none ++ */ ++static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset, ++ u32 mask, u32 value) ++{ ++ u32 reg_val, orig_val; ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); ++ return; ++ } ++ ++ reg_val = orig_val = readl_relaxed(ctrl->cpr_ctrl_base + offset); ++ reg_val &= ~mask; ++ reg_val |= value & mask; ++ ++ if (reg_val != orig_val) ++ writel_relaxed(reg_val, ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_ctrl_loop_enable() - enable the CPR sensing loop for a given controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl) ++{ ++ if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta ++ && ctrl->aggr_corner.sdelta->allow_boost)) ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE); ++} ++ ++/** ++ * cpr3_ctrl_loop_disable() - disable the CPR sensing loop for a given ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static inline void cpr3_ctrl_loop_disable(struct cpr3_controller *ctrl) ++{ ++ if (ctrl->cpr_enabled) ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_DISABLE); ++} ++ ++/** ++ * cpr3_clock_enable() - prepare and enable all clocks used by this CPR3 ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_clock_enable(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = clk_prepare_enable(ctrl->bus_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable bus clock, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = clk_prepare_enable(ctrl->iface_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable interface clock, rc=%d\n", rc); ++ clk_disable_unprepare(ctrl->bus_clk); ++ return rc; ++ } ++ ++ rc = clk_prepare_enable(ctrl->core_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable core clock, rc=%d\n", rc); ++ clk_disable_unprepare(ctrl->iface_clk); ++ clk_disable_unprepare(ctrl->bus_clk); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_clock_disable() - disable and unprepare all clocks used by this CPR3 ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static void cpr3_clock_disable(struct cpr3_controller *ctrl) ++{ ++ clk_disable_unprepare(ctrl->core_clk); ++ clk_disable_unprepare(ctrl->iface_clk); ++ clk_disable_unprepare(ctrl->bus_clk); ++} ++ ++/** ++ * cpr3_ctrl_clear_cpr4_config() - clear the CPR4 register configuration ++ * programmed for current aggregated corner of a given controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl) ++{ ++ struct cpr4_sdelta *aggr_sdelta = ctrl->aggr_corner.sdelta; ++ bool cpr_enabled = ctrl->cpr_enabled; ++ int i, rc = 0; ++ ++ if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj ++ || aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost)) ++ /* cpr4 features are not enabled */ ++ return 0; ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ /* ++ * Clear feature enable configuration made for current ++ * aggregated corner. ++ */ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK ++ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_BOOST_EN ++ | CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, ++ 0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); ++ ++ for (i = 0; i <= aggr_sdelta->max_core_count; i++) { ++ /* Clear voltage margin adjustments programmed in TEMP_COREi */ ++ cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0); ++ } ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_closed_loop_enable() - enable logical CPR closed-loop operation ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_closed_loop_enable(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ if (!ctrl->cpr_allowed_hw || !ctrl->cpr_allowed_sw) { ++ cpr3_err(ctrl, "cannot enable closed-loop CPR operation because it is disallowed\n"); ++ return -EPERM; ++ } else if (ctrl->cpr_enabled) { ++ /* Already enabled */ ++ return 0; ++ } else if (ctrl->cpr_suspended) { ++ /* ++ * CPR must remain disabled as the system is entering suspend. ++ */ ++ return 0; ++ } ++ ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to enable CPR clocks, rc=%d\n", rc); ++ return rc; ++ } ++ ++ ctrl->cpr_enabled = true; ++ cpr3_debug(ctrl, "CPR closed-loop operation enabled\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_closed_loop_disable() - disable logical CPR closed-loop operation ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static inline int cpr3_closed_loop_disable(struct cpr3_controller *ctrl) ++{ ++ if (!ctrl->cpr_enabled) { ++ /* Already disabled */ ++ return 0; ++ } ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ cpr3_debug(ctrl, "CPR closed-loop operation disabled\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_get_gcnt() - returns the GCNT register value corresponding ++ * to the clock rate and sensor time of the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: GCNT value ++ */ ++static u32 cpr3_regulator_get_gcnt(struct cpr3_controller *ctrl) ++{ ++ u64 temp; ++ unsigned int remainder; ++ u32 gcnt; ++ ++ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->sensor_time; ++ remainder = do_div(temp, 1000000000); ++ if (remainder) ++ temp++; ++ /* ++ * GCNT == 0 corresponds to a single ref clock measurement interval so ++ * offset GCNT values by 1. ++ */ ++ gcnt = temp - 1; ++ ++ return gcnt; ++} ++ ++/** ++ * cpr3_regulator_init_thread() - performs hardware initialization of CPR ++ * thread registers ++ * @thread: Pointer to the CPR3 thread ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_thread(struct cpr3_thread *thread) ++{ ++ u32 reg; ++ ++ reg = (thread->consecutive_up << CPR3_THRESH_CONS_UP_SHIFT) ++ & CPR3_THRESH_CONS_UP_MASK; ++ reg |= (thread->consecutive_down << CPR3_THRESH_CONS_DOWN_SHIFT) ++ & CPR3_THRESH_CONS_DOWN_MASK; ++ reg |= (thread->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT) ++ & CPR3_THRESH_UP_THRESH_MASK; ++ reg |= (thread->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT) ++ & CPR3_THRESH_DOWN_THRESH_MASK; ++ ++ cpr3_write(thread->ctrl, CPR3_REG_THRESH(thread->thread_id), reg); ++ ++ /* ++ * Mask all RO's initially so that unused thread doesn't contribute ++ * to closed-loop voltage. ++ */ ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ CPR3_RO_MASK); ++ ++ return 0; ++} ++ ++/** ++ * cpr4_regulator_init_temp_points() - performs hardware initialization of CPR4 ++ * registers to track tsen temperature data and also specify the ++ * temperature band range values to apply different voltage margins ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_regulator_init_temp_points(struct cpr3_controller *ctrl) ++{ ++ if (!ctrl->allow_temp_adj) ++ return 0; ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_TEMP_SENSOR_ID_START_MASK, ++ ctrl->temp_sensor_id_start ++ << CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_TEMP_SENSOR_ID_END_MASK, ++ ctrl->temp_sensor_id_end ++ << CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT2, ++ CPR4_MARGIN_TEMP_POINT2_MASK, ++ (ctrl->temp_band_count == 4 ? ctrl->temp_points[2] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT2_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, ++ CPR4_MARGIN_TEMP_POINT1_MASK, ++ (ctrl->temp_band_count >= 3 ? ctrl->temp_points[1] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT1_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, ++ CPR4_MARGIN_TEMP_POINT0_MASK, ++ (ctrl->temp_band_count >= 2 ? ctrl->temp_points[0] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT0_SHIFT); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_cpr4() - performs hardware initialization at the ++ * controller and thread level required for CPR4 operation. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * This function allocates sdelta structures and sdelta tables for aggregated ++ * corners of the controller and its threads. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ struct cpr4_sdelta *sdelta; ++ int i, j, ctrl_max_core_count, thread_max_core_count, rc = 0; ++ bool ctrl_valid_sdelta, thread_valid_sdelta; ++ u32 pmic_step_size = 1; ++ int thread_id = 0; ++ u64 temp; ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->saw_use_unit_mV) ++ pmic_step_size = ctrl->step_volt / 1000; ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK, ++ (pmic_step_size ++ << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, ++ CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK, ++ (ctrl->down_error_step_limit ++ << CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, ++ CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK, ++ (ctrl->up_error_step_limit ++ << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); ++ ++ /* ++ * Enable thread aggregation regardless of which threads are ++ * enabled or disabled. ++ */ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, ++ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, ++ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN); ++ ++ switch (ctrl->thread_count) { ++ case 0: ++ /* Disable both threads */ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(0), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(1), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ break; ++ case 1: ++ /* Disable unused thread */ ++ thread_id = ctrl->thread[0].thread_id ? 0 : 1; ++ cpr3_masked_write(ctrl, ++ CPR4_REG_CPR_MASK_THREAD(thread_id), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ break; ++ } ++ } ++ ++ if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj ++ && !ctrl->allow_boost) { ++ /* ++ * Skip below configuration as none of the features ++ * are enabled. ++ */ ++ return rc; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, ++ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK, ++ ctrl->step_quot_fixed ++ << CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN, ++ (ctrl->use_dynamic_step_quot ++ ? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK, ++ ctrl->initial_temp_band ++ << CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT); ++ ++ rc = cpr4_regulator_init_temp_points(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "initialize temp points failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (ctrl->voltage_settling_time) { ++ /* ++ * Configure the settling timer used to account for ++ * one VDD supply step. ++ */ ++ temp = (u64)ctrl->cpr_clock_rate ++ * (u64)ctrl->voltage_settling_time; ++ do_div(temp, 1000000000); ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS, ++ CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK, ++ temp ++ << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT); ++ } ++ ++ /* ++ * Allocate memory for cpr4_sdelta structure and sdelta table for ++ * controller aggregated corner by finding the maximum core count ++ * used by any cpr3 regulators. ++ */ ++ ctrl_max_core_count = 1; ++ ctrl_valid_sdelta = false; ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ ++ /* ++ * Allocate memory for cpr4_sdelta structure and sdelta table ++ * for thread aggregated corner by finding the maximum core ++ * count used by any cpr3 regulators of the thread. ++ */ ++ thread_max_core_count = 1; ++ thread_valid_sdelta = false; ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ thread_max_core_count = max(thread_max_core_count, ++ vreg->max_core_count); ++ thread_valid_sdelta |= (vreg->allow_core_count_adj ++ | vreg->allow_temp_adj ++ | vreg->allow_boost); ++ } ++ if (thread_valid_sdelta) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), ++ GFP_KERNEL); ++ if (!sdelta) ++ return -ENOMEM; ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, ++ thread_max_core_count ++ * ctrl->temp_band_count, ++ sizeof(*sdelta->table), ++ GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ sdelta->boost_table = devm_kcalloc(ctrl->dev, ++ ctrl->temp_band_count, ++ sizeof(*sdelta->boost_table), ++ GFP_KERNEL); ++ if (!sdelta->boost_table) ++ return -ENOMEM; ++ ++ thread->aggr_corner.sdelta = sdelta; ++ } ++ ++ ctrl_valid_sdelta |= thread_valid_sdelta; ++ ctrl_max_core_count = max(ctrl_max_core_count, ++ thread_max_core_count); ++ } ++ ++ if (ctrl_valid_sdelta) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), GFP_KERNEL); ++ if (!sdelta) ++ return -ENOMEM; ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, ctrl_max_core_count ++ * ctrl->temp_band_count, ++ sizeof(*sdelta->table), GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ sdelta->boost_table = devm_kcalloc(ctrl->dev, ++ ctrl->temp_band_count, ++ sizeof(*sdelta->boost_table), ++ GFP_KERNEL); ++ if (!sdelta->boost_table) ++ return -ENOMEM; ++ ++ ctrl->aggr_corner.sdelta = sdelta; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_write_temp_core_margin() - programs hardware SDELTA registers with ++ * the voltage margin adjustments that need to be applied for ++ * different online core-count and temperature bands. ++ * @ctrl: Pointer to the CPR3 controller ++ * @addr: SDELTA register address ++ * @temp_core_adj: Array of voltage margin values for different temperature ++ * bands. ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: none ++ */ ++static void cpr3_write_temp_core_margin(struct cpr3_controller *ctrl, ++ int addr, int *temp_core_adj) ++{ ++ int i, margin_steps; ++ u32 reg = 0; ++ ++ for (i = 0; i < ctrl->temp_band_count; i++) { ++ margin_steps = max(min(temp_core_adj[i], 127), -128); ++ reg |= (margin_steps & CPR4_MARGIN_TEMP_CORE_ADJ_MASK) << ++ (i * CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT); ++ } ++ ++ cpr3_write(ctrl, addr, reg); ++ cpr3_debug(ctrl, "sdelta offset=0x%08x, val=0x%08x\n", addr, reg); ++} ++ ++/** ++ * cpr3_controller_program_sdelta() - programs hardware SDELTA registers with ++ * the voltage margin adjustments that need to be applied at ++ * different online core-count and temperature bands. Also, ++ * programs hardware register configuration for per-online-core ++ * and per-temperature based adjustments. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_corner *corner = &ctrl->aggr_corner; ++ struct cpr4_sdelta *sdelta = corner->sdelta; ++ int i, index, max_core_count, rc = 0; ++ bool cpr_enabled = ctrl->cpr_enabled; ++ ++ if (!sdelta) ++ /* cpr4_sdelta not defined for current aggregated corner */ ++ return 0; ++ ++ if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ (ctrl->use_hw_closed_loop && !sdelta->allow_boost) ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0); ++ } ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj ++ && !sdelta->allow_boost) { ++ /* ++ * Per-online-core, per-temperature and voltage boost ++ * adjustments are disabled for this aggregation corner. ++ */ ++ return 0; ++ } ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ max_core_count = sdelta->max_core_count; ++ ++ if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) { ++ if (sdelta->allow_core_count_adj) { ++ /* Program TEMP_CORE0 to same margins as TEMP_CORE1 */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE(0), ++ &sdelta->table[0]); ++ } ++ ++ for (i = 0; i < max_core_count; i++) { ++ index = i * sdelta->temp_band_count; ++ /* ++ * Program TEMP_COREi with voltage margin adjustments ++ * that need to be applied when the number of cores ++ * becomes i. ++ */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE( ++ sdelta->allow_core_count_adj ++ ? i + 1 : max_core_count), ++ &sdelta->table[index]); ++ } ++ } ++ ++ if (sdelta->allow_boost) { ++ /* Program only boost_num_cores row of SDELTA */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores), ++ &sdelta->boost_table[0]); ++ } ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) { ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, ++ max_core_count ++ << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); ++ } ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK ++ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_BOOST_EN, ++ max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT ++ | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ++ ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) ++ | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) ++ ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) ++ | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) ++ || !ctrl->supports_hw_closed_loop) ++ ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) ++ | (sdelta->allow_boost ++ ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); ++ ++ /* ++ * Ensure that all previous CPR register writes have completed before ++ * continuing. ++ */ ++ mb(); ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_ctrl() - performs hardware initialization of CPR ++ * controller registers ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) ++{ ++ int i, j, k, m, rc; ++ u32 ro_used = 0; ++ u32 gcnt, cont_dly, up_down_dly, val; ++ u64 temp; ++ char *mode; ++ ++ if (ctrl->core_clk) { ++ rc = clk_set_rate(ctrl->core_clk, ctrl->cpr_clock_rate); ++ if (rc) { ++ cpr3_err(ctrl, "clk_set_rate(core_clk, %u) failed, rc=%d\n", ++ ctrl->cpr_clock_rate, rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ ++ /* Find all RO's used by any corner of any regulator. */ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ for (k = 0; k < ctrl->thread[i].vreg[j].corner_count; ++ k++) ++ for (m = 0; m < CPR3_RO_COUNT; m++) ++ if (ctrl->thread[i].vreg[j].corner[k]. ++ target_quot[m]) ++ ro_used |= BIT(m); ++ ++ /* Configure the GCNT of the RO's that will be used */ ++ gcnt = cpr3_regulator_get_gcnt(ctrl); ++ for (i = 0; i < CPR3_RO_COUNT; i++) ++ if (ro_used & BIT(i)) ++ cpr3_write(ctrl, CPR3_REG_GCNT(i), gcnt); ++ ++ /* Configure the loop delay time */ ++ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->loop_time; ++ do_div(temp, 1000000000); ++ cont_dly = temp; ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly); ++ else ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, cont_dly); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ temp = (u64)ctrl->cpr_clock_rate * ++ (u64)ctrl->up_down_delay_time; ++ do_div(temp, 1000000000); ++ up_down_dly = temp; ++ if (ctrl->supports_hw_closed_loop) ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, ++ up_down_dly); ++ cpr3_debug(ctrl, "up_down_dly=%u, up_down_delay_time=%u ns\n", ++ up_down_dly, ctrl->up_down_delay_time); ++ } ++ ++ cpr3_debug(ctrl, "cpr_clock_rate=%u HZ, sensor_time=%u ns, loop_time=%u ns, gcnt=%u, cont_dly=%u\n", ++ ctrl->cpr_clock_rate, ctrl->sensor_time, ctrl->loop_time, ++ gcnt, cont_dly); ++ ++ /* Configure CPR sensor operation */ ++ val = (ctrl->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT) ++ & CPR3_CPR_CTL_IDLE_CLOCKS_MASK; ++ val |= (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) ++ & CPR3_CPR_CTL_COUNT_MODE_MASK; ++ val |= (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT) ++ & CPR3_CPR_CTL_COUNT_REPEAT_MASK; ++ cpr3_write(ctrl, CPR3_REG_CPR_CTL, val); ++ ++ cpr3_debug(ctrl, "idle_clocks=%u, count_mode=%u, count_repeat=%u; CPR_CTL=0x%08X\n", ++ ctrl->idle_clocks, ctrl->count_mode, ctrl->count_repeat, val); ++ ++ /* Configure CPR default step quotients */ ++ val = (ctrl->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT) ++ & CPR3_CPR_STEP_QUOT_MIN_MASK; ++ val |= (ctrl->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT) ++ & CPR3_CPR_STEP_QUOT_MAX_MASK; ++ cpr3_write(ctrl, CPR3_REG_CPR_STEP_QUOT, val); ++ ++ cpr3_debug(ctrl, "step_quot_min=%u, step_quot_max=%u; STEP_QUOT=0x%08X\n", ++ ctrl->step_quot_init_min, ctrl->step_quot_init_max, val); ++ ++ /* Configure the CPR sensor ownership */ ++ for (i = 0; i < ctrl->sensor_count; i++) ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(i), ++ ctrl->sensor_owner[i]); ++ ++ /* Configure per-thread registers */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ rc = cpr3_regulator_init_thread(&ctrl->thread[i]); ++ if (rc) { ++ cpr3_err(ctrl, "CPR thread register initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ ++ cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", ++ ctrl->proc_clock_throttle); ++ } ++ ++ if ((ctrl->use_hw_closed_loop || ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ++ ctrl->vdd_limit_regulator) { ++ rc = regulator_enable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_regulator_init_cpr4(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR4-specific controller initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ /* Ensure that all register writes complete before disabling clocks. */ ++ wmb(); ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ ++ if (!ctrl->cpr_allowed_sw || !ctrl->cpr_allowed_hw) ++ mode = "open-loop"; ++ else if (ctrl->supports_hw_closed_loop) ++ mode = ctrl->use_hw_closed_loop ++ ? "HW closed-loop" : "SW closed-loop"; ++ else ++ mode = "closed-loop"; ++ ++ cpr3_info(ctrl, "Default CPR mode = %s", mode); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_set_target_quot() - configure the target quotient for each ++ * RO of the CPR3 thread and set the RO mask ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_set_target_quot(struct cpr3_thread *thread) ++{ ++ u32 new_quot, last_quot; ++ int i; ++ ++ if (thread->aggr_corner.ro_mask == CPR3_RO_MASK ++ && thread->last_closed_loop_aggr_corner.ro_mask == CPR3_RO_MASK) { ++ /* Avoid writing target quotients since all RO's are masked. */ ++ return; ++ } else if (thread->aggr_corner.ro_mask == CPR3_RO_MASK) { ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ CPR3_RO_MASK); ++ thread->last_closed_loop_aggr_corner.ro_mask = CPR3_RO_MASK; ++ /* ++ * Only the RO_MASK register needs to be written since all ++ * RO's are masked. ++ */ ++ return; ++ } else if (thread->aggr_corner.ro_mask ++ != thread->last_closed_loop_aggr_corner.ro_mask) { ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ thread->aggr_corner.ro_mask); ++ } ++ ++ for (i = 0; i < CPR3_RO_COUNT; i++) { ++ new_quot = thread->aggr_corner.target_quot[i]; ++ last_quot = thread->last_closed_loop_aggr_corner.target_quot[i]; ++ if (new_quot != last_quot) ++ cpr3_write(thread->ctrl, ++ CPR3_REG_TARGET_QUOT(thread->thread_id, i), ++ new_quot); ++ } ++ ++ thread->last_closed_loop_aggr_corner = thread->aggr_corner; ++ ++ return; ++} ++ ++/** ++ * cpr3_update_vreg_closed_loop_volt() - update the last known settled ++ * closed loop voltage for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @vdd_volt: Last known settled voltage in microvolts for the ++ * VDD supply ++ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register ++ * ++ * Return: none ++ */ ++static void cpr3_update_vreg_closed_loop_volt(struct cpr3_regulator *vreg, ++ int vdd_volt, u32 reg_last_measurement) ++{ ++ bool step_dn, step_up, aggr_step_up, aggr_step_dn, aggr_step_mid; ++ bool valid, pd_valid, saw_error; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ u32 id; ++ ++ if (vreg->last_closed_loop_corner == CPR3_REGULATOR_CORNER_INVALID) ++ return; ++ else ++ corner = &vreg->corner[vreg->last_closed_loop_corner]; ++ ++ if (vreg->thread->last_closed_loop_aggr_corner.ro_mask ++ == CPR3_RO_MASK || !vreg->aggregated) { ++ return; ++ } else if (!ctrl->cpr_enabled || !ctrl->last_corner_was_closed_loop) { ++ return; ++ } else if (ctrl->thread_count == 1 ++ && vdd_volt >= corner->floor_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, ++ corner->floor_volt); ++ return; ++ } else if (!ctrl->supports_hw_closed_loop) { ++ return; ++ } else if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPR3) { ++ corner->last_volt = vdd_volt; ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, ++ corner->floor_volt); ++ return; ++ } ++ ++ /* CPR clocks are on and HW closed loop is supported */ ++ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); ++ if (!valid) { ++ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X valid bit not set\n", ++ reg_last_measurement); ++ return; ++ } ++ ++ id = vreg->thread->thread_id; ++ ++ step_dn ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_DN(id)); ++ step_up ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_UP(id)); ++ aggr_step_dn = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_DN); ++ aggr_step_mid ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_MID); ++ aggr_step_up = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_UP); ++ saw_error = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_SAW_ERROR); ++ pd_valid ++ = !((((reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) ++ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT) ++ & vreg->pd_bypass_mask) == vreg->pd_bypass_mask); ++ ++ if (!pd_valid) { ++ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X, all power domains bypassed\n", ++ reg_last_measurement); ++ return; ++ } else if (step_dn && step_up) { ++ cpr3_err(vreg, "both up and down status bits set, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ reg_last_measurement); ++ return; ++ } else if (aggr_step_dn && step_dn && vdd_volt < corner->last_volt ++ && vdd_volt >= corner->floor_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (aggr_step_up && step_up && vdd_volt > corner->last_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (aggr_step_mid ++ && vdd_volt >= corner->floor_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (saw_error && (vdd_volt == corner->ceiling_volt ++ || vdd_volt == corner->floor_volt)) { ++ corner->last_volt = vdd_volt; ++ } else { ++ cpr3_debug(vreg, "last_volt not updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, vdd_volt=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, corner->floor_volt, ++ vdd_volt, reg_last_measurement); ++ return; ++ } ++ ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, corner->ceiling_volt, ++ vreg->last_closed_loop_corner, corner->floor_volt, ++ reg_last_measurement); ++} ++ ++/** ++ * cpr3_regulator_mem_acc_bhs_used() - determines if mem-acc regulators powered ++ * through a BHS are associated with the CPR3 controller or any of ++ * the CPR3 regulators it controls. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function determines if the CPR3 controller or any of its CPR3 regulators ++ * need to manage mem-acc regulators that are currently powered through a BHS ++ * and whose corner selection is based upon a particular voltage threshold. ++ * ++ * Return: true or false ++ */ ++static bool cpr3_regulator_mem_acc_bhs_used(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j; ++ ++ if (!ctrl->mem_acc_threshold_volt) ++ return false; ++ ++ if (ctrl->mem_acc_regulator) ++ return true; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (vreg->mem_acc_regulator) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/** ++ * cpr3_regulator_config_bhs_mem_acc() - configure the mem-acc regulator ++ * settings for hardware blocks currently powered through the BHS. ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function programs the mem-acc regulator corners for CPR3 regulators ++ * whose LDO regulators are in bypassed state. The function also handles ++ * CPR3 controllers which utilize mem-acc regulators that operate independently ++ * from the LDO hardware and that must be programmed when the VDD supply ++ * crosses a particular voltage threshold. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_config_bhs_mem_acc(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j, rc, mem_acc_corn, safe_volt; ++ int mem_acc_volt = ctrl->mem_acc_threshold_volt; ++ int ref_volt; ++ ++ if (!cpr3_regulator_mem_acc_bhs_used(ctrl)) ++ return 0; ++ ++ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : ++ new_volt; ++ ++ if (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || ++ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))) { ++ if (ref_volt < *last_volt) ++ safe_volt = max(mem_acc_volt, aggr_corner->last_volt); ++ else ++ safe_volt = max(mem_acc_volt, *last_volt); ++ ++ rc = regulator_set_voltage(ctrl->vdd_regulator, safe_volt, ++ new_volt < *last_volt ? ++ ctrl->aggr_corner.ceiling_volt : ++ new_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ safe_volt, rc); ++ return rc; ++ } ++ ++ *last_volt = safe_volt; ++ ++ mem_acc_corn = ref_volt < mem_acc_volt ? ++ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] : ++ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER]; ++ ++ if (ctrl->mem_acc_regulator) { ++ rc = regulator_set_voltage(ctrl->mem_acc_regulator, ++ mem_acc_corn, mem_acc_corn); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ mem_acc_corn, rc); ++ return rc; ++ } ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (!vreg->mem_acc_regulator) ++ continue; ++ ++ rc = regulator_set_voltage( ++ vreg->mem_acc_regulator, mem_acc_corn, ++ mem_acc_corn); ++ if (rc) { ++ cpr3_err(vreg, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ mem_acc_corn, rc); ++ return rc; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_switch_apm_mode() - switch the mode of the APM controller ++ * associated with a given CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function requests a switch of the APM mode while guaranteeing ++ * any LDO regulator hardware requirements are satisfied. The function must ++ * be called once it is known a new VDD supply setpoint crosses the APM ++ * voltage threshold. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_switch_apm_mode(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct regulator *vdd = ctrl->vdd_regulator; ++ int apm_volt = ctrl->apm_threshold_volt; ++ int orig_last_volt = *last_volt; ++ int rc; ++ ++ rc = regulator_set_voltage(vdd, apm_volt, apm_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ apm_volt, rc); ++ return rc; ++ } ++ ++ *last_volt = apm_volt; ++ ++ rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt ++ ? ctrl->apm_high_supply : ctrl->apm_low_supply); ++ if (rc) { ++ cpr3_err(ctrl, "APM switch failed, rc=%d\n", rc); ++ /* Roll back the voltage. */ ++ regulator_set_voltage(vdd, orig_last_volt, INT_MAX); ++ *last_volt = orig_last_volt; ++ return rc; ++ } ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_config_voltage_crossings() - configure APM and mem-acc ++ * settings depending upon a new VDD supply setpoint ++ * ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function handles the APM and mem-acc regulator reconfiguration if ++ * the new VDD supply voltage will result in crossing their respective voltage ++ * thresholds. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_config_voltage_crossings(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ bool apm_crossing = false, mem_acc_crossing = false; ++ bool mem_acc_bhs_used; ++ int apm_volt = ctrl->apm_threshold_volt; ++ int mem_acc_volt = ctrl->mem_acc_threshold_volt; ++ int ref_volt, rc; ++ ++ if (ctrl->apm && apm_volt > 0 ++ && ((*last_volt < apm_volt && apm_volt <= new_volt) ++ || (*last_volt >= apm_volt && apm_volt > new_volt))) ++ apm_crossing = true; ++ ++ mem_acc_bhs_used = cpr3_regulator_mem_acc_bhs_used(ctrl); ++ ++ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : ++ new_volt; ++ ++ if (mem_acc_bhs_used && ++ (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || ++ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt)))) ++ mem_acc_crossing = true; ++ ++ if (apm_crossing && mem_acc_crossing) { ++ if ((new_volt < *last_volt && apm_volt >= mem_acc_volt) || ++ (new_volt >= *last_volt && apm_volt < mem_acc_volt)) { ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, ++ last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ } else { ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, ++ last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ } ++ } else if (apm_crossing) { ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ } else if (mem_acc_crossing) { ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_config_mem_acc() - configure the corner of the mem-acc ++ * regulator associated with the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_config_mem_acc(struct cpr3_controller *ctrl, ++ struct cpr3_corner *aggr_corner) ++{ ++ int rc; ++ ++ if (ctrl->mem_acc_regulator && aggr_corner->mem_acc_volt) { ++ rc = regulator_set_voltage(ctrl->mem_acc_regulator, ++ aggr_corner->mem_acc_volt, ++ aggr_corner->mem_acc_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ aggr_corner->mem_acc_volt, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_scale_vdd_voltage() - scale the CPR controlled VDD supply ++ * voltage to the new level while satisfying any other hardware ++ * requirements ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to end ++ * up at ++ * @last_volt: Last known voltage in microvolts for the VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function scales the CPR controlled VDD supply voltage from its ++ * current level to the new voltage that is specified. If the supply is ++ * configured to use the APM and the APM threshold is crossed as a result of ++ * the voltage scaling, then this function also stops at the APM threshold, ++ * switches the APM source, and finally sets the final new voltage. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, ++ int new_volt, int last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct regulator *vdd = ctrl->vdd_regulator; ++ int rc; ++ ++ if (new_volt < last_volt) { ++ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); ++ if (rc) ++ return rc; ++ } else { ++ /* Increasing VDD voltage */ ++ if (ctrl->system_regulator) { ++ rc = regulator_set_voltage(ctrl->system_regulator, ++ aggr_corner->system_volt, INT_MAX); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", ++ aggr_corner->system_volt, rc); ++ return rc; ++ } ++ } ++ } ++ ++ rc = cpr3_regulator_config_voltage_crossings(ctrl, new_volt, &last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to handle voltage threshold crossing configurations, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Subtract a small amount from the min_uV parameter so that the ++ * set voltage request is not dropped by the framework due to being ++ * duplicate. This is needed in order to switch from hardware ++ * closed-loop to open-loop successfully. ++ */ ++ rc = regulator_set_voltage(vdd, new_volt - (ctrl->cpr_enabled ? 0 : 1), ++ aggr_corner->ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ new_volt, rc); ++ return rc; ++ } ++ ++ if (new_volt == last_volt && ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ /* ++ * CPR4 features enforce voltage reprogramming when the last ++ * set voltage and new set voltage are same. This way, we can ++ * ensure that SAW PMIC STATUS register is updated with newly ++ * programmed voltage. ++ */ ++ rc = regulator_sync_voltage(vdd); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_sync_voltage(vdd) == %d failed, rc=%d\n", ++ new_volt, rc); ++ return rc; ++ } ++ } ++ ++ if (new_volt >= last_volt) { ++ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); ++ if (rc) ++ return rc; ++ } else { ++ /* Decreasing VDD voltage */ ++ if (ctrl->system_regulator) { ++ rc = regulator_set_voltage(ctrl->system_regulator, ++ aggr_corner->system_volt, INT_MAX); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", ++ aggr_corner->system_volt, rc); ++ return rc; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_get_dynamic_floor_volt() - returns the current dynamic floor ++ * voltage based upon static configurations and the state of all ++ * power domains during the last CPR measurement ++ * @ctrl: Pointer to the CPR3 controller ++ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register ++ * ++ * When using HW closed-loop, the dynamic floor voltage is always returned ++ * regardless of the current state of the power domains. ++ * ++ * Return: dynamic floor voltage in microvolts or 0 if dynamic floor is not ++ * currently required ++ */ ++static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl, ++ u32 reg_last_measurement) ++{ ++ int dynamic_floor_volt = 0; ++ struct cpr3_regulator *vreg; ++ bool valid, pd_valid; ++ u32 bypass_bits; ++ int i, j; ++ ++ if (!ctrl->supports_hw_closed_loop) ++ return 0; ++ ++ if (likely(!ctrl->use_hw_closed_loop)) { ++ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); ++ bypass_bits ++ = (reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) ++ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT; ++ } else { ++ /* ++ * Ensure that the dynamic floor voltage is always used for ++ * HW closed-loop since the conditions below cannot be evaluated ++ * after each CPR measurement. ++ */ ++ valid = false; ++ bypass_bits = 0; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (!vreg->uses_dynamic_floor) ++ continue; ++ ++ pd_valid = !((bypass_bits & vreg->pd_bypass_mask) ++ == vreg->pd_bypass_mask); ++ ++ if (!valid || !pd_valid) ++ dynamic_floor_volt = max(dynamic_floor_volt, ++ vreg->corner[ ++ vreg->dynamic_floor_corner].last_volt); ++ } ++ } ++ ++ return dynamic_floor_volt; ++} ++ ++/** ++ * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in ++ * microvolts that can result from different operating conditions ++ * for the specified sdelta struct ++ * @sdelta: Pointer to the sdelta structure ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: voltage difference between the highest and lowest adjustments if ++ * sdelta and sdelta->table are valid, else 0. ++ */ ++static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta, ++ int step_volt) ++{ ++ int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN; ++ ++ if (!sdelta || !sdelta->table) ++ return 0; ++ ++ for (i = 0; i < sdelta->max_core_count; i++) { ++ for (j = 0; j < sdelta->temp_band_count; j++) { ++ index = i * sdelta->temp_band_count + j; ++ sdelta_min = min(sdelta_min, sdelta->table[index]); ++ sdelta_max = max(sdelta_max, sdelta->table[index]); ++ } ++ } ++ ++ return (sdelta_max - sdelta_min) * step_volt; ++} ++ ++/** ++ * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current ++ * aggregated corner and current corner of a given regulator ++ * and adjust the sdelta strucuture data of aggregate corner. ++ * @aggr_corner: Pointer to accumulated aggregated corner which ++ * is both an input and an output ++ * @corner: Pointer to the corner to be aggregated with ++ * aggr_corner ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_aggregate_sdelta( ++ struct cpr3_corner *aggr_corner, ++ const struct cpr3_corner *corner, int step_volt) ++{ ++ struct cpr4_sdelta *aggr_sdelta, *sdelta; ++ int aggr_core_count, core_count, temp_band_count; ++ u32 aggr_index, index; ++ int i, j, sdelta_size, cap_steps, adjust_sdelta; ++ ++ aggr_sdelta = aggr_corner->sdelta; ++ sdelta = corner->sdelta; ++ ++ if (aggr_corner->open_loop_volt < corner->open_loop_volt) { ++ /* ++ * Found the new dominant regulator as its open-loop requirement ++ * is higher than previous dominant regulator. Calculate cap ++ * voltage to limit the SDELTA values to make sure the runtime ++ * (Core-count/temp) adjustments do not violate other ++ * regulators' voltage requirements. Use cpr4_sdelta values of ++ * new dominant regulator. ++ */ ++ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, ++ (corner->open_loop_volt - ++ aggr_corner->open_loop_volt)); ++ ++ /* Clear old data in the sdelta table */ ++ sdelta_size = aggr_sdelta->max_core_count ++ * aggr_sdelta->temp_band_count; ++ ++ if (aggr_sdelta->allow_core_count_adj ++ || aggr_sdelta->allow_temp_adj) ++ memset(aggr_sdelta->table, 0, sdelta_size ++ * sizeof(*aggr_sdelta->table)); ++ ++ if (sdelta->allow_temp_adj || sdelta->allow_core_count_adj) { ++ /* Copy new data in sdelta table */ ++ sdelta_size = sdelta->max_core_count ++ * sdelta->temp_band_count; ++ if (sdelta->table) ++ memcpy(aggr_sdelta->table, sdelta->table, ++ sdelta_size * sizeof(*sdelta->table)); ++ } ++ ++ if (sdelta->allow_boost) { ++ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, ++ sdelta->temp_band_count ++ * sizeof(*sdelta->boost_table)); ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } else if (aggr_sdelta->allow_boost) { ++ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { ++ adjust_sdelta = (corner->open_loop_volt ++ - aggr_corner->open_loop_volt) ++ / step_volt; ++ aggr_sdelta->boost_table[i] += adjust_sdelta; ++ aggr_sdelta->boost_table[i] ++ = min(aggr_sdelta->boost_table[i], 0); ++ } ++ } ++ ++ aggr_corner->open_loop_volt = corner->open_loop_volt; ++ aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj; ++ aggr_sdelta->allow_core_count_adj ++ = sdelta->allow_core_count_adj; ++ aggr_sdelta->max_core_count = sdelta->max_core_count; ++ aggr_sdelta->temp_band_count = sdelta->temp_band_count; ++ } else if (aggr_corner->open_loop_volt > corner->open_loop_volt) { ++ /* ++ * Adjust the cap voltage if the open-loop requirement of new ++ * regulator is the next highest. ++ */ ++ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, ++ (aggr_corner->open_loop_volt ++ - corner->open_loop_volt)); ++ ++ if (sdelta->allow_boost) { ++ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { ++ adjust_sdelta = (aggr_corner->open_loop_volt ++ - corner->open_loop_volt) ++ / step_volt; ++ aggr_sdelta->boost_table[i] = ++ sdelta->boost_table[i] + adjust_sdelta; ++ aggr_sdelta->boost_table[i] ++ = min(aggr_sdelta->boost_table[i], 0); ++ } ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } ++ } else { ++ /* ++ * Found another dominant regulator with same open-loop ++ * requirement. Make cap voltage to '0'. Disable core-count ++ * adjustments as we couldn't support for both regulators. ++ * Keep enable temp based adjustments if enabled for both ++ * regulators and choose mininum margin adjustment values ++ * between them. ++ */ ++ aggr_sdelta->cap_volt = 0; ++ aggr_sdelta->allow_core_count_adj = false; ++ ++ if (aggr_sdelta->allow_temp_adj ++ && sdelta->allow_temp_adj) { ++ aggr_core_count = aggr_sdelta->max_core_count - 1; ++ core_count = sdelta->max_core_count - 1; ++ temp_band_count = sdelta->temp_band_count; ++ for (j = 0; j < temp_band_count; j++) { ++ aggr_index = aggr_core_count * temp_band_count ++ + j; ++ index = core_count * temp_band_count + j; ++ aggr_sdelta->table[aggr_index] = ++ min(aggr_sdelta->table[aggr_index], ++ sdelta->table[index]); ++ } ++ } else { ++ aggr_sdelta->allow_temp_adj = false; ++ } ++ ++ if (sdelta->allow_boost) { ++ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, ++ sdelta->temp_band_count ++ * sizeof(*sdelta->boost_table)); ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } ++ } ++ ++ /* Keep non-dominant clients boost enable state */ ++ aggr_sdelta->allow_boost |= sdelta->allow_boost; ++ if (aggr_sdelta->allow_boost) ++ aggr_sdelta->allow_core_count_adj = false; ++ ++ if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) { ++ core_count = aggr_sdelta->max_core_count; ++ temp_band_count = aggr_sdelta->temp_band_count; ++ /* ++ * Convert cap voltage from uV to PMIC steps and use to limit ++ * sdelta margin adjustments. ++ */ ++ cap_steps = aggr_sdelta->cap_volt / step_volt; ++ for (i = 0; i < core_count; i++) ++ for (j = 0; j < temp_band_count; j++) { ++ index = i * temp_band_count + j; ++ aggr_sdelta->table[index] = ++ min(aggr_sdelta->table[index], ++ cap_steps); ++ } ++ } ++} ++ ++/** ++ * cpr3_regulator_aggregate_corners() - aggregate two corners together ++ * @aggr_corner: Pointer to accumulated aggregated corner which ++ * is both an input and an output ++ * @corner: Pointer to the corner to be aggregated with ++ * aggr_corner ++ * @aggr_quot: Flag indicating that target quotients should be ++ * aggregated as well. ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner, ++ const struct cpr3_corner *corner, bool aggr_quot, ++ int step_volt) ++{ ++ int i; ++ ++ aggr_corner->ceiling_volt ++ = max(aggr_corner->ceiling_volt, corner->ceiling_volt); ++ aggr_corner->floor_volt ++ = max(aggr_corner->floor_volt, corner->floor_volt); ++ aggr_corner->last_volt ++ = max(aggr_corner->last_volt, corner->last_volt); ++ aggr_corner->system_volt ++ = max(aggr_corner->system_volt, corner->system_volt); ++ aggr_corner->mem_acc_volt ++ = max(aggr_corner->mem_acc_volt, corner->mem_acc_volt); ++ aggr_corner->irq_en |= corner->irq_en; ++ aggr_corner->use_open_loop |= corner->use_open_loop; ++ ++ if (aggr_quot) { ++ aggr_corner->ro_mask &= corner->ro_mask; ++ ++ for (i = 0; i < CPR3_RO_COUNT; i++) ++ aggr_corner->target_quot[i] ++ = max(aggr_corner->target_quot[i], ++ corner->target_quot[i]); ++ } ++ ++ if (aggr_corner->sdelta && corner->sdelta ++ && (aggr_corner->sdelta->table ++ || aggr_corner->sdelta->boost_table)) { ++ cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt); ++ } else { ++ aggr_corner->open_loop_volt ++ = max(aggr_corner->open_loop_volt, ++ corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller ++ * to reflect the corners used by all CPR3 regulators as well as ++ * the CPR operating mode ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function aggregates the CPR parameters for all CPR3 regulators ++ * associated with the VDD supply. Upon success, it sets the aggregated last ++ * known good voltage. ++ * ++ * The VDD supply voltage will not be physically configured unless this ++ * condition is met by at least one of the regulators of the controller: ++ * regulator->vreg_enabled == true && ++ * regulator->current_corner != CPR3_REGULATOR_CORNER_INVALID ++ * ++ * CPR registers for the controller and each thread are updated as long as ++ * ctrl->cpr_enabled == true. ++ * ++ * Note, CPR3 controller lock must be held by the caller. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_corner aggr_corner = {}; ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ struct cpr4_sdelta *sdelta; ++ bool valid = false; ++ bool thread_valid; ++ int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt; ++ u32 reg_last_measurement = 0, sdelta_size; ++ int *sdelta_table, *boost_table; ++ ++ last_corner_volt = 0; ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ vdd_volt = regulator_get_voltage(ctrl->vdd_regulator); ++ if (vdd_volt < 0) { ++ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", ++ vdd_volt); ++ return vdd_volt; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ /* ++ * Save aggregated corner open-loop voltage which was programmed ++ * during last corner switch which is used when programming new ++ * aggregated corner open-loop voltage. ++ */ ++ last_corner_volt = ctrl->aggr_corner.open_loop_volt; ++ } ++ ++ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop && ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ reg_last_measurement ++ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); ++ ++ aggr_corner.sdelta = ctrl->aggr_corner.sdelta; ++ if (aggr_corner.sdelta) { ++ sdelta = aggr_corner.sdelta; ++ sdelta_table = sdelta->table; ++ if (sdelta_table) { ++ sdelta_size = sdelta->max_core_count * ++ sdelta->temp_band_count; ++ memset(sdelta_table, 0, sdelta_size ++ * sizeof(*sdelta_table)); ++ } ++ ++ boost_table = sdelta->boost_table; ++ if (boost_table) ++ memset(boost_table, 0, sdelta->temp_band_count ++ * sizeof(*boost_table)); ++ ++ memset(sdelta, 0, sizeof(*sdelta)); ++ sdelta->table = sdelta_table; ++ sdelta->cap_volt = INT_MAX; ++ sdelta->boost_table = boost_table; ++ } ++ ++ /* Aggregate the requests of all threads */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ thread_valid = false; ++ ++ sdelta = thread->aggr_corner.sdelta; ++ if (sdelta) { ++ sdelta_table = sdelta->table; ++ if (sdelta_table) { ++ sdelta_size = sdelta->max_core_count * ++ sdelta->temp_band_count; ++ memset(sdelta_table, 0, sdelta_size ++ * sizeof(*sdelta_table)); ++ } ++ ++ boost_table = sdelta->boost_table; ++ if (boost_table) ++ memset(boost_table, 0, sdelta->temp_band_count ++ * sizeof(*boost_table)); ++ ++ memset(sdelta, 0, sizeof(*sdelta)); ++ sdelta->table = sdelta_table; ++ sdelta->cap_volt = INT_MAX; ++ sdelta->boost_table = boost_table; ++ } ++ ++ memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner)); ++ thread->aggr_corner.sdelta = sdelta; ++ thread->aggr_corner.ro_mask = CPR3_RO_MASK; ++ ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ ++ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop) ++ cpr3_update_vreg_closed_loop_volt(vreg, ++ vdd_volt, reg_last_measurement); ++ ++ if (!vreg->vreg_enabled ++ || vreg->current_corner ++ == CPR3_REGULATOR_CORNER_INVALID) { ++ /* Cannot participate in aggregation. */ ++ vreg->aggregated = false; ++ continue; ++ } else { ++ vreg->aggregated = true; ++ thread_valid = true; ++ } ++ ++ cpr3_regulator_aggregate_corners(&thread->aggr_corner, ++ &vreg->corner[vreg->current_corner], ++ true, ctrl->step_volt); ++ } ++ ++ valid |= thread_valid; ++ ++ if (thread_valid) ++ cpr3_regulator_aggregate_corners(&aggr_corner, ++ &thread->aggr_corner, ++ false, ctrl->step_volt); ++ } ++ ++ if (valid && ctrl->cpr_allowed_hw && ctrl->cpr_allowed_sw) { ++ rc = cpr3_closed_loop_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); ++ return rc; ++ } ++ } else { ++ rc = cpr3_closed_loop_disable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ /* No threads are enabled with a valid corner so exit. */ ++ if (!valid) ++ return 0; ++ ++ /* ++ * When using CPR hardware closed-loop, the voltage may vary anywhere ++ * between the floor and ceiling voltage without software notification. ++ * Therefore, it is required that the floor to ceiling range for the ++ * aggregated corner not intersect the APM threshold voltage. Adjust ++ * the floor to ceiling range if this requirement is violated. ++ * ++ * The following algorithm is applied in the case that ++ * floor < threshold <= ceiling: ++ * if open_loop >= threshold - adj, then floor = threshold ++ * else ceiling = threshold - step ++ * where adj = an adjustment factor to ensure sufficient voltage margin ++ * and step = VDD output step size ++ * ++ * The open-loop and last known voltages are also bounded by the new ++ * floor or ceiling value as needed. ++ */ ++ if (ctrl->use_hw_closed_loop ++ && aggr_corner.ceiling_volt >= ctrl->apm_threshold_volt ++ && aggr_corner.floor_volt < ctrl->apm_threshold_volt) { ++ ++ if (aggr_corner.open_loop_volt ++ >= ctrl->apm_threshold_volt - ctrl->apm_adj_volt) ++ aggr_corner.floor_volt = ctrl->apm_threshold_volt; ++ else ++ aggr_corner.ceiling_volt ++ = ctrl->apm_threshold_volt - ctrl->step_volt; ++ ++ aggr_corner.last_volt ++ = max(aggr_corner.last_volt, aggr_corner.floor_volt); ++ aggr_corner.last_volt ++ = min(aggr_corner.last_volt, aggr_corner.ceiling_volt); ++ aggr_corner.open_loop_volt ++ = max(aggr_corner.open_loop_volt, aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt ++ = min(aggr_corner.open_loop_volt, aggr_corner.ceiling_volt); ++ } ++ ++ if (ctrl->use_hw_closed_loop ++ && aggr_corner.ceiling_volt >= ctrl->mem_acc_threshold_volt ++ && aggr_corner.floor_volt < ctrl->mem_acc_threshold_volt) { ++ aggr_corner.floor_volt = ctrl->mem_acc_threshold_volt; ++ aggr_corner.last_volt = max(aggr_corner.last_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt = max(aggr_corner.open_loop_volt, ++ aggr_corner.floor_volt); ++ } ++ ++ if (ctrl->use_hw_closed_loop) { ++ dynamic_floor_volt ++ = cpr3_regulator_get_dynamic_floor_volt(ctrl, ++ reg_last_measurement); ++ if (aggr_corner.floor_volt < dynamic_floor_volt) { ++ aggr_corner.floor_volt = dynamic_floor_volt; ++ aggr_corner.last_volt = max(aggr_corner.last_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt ++ = max(aggr_corner.open_loop_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.ceiling_volt = max(aggr_corner.ceiling_volt, ++ aggr_corner.floor_volt); ++ } ++ } ++ ++ if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { ++ /* ++ * Always program open-loop voltage for CPR4 controllers which ++ * support hardware closed-loop. Storing the last closed loop ++ * voltage in corner structure can still help with debugging. ++ */ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ new_volt = aggr_corner.last_volt; ++ else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->supports_hw_closed_loop) ++ new_volt = aggr_corner.open_loop_volt; ++ else ++ new_volt = min(aggr_corner.last_volt + ++ cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta, ++ ctrl->step_volt), ++ aggr_corner.ceiling_volt); ++ ++ aggr_corner.last_volt = new_volt; ++ } else { ++ new_volt = aggr_corner.open_loop_volt; ++ aggr_corner.last_volt = aggr_corner.open_loop_volt; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->supports_hw_closed_loop) { ++ /* ++ * Store last aggregated corner open-loop voltage in vdd_volt ++ * which is used when programming current aggregated corner ++ * required voltage. ++ */ ++ vdd_volt = last_corner_volt; ++ } ++ ++ cpr3_debug(ctrl, "setting new voltage=%d uV\n", new_volt); ++ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, ++ vdd_volt, &aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "vdd voltage scaling failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ /* Only update registers if CPR is enabled. */ ++ if (ctrl->cpr_enabled) { ++ if (ctrl->use_hw_closed_loop) { ++ /* Hardware closed-loop */ ++ ++ /* Set ceiling and floor limits in hardware */ ++ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, ++ aggr_corner.floor_volt, ++ aggr_corner.ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ /* Software closed-loop */ ++ ++ /* ++ * Disable UP or DOWN interrupts when at ceiling or ++ * floor respectively. ++ */ ++ if (new_volt == aggr_corner.floor_volt) ++ aggr_corner.irq_en &= ~CPR3_IRQ_DOWN; ++ if (new_volt == aggr_corner.ceiling_volt) ++ aggr_corner.irq_en &= ~CPR3_IRQ_UP; ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, aggr_corner.irq_en); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ cpr3_regulator_set_target_quot(&ctrl->thread[i]); ++ ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (vreg->vreg_enabled) ++ vreg->last_closed_loop_corner ++ = vreg->current_corner; ++ } ++ } ++ ++ if (ctrl->proc_clock_throttle) { ++ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt ++ && (ctrl->use_hw_closed_loop ++ || new_volt < aggr_corner.ceiling_volt)) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ ctrl->proc_clock_throttle); ++ else ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ } ++ ++ /* ++ * Ensure that all CPR register writes complete before ++ * re-enabling CPR loop operation. ++ */ ++ wmb(); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->vdd_limit_regulator) { ++ /* Set ceiling and floor limits in hardware */ ++ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, ++ aggr_corner.floor_volt, ++ aggr_corner.ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ ctrl->aggr_corner = aggr_corner; ++ ++ if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj ++ || ctrl->allow_boost) { ++ rc = cpr3_controller_program_sdelta(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ /* ++ * Only enable the CPR controller if it is possible to set more than ++ * one vdd-supply voltage. ++ */ ++ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt && ++ !aggr_corner.use_open_loop) ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled; ++ cpr3_debug(ctrl, "CPR configuration updated\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_wait_for_idle() - wait for the CPR controller to no longer be ++ * busy ++ * @ctrl: Pointer to the CPR3 controller ++ * @max_wait_ns: Max wait time in nanoseconds ++ * ++ * Return: 0 on success or -ETIMEDOUT if the controller was still busy after ++ * the maximum delay time ++ */ ++static int cpr3_regulator_wait_for_idle(struct cpr3_controller *ctrl, ++ s64 max_wait_ns) ++{ ++ ktime_t start, end; ++ s64 time_ns; ++ u32 reg; ++ ++ /* ++ * Ensure that all previous CPR register writes have completed before ++ * checking the status register. ++ */ ++ mb(); ++ ++ start = ktime_get(); ++ do { ++ end = ktime_get(); ++ time_ns = ktime_to_ns(ktime_sub(end, start)); ++ if (time_ns > max_wait_ns) { ++ cpr3_err(ctrl, "CPR controller still busy after %lld us\n", ++ div_s64(time_ns, 1000)); ++ return -ETIMEDOUT; ++ } ++ usleep_range(50, 100); ++ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ } while (reg & CPR3_CPR_STATUS_BUSY_MASK); ++ ++ return 0; ++} ++ ++/** ++ * cmp_int() - int comparison function to be passed into the sort() function ++ * which leads to ascending sorting ++ * @a: First int value ++ * @b: Second int value ++ * ++ * Return: >0 if a > b, 0 if a == b, <0 if a < b ++ */ ++static int cmp_int(const void *a, const void *b) ++{ ++ return *(int *)a - *(int *)b; ++} ++ ++/** ++ * cpr3_regulator_measure_aging() - measure the quotient difference for the ++ * specified CPR aging sensor ++ * @ctrl: Pointer to the CPR3 controller ++ * @aging_sensor: Aging sensor to measure ++ * ++ * Note that vdd-supply must be configured to the aging reference voltage before ++ * calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, ++ struct cpr3_aging_sensor_info *aging_sensor) ++{ ++ u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max; ++ u32 quot_min_scaled, quot_max_scaled; ++ u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore; ++ u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0; ++ int quot_delta, quot_delta_scaled, quot_delta_scaled_sum; ++ int *quot_delta_results; ++ int rc, rc2, i, aging_measurement_count, filtered_count; ++ bool is_aging_measurement; ++ ++ quot_delta_results = kcalloc(CPR3_AGING_MEASUREMENT_ITERATIONS, ++ sizeof(*quot_delta_results), GFP_KERNEL); ++ if (!quot_delta_results) ++ return -ENOMEM; ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ kfree(quot_delta_results); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ /* Enable up, down, and mid CPR interrupts */ ++ irq_restore = cpr3_read(ctrl, CPR3_REG_IRQ_EN); ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); ++ ++ /* Ensure that the aging sensor is assigned to CPR thread 0 */ ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), 0); ++ ++ /* Switch from HW to SW closed-loop if necessary */ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ } ++ ++ /* Configure the GCNT for RO0 and RO1 that are used for aging */ ++ gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0)); ++ gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1)); ++ gcnt_ref = cpr3_regulator_get_gcnt(ctrl); ++ gcnt = gcnt_ref * 3 / 2; ++ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt); ++ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt); ++ ++ /* Unmask all RO's */ ++ ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0)); ++ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0); ++ ++ /* ++ * Mask all sensors except for the one to measure and bypass all ++ * sensors in collapsible domains. ++ */ ++ for (i = 0; i <= ctrl->sensor_count / 32; i++) { ++ mask = GENMASK(min(31, ctrl->sensor_count - i * 32), 0); ++ if (aging_sensor->sensor_id / 32 >= i ++ && aging_sensor->sensor_id / 32 < (i + 1)) ++ mask &= ~BIT(aging_sensor->sensor_id % 32); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), mask); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), ++ aging_sensor->bypass_mask[i]); ++ } ++ ++ /* Set CPR loop delays to 0 us */ ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cont_dly_restore = cpr3_read(ctrl, CPR3_REG_CPR_TIMER_MID_CONT); ++ up_down_dly_restore = cpr3_read(ctrl, ++ CPR3_REG_CPR_TIMER_UP_DN_CONT); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, 0); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, 0); ++ } else { ++ cont_dly_restore = cpr3_read(ctrl, ++ CPR3_REG_CPR_TIMER_AUTO_CONT); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, 0); ++ } ++ ++ /* Set count mode to all-at-once min with no repeat */ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, ++ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN ++ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); ++ ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ rc = cpr3_regulator_wait_for_idle(ctrl, ++ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); ++ if (rc) ++ goto cleanup; ++ ++ /* Set count mode to all-at-once aging */ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, CPR3_CPR_CTL_COUNT_MODE_MASK, ++ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE ++ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); ++ ++ aging_measurement_count = 0; ++ for (i = 0; i < CPR3_AGING_MEASUREMENT_ITERATIONS; i++) { ++ /* Send CONT_NACK */ ++ cpr3_write(ctrl, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK); ++ ++ rc = cpr3_regulator_wait_for_idle(ctrl, ++ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); ++ if (rc) ++ goto cleanup; ++ ++ /* Check for PAGE_IS_AGE flag in status register */ ++ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ is_aging_measurement ++ = reg & CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK; ++ ++ /* Read CPR measurement results */ ++ result = cpr3_read(ctrl, CPR3_REG_RESULT1(0)); ++ quot_min = (result & CPR3_RESULT1_QUOT_MIN_MASK) ++ >> CPR3_RESULT1_QUOT_MIN_SHIFT; ++ quot_max = (result & CPR3_RESULT1_QUOT_MAX_MASK) ++ >> CPR3_RESULT1_QUOT_MAX_SHIFT; ++ sel_min = (result & CPR3_RESULT1_RO_MIN_MASK) ++ >> CPR3_RESULT1_RO_MIN_SHIFT; ++ sel_max = (result & CPR3_RESULT1_RO_MAX_MASK) ++ >> CPR3_RESULT1_RO_MAX_SHIFT; ++ ++ /* ++ * Scale the quotients so that they are equivalent to the fused ++ * values. This accounts for the difference in measurement ++ * interval times. ++ */ ++ quot_min_scaled = quot_min * (gcnt_ref + 1) / (gcnt + 1); ++ quot_max_scaled = quot_max * (gcnt_ref + 1) / (gcnt + 1); ++ ++ if (sel_max == 1) { ++ quot_delta = quot_max - quot_min; ++ quot_delta_scaled = quot_max_scaled - quot_min_scaled; ++ } else { ++ quot_delta = quot_min - quot_max; ++ quot_delta_scaled = quot_min_scaled - quot_max_scaled; ++ } ++ ++ if (is_aging_measurement) ++ quot_delta_results[aging_measurement_count++] ++ = quot_delta_scaled; ++ ++ cpr3_debug(ctrl, "aging results: page_is_age=%u, sel_min=%u, sel_max=%u, quot_min=%u, quot_max=%u, quot_delta=%d, quot_min_scaled=%u, quot_max_scaled=%u, quot_delta_scaled=%d\n", ++ is_aging_measurement, sel_min, sel_max, quot_min, ++ quot_max, quot_delta, quot_min_scaled, quot_max_scaled, ++ quot_delta_scaled); ++ } ++ ++ filtered_count ++ = aging_measurement_count - CPR3_AGING_MEASUREMENT_FILTER * 2; ++ if (filtered_count > 0) { ++ sort(quot_delta_results, aging_measurement_count, ++ sizeof(*quot_delta_results), cmp_int, NULL); ++ ++ quot_delta_scaled_sum = 0; ++ for (i = 0; i < filtered_count; i++) ++ quot_delta_scaled_sum ++ += quot_delta_results[i ++ + CPR3_AGING_MEASUREMENT_FILTER]; ++ ++ aging_sensor->measured_quot_diff ++ = quot_delta_scaled_sum / filtered_count; ++ cpr3_info(ctrl, "average quotient delta=%d (count=%d)\n", ++ aging_sensor->measured_quot_diff, ++ filtered_count); ++ } else { ++ cpr3_err(ctrl, "%d aging measurements completed after %d iterations\n", ++ aging_measurement_count, ++ CPR3_AGING_MEASUREMENT_ITERATIONS); ++ rc = -EBUSY; ++ } ++ ++cleanup: ++ kfree(quot_delta_results); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc2 = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc2) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc2); ++ rc = rc2; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore); ++ ++ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore); ++ ++ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore); ++ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore); ++ ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly_restore); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, ++ up_down_dly_restore); ++ } else { ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, ++ cont_dly_restore); ++ } ++ ++ for (i = 0; i <= ctrl->sensor_count / 32; i++) { ++ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), 0); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), 0); ++ } ++ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, ++ (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) ++ | (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT)); ++ ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), ++ ctrl->sensor_owner[aging_sensor->sensor_id]); ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as ++ * well as the floor, ceiling, and open-loop voltages for the ++ * regulator by removing the old adjustment and adding the new one ++ * @vreg: Pointer to the CPR3 regulator ++ * @old_adjust_volt: Old aging adjustment voltage in microvolts ++ * @new_adjust_volt: New aging adjustment voltage in microvolts ++ * ++ * Also reset the cached closed loop voltage (last_volt) to equal the open-loop ++ * voltage for each corner. ++ * ++ * Return: None ++ */ ++static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg, ++ int old_adjust_volt, int new_adjust_volt) ++{ ++ unsigned long long temp; ++ int i, j, old_volt, new_volt, rounded_volt; ++ ++ if (!vreg->aging_allowed) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ temp = (unsigned long long)old_adjust_volt ++ * (unsigned long long)vreg->corner[i].aging_derate; ++ do_div(temp, 1000); ++ old_volt = temp; ++ ++ temp = (unsigned long long)new_adjust_volt ++ * (unsigned long long)vreg->corner[i].aging_derate; ++ do_div(temp, 1000); ++ new_volt = temp; ++ ++ old_volt = min(vreg->aging_max_adjust_volt, old_volt); ++ new_volt = min(vreg->aging_max_adjust_volt, new_volt); ++ ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] != 0) { ++ vreg->corner[i].target_quot[j] ++ += cpr3_quot_adjustment( ++ vreg->corner[i].ro_scale[j], ++ new_volt) ++ - cpr3_quot_adjustment( ++ vreg->corner[i].ro_scale[j], ++ old_volt); ++ } ++ } ++ ++ rounded_volt = CPR3_ROUND(new_volt, ++ vreg->thread->ctrl->step_volt); ++ ++ if (!vreg->aging_allow_open_loop_adj) ++ rounded_volt = 0; ++ ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].unaged_ceiling_volt + rounded_volt; ++ vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt, ++ vreg->corner[i].abs_ceiling_volt); ++ vreg->corner[i].floor_volt ++ = vreg->corner[i].unaged_floor_volt + rounded_volt; ++ vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt, ++ vreg->corner[i].ceiling_volt); ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].unaged_open_loop_volt + rounded_volt; ++ vreg->corner[i].open_loop_volt ++ = min(vreg->corner[i].open_loop_volt, ++ vreg->corner[i].ceiling_volt); ++ ++ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; ++ ++ cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n", ++ i, new_volt, rounded_volt); ++ } ++} ++ ++/** ++ * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the ++ * regulators managed by this CPR controller to account for aging ++ * @ctrl: Pointer to the CPR3 controller ++ * @ref_adjust_volt: New aging reference adjustment voltage in microvolts to ++ * apply to all regulators managed by this CPR controller ++ * ++ * The existing aging adjustment as defined by ctrl->aging_ref_adjust_volt is ++ * first removed and then the adjustment is applied. Lastly, the value of ++ * ctrl->aging_ref_adjust_volt is updated to ref_adjust_volt. ++ */ ++static void cpr3_regulator_set_aging_ref_adjustment( ++ struct cpr3_controller *ctrl, int ref_adjust_volt) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ cpr3_regulator_readjust_volt_and_quot(vreg, ++ ctrl->aging_ref_adjust_volt, ref_adjust_volt); ++ } ++ } ++ ++ ctrl->aging_ref_adjust_volt = ref_adjust_volt; ++} ++ ++/** ++ * cpr3_regulator_aging_adjust() - adjust the target quotients for regulators ++ * based on the output of CPR aging sensors ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_regulator *vreg; ++ struct cpr3_corner restore_aging_corner; ++ struct cpr3_corner *corner; ++ int *restore_current_corner; ++ bool *restore_vreg_enabled; ++ int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0; ++ u32 reg; ++ ++ if (!ctrl->aging_required || !ctrl->cpr_enabled ++ || ctrl->aggr_corner.ceiling_volt == 0 ++ || ctrl->aggr_corner.ceiling_volt > ctrl->aging_ref_volt) ++ return 0; ++ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg_count++; ++ ++ if (vreg->aging_allowed && vreg->vreg_enabled ++ && vreg->current_corner > vreg->aging_corner) ++ return 0; ++ } ++ } ++ ++ /* Verify that none of the aging sensors are currently masked. */ ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ id = ctrl->aging_sensor[i].sensor_id; ++ reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id)); ++ if (reg & BIT(id % 32)) ++ return 0; ++ } ++ ++ /* ++ * Verify that the aging possible register (if specified) has an ++ * acceptable value. ++ */ ++ if (ctrl->aging_possible_reg) { ++ reg = readl_relaxed(ctrl->aging_possible_reg); ++ reg &= ctrl->aging_possible_mask; ++ if (reg != ctrl->aging_possible_val) ++ return 0; ++ } ++ ++ restore_current_corner = kcalloc(vreg_count, ++ sizeof(*restore_current_corner), GFP_KERNEL); ++ restore_vreg_enabled = kcalloc(vreg_count, ++ sizeof(*restore_vreg_enabled), GFP_KERNEL); ++ if (!restore_current_corner || !restore_vreg_enabled) { ++ kfree(restore_current_corner); ++ kfree(restore_vreg_enabled); ++ return -ENOMEM; ++ } ++ ++ /* Force all regulators to the aging corner */ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ restore_current_corner[vreg_count] ++ = vreg->current_corner; ++ restore_vreg_enabled[vreg_count] ++ = vreg->vreg_enabled; ++ ++ vreg->current_corner = vreg->aging_corner; ++ vreg->vreg_enabled = true; ++ } ++ } ++ ++ /* Force one of the regulators to require the aging reference voltage */ ++ vreg = &ctrl->thread[0].vreg[0]; ++ corner = &vreg->corner[vreg->current_corner]; ++ restore_aging_corner = *corner; ++ corner->ceiling_volt = ctrl->aging_ref_volt; ++ corner->floor_volt = ctrl->aging_ref_volt; ++ corner->open_loop_volt = ctrl->aging_ref_volt; ++ corner->last_volt = ctrl->aging_ref_volt; ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ /* Set the vdd supply voltage to the aging reference voltage */ ++ rc = _cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to force vdd-supply to the aging reference voltage=%d uV, rc=%d\n", ++ ctrl->aging_ref_volt, rc); ++ goto cleanup; ++ } ++ ++ if (ctrl->aging_vdd_mode) { ++ rc = regulator_set_mode(ctrl->vdd_regulator, ++ ctrl->aging_vdd_mode); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", ++ ctrl->aging_vdd_mode, rc); ++ goto cleanup; ++ } ++ } ++ ++ /* Perform aging measurement on all aging sensors */ ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) { ++ rc = cpr3_regulator_measure_aging(ctrl, ++ &ctrl->aging_sensor[i]); ++ if (!rc) ++ break; ++ } ++ ++ if (!rc) { ++ aging_volt = ++ cpr3_voltage_adjustment( ++ ctrl->aging_sensor[i].ro_scale, ++ ctrl->aging_sensor[i].measured_quot_diff ++ - ctrl->aging_sensor[i].init_quot_diff); ++ max_aging_volt = max(max_aging_volt, aging_volt); ++ } else { ++ cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n", ++ j, rc); ++ ctrl->aging_failed = true; ++ ctrl->aging_required = false; ++ goto cleanup; ++ } ++ } ++ ++cleanup: ++ vreg = &ctrl->thread[0].vreg[0]; ++ vreg->corner[vreg->current_corner] = restore_aging_corner; ++ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg->current_corner ++ = restore_current_corner[vreg_count]; ++ vreg->vreg_enabled = restore_vreg_enabled[vreg_count]; ++ } ++ } ++ ++ kfree(restore_current_corner); ++ kfree(restore_vreg_enabled); ++ ++ /* Adjust the CPR target quotients according to the aging measurement */ ++ if (!rc) { ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt); ++ ++ cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n", ++ ctrl->aging_ref_adjust_volt); ++ ctrl->aging_succeeded = true; ++ ctrl->aging_required = false; ++ } ++ ++ if (ctrl->aging_complete_vdd_mode) { ++ rc = regulator_set_mode(ctrl->vdd_regulator, ++ ctrl->aging_complete_vdd_mode); ++ if (rc) ++ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", ++ ctrl->aging_complete_vdd_mode, rc); ++ } ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ /* ++ * Restore vdd-supply to the voltage before the aging measurement and ++ * restore the CPR3 controller hardware state. ++ */ ++ rc2 = _cpr3_regulator_update_ctrl_state(ctrl); ++ ++ /* Stop last_volt caching on for the next request */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ return rc ? rc : rc2; ++} ++ ++/** ++ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller ++ * to reflect the corners used by all CPR3 regulators as well as ++ * the CPR operating mode and perform aging adjustments if needed ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Note, CPR3 controller lock must be held by the caller. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = _cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) ++ return rc; ++ ++ return cpr3_regulator_aging_adjust(ctrl); ++} ++ ++/** ++ * cpr3_regulator_set_voltage() - set the voltage corner for the CPR3 regulator ++ * associated with the regulator device ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @corner: New voltage corner to set (offset by CPR3_CORNER_OFFSET) ++ * @corner_max: Maximum voltage corner allowed (offset by ++ * CPR3_CORNER_OFFSET) ++ * @selector: Pointer which is filled with the selector value for the ++ * corner ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. The VDD voltage will not be ++ * physically configured until both this function and cpr3_regulator_enable() ++ * are called. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_set_voltage(struct regulator_dev *rdev, ++ int corner, int corner_max, unsigned *selector) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc = 0; ++ int last_corner; ++ ++ corner -= CPR3_CORNER_OFFSET; ++ corner_max -= CPR3_CORNER_OFFSET; ++ *selector = corner; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!vreg->vreg_enabled) { ++ vreg->current_corner = corner; ++ cpr3_debug(vreg, "stored corner=%d\n", corner); ++ goto done; ++ } else if (vreg->current_corner == corner) { ++ goto done; ++ } ++ ++ last_corner = vreg->current_corner; ++ vreg->current_corner = corner; ++ ++ if (vreg->cpr4_regulator_data != NULL) ++ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) ++ vreg->cpr4_regulator_data->mem_acc_funcs->set_mem_acc(rdev); ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ vreg->current_corner = last_corner; ++ } ++ ++ if (vreg->cpr4_regulator_data != NULL) ++ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) ++ vreg->cpr4_regulator_data->mem_acc_funcs->clear_mem_acc(rdev); ++ ++ cpr3_debug(vreg, "set corner=%d\n", corner); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_handle_temp_open_loop_adjustment() - voltage based cold temperature ++ * ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @is_cold: Flag to denote enter/exit cold condition ++ * ++ * This function is adjusts voltage margin based on cold condition ++ * ++ * Return: 0 = success ++ */ ++ ++int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold) ++{ ++ int i ,j, k, rc; ++ struct cpr3_regulator *vreg; ++ ++ mutex_lock(&ctrl->lock); ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ for (k = 0; k < vreg->corner_count; k++) { ++ vreg->corner[k].open_loop_volt = is_cold ? ++ vreg->corner[k].cold_temp_open_loop_volt : ++ vreg->corner[k].normal_temp_open_loop_volt; ++ } ++ } ++ } ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_get_voltage() - get the voltage corner for the CPR3 regulator ++ * associated with the regulator device ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: voltage corner value offset by CPR3_CORNER_OFFSET ++ */ ++static int cpr3_regulator_get_voltage(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if (vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID) ++ return CPR3_CORNER_OFFSET; ++ else ++ return vreg->current_corner + CPR3_CORNER_OFFSET; ++} ++ ++/** ++ * cpr3_regulator_list_voltage() - return the voltage corner mapped to the ++ * specified selector ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @selector: Regulator selector ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: voltage corner value offset by CPR3_CORNER_OFFSET ++ */ ++static int cpr3_regulator_list_voltage(struct regulator_dev *rdev, ++ unsigned selector) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if (selector < vreg->corner_count) ++ return selector + CPR3_CORNER_OFFSET; ++ else ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_is_enabled() - return the enable state of the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: true if regulator is enabled, false if regulator is disabled ++ */ ++static int cpr3_regulator_is_enabled(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ return vreg->vreg_enabled; ++} ++ ++/** ++ * cpr3_regulator_enable() - enable the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_enable(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc = 0; ++ ++ if (vreg->vreg_enabled == true) ++ return 0; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->system_regulator) { ++ rc = regulator_enable(ctrl->system_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_enable(system) failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ rc = regulator_enable(ctrl->vdd_regulator); ++ if (rc) { ++ cpr3_err(vreg, "regulator_enable(vdd) failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ vreg->vreg_enabled = true; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ regulator_disable(ctrl->vdd_regulator); ++ vreg->vreg_enabled = false; ++ goto done; ++ } ++ ++ cpr3_debug(vreg, "Enabled\n"); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_disable() - disable the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_disable(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc, rc2; ++ ++ if (vreg->vreg_enabled == false) ++ return 0; ++ ++ mutex_lock(&ctrl->lock); ++ rc = regulator_disable(ctrl->vdd_regulator); ++ if (rc) { ++ cpr3_err(vreg, "regulator_disable(vdd) failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ vreg->vreg_enabled = false; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ rc2 = regulator_enable(ctrl->vdd_regulator); ++ vreg->vreg_enabled = true; ++ goto done; ++ } ++ ++ if (ctrl->system_regulator) { ++ rc = regulator_disable(ctrl->system_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_disable(system) failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_debug(vreg, "Disabled\n"); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++static struct regulator_ops cpr3_regulator_ops = { ++ .enable = cpr3_regulator_enable, ++ .disable = cpr3_regulator_disable, ++ .is_enabled = cpr3_regulator_is_enabled, ++ .set_voltage = cpr3_regulator_set_voltage, ++ .get_voltage = cpr3_regulator_get_voltage, ++ .list_voltage = cpr3_regulator_list_voltage, ++}; ++ ++/** ++ * cpr3_print_result() - print CPR measurement results to the kernel log for ++ * debugging purposes ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: None ++ */ ++static void cpr3_print_result(struct cpr3_thread *thread) ++{ ++ struct cpr3_controller *ctrl = thread->ctrl; ++ u32 result[3], busy, step_dn, step_up, error_steps, error, negative; ++ u32 quot_min, quot_max, ro_min, ro_max, step_quot_min, step_quot_max; ++ u32 sensor_min, sensor_max; ++ char *sign; ++ ++ result[0] = cpr3_read(ctrl, CPR3_REG_RESULT0(thread->thread_id)); ++ result[1] = cpr3_read(ctrl, CPR3_REG_RESULT1(thread->thread_id)); ++ result[2] = cpr3_read(ctrl, CPR3_REG_RESULT2(thread->thread_id)); ++ ++ busy = !!(result[0] & CPR3_RESULT0_BUSY_MASK); ++ step_dn = !!(result[0] & CPR3_RESULT0_STEP_DN_MASK); ++ step_up = !!(result[0] & CPR3_RESULT0_STEP_UP_MASK); ++ error_steps = (result[0] & CPR3_RESULT0_ERROR_STEPS_MASK) ++ >> CPR3_RESULT0_ERROR_STEPS_SHIFT; ++ error = (result[0] & CPR3_RESULT0_ERROR_MASK) ++ >> CPR3_RESULT0_ERROR_SHIFT; ++ negative = !!(result[0] & CPR3_RESULT0_NEGATIVE_MASK); ++ ++ quot_min = (result[1] & CPR3_RESULT1_QUOT_MIN_MASK) ++ >> CPR3_RESULT1_QUOT_MIN_SHIFT; ++ quot_max = (result[1] & CPR3_RESULT1_QUOT_MAX_MASK) ++ >> CPR3_RESULT1_QUOT_MAX_SHIFT; ++ ro_min = (result[1] & CPR3_RESULT1_RO_MIN_MASK) ++ >> CPR3_RESULT1_RO_MIN_SHIFT; ++ ro_max = (result[1] & CPR3_RESULT1_RO_MAX_MASK) ++ >> CPR3_RESULT1_RO_MAX_SHIFT; ++ ++ step_quot_min = (result[2] & CPR3_RESULT2_STEP_QUOT_MIN_MASK) ++ >> CPR3_RESULT2_STEP_QUOT_MIN_SHIFT; ++ step_quot_max = (result[2] & CPR3_RESULT2_STEP_QUOT_MAX_MASK) ++ >> CPR3_RESULT2_STEP_QUOT_MAX_SHIFT; ++ sensor_min = (result[2] & CPR3_RESULT2_SENSOR_MIN_MASK) ++ >> CPR3_RESULT2_SENSOR_MIN_SHIFT; ++ sensor_max = (result[2] & CPR3_RESULT2_SENSOR_MAX_MASK) ++ >> CPR3_RESULT2_SENSOR_MAX_SHIFT; ++ ++ sign = negative ? "-" : ""; ++ cpr3_debug(ctrl, "thread %u: busy=%u, step_dn=%u, step_up=%u, error_steps=%s%u, error=%s%u\n", ++ thread->thread_id, busy, step_dn, step_up, sign, error_steps, ++ sign, error); ++ cpr3_debug(ctrl, "thread %u: quot_min=%u, quot_max=%u, ro_min=%u, ro_max=%u\n", ++ thread->thread_id, quot_min, quot_max, ro_min, ro_max); ++ cpr3_debug(ctrl, "thread %u: step_quot_min=%u, step_quot_max=%u, sensor_min=%u, sensor_max=%u\n", ++ thread->thread_id, step_quot_min, step_quot_max, sensor_min, ++ sensor_max); ++} ++ ++/** ++ * cpr3_thread_busy() - returns if the specified CPR3 thread is busy taking ++ * a measurement ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: CPR3 busy status ++ */ ++static bool cpr3_thread_busy(struct cpr3_thread *thread) ++{ ++ u32 result; ++ ++ result = cpr3_read(thread->ctrl, CPR3_REG_RESULT0(thread->thread_id)); ++ ++ return !!(result & CPR3_RESULT0_BUSY_MASK); ++} ++ ++/** ++ * cpr3_irq_handler() - CPR interrupt handler callback function used for ++ * software closed-loop operation ++ * @irq: CPR interrupt number ++ * @data: Private data corresponding to the CPR3 controller ++ * pointer ++ * ++ * This function increases or decreases the vdd supply voltage based upon the ++ * CPR controller recommendation. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t cpr3_irq_handler(int irq, void *data) ++{ ++ struct cpr3_controller *ctrl = data; ++ struct cpr3_corner *aggr = &ctrl->aggr_corner; ++ u32 cont = CPR3_CONT_CMD_NACK; ++ u32 reg_last_measurement = 0; ++ struct cpr3_regulator *vreg; ++ struct cpr3_corner *corner; ++ unsigned long flags; ++ int i, j, new_volt, last_volt, dynamic_floor_volt, rc; ++ u32 irq_en, status, cpr_status, ctl; ++ bool up, down; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_debug(ctrl, "CPR interrupt received but CPR is disabled\n"); ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++ } else if (ctrl->use_hw_closed_loop) { ++ cpr3_debug(ctrl, "CPR interrupt received but CPR is using HW closed-loop\n"); ++ goto done; ++ } ++ ++ /* ++ * CPR IRQ status checking and CPR controller disabling must happen ++ * atomically and without invening delay in order to avoid an interrupt ++ * storm caused by the handler racing with the CPR controller. ++ */ ++ local_irq_save(flags); ++ preempt_disable(); ++ ++ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); ++ up = status & CPR3_IRQ_UP; ++ down = status & CPR3_IRQ_DOWN; ++ ++ if (!up && !down) { ++ /* ++ * Toggle the CPR controller off and then back on since the ++ * hardware and software states are out of sync. This condition ++ * occurs after an aging measurement completes as the CPR IRQ ++ * physically triggers during the aging measurement but the ++ * handler is stuck waiting on the mutex lock. ++ */ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ local_irq_restore(flags); ++ preempt_enable(); ++ ++ /* Wait for the loop disable write to complete */ ++ mb(); ++ ++ /* Wait for BUSY=1 and LOOP_EN=0 in CPR controller registers. */ ++ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { ++ cpr_status = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ ctl = cpr3_read(ctrl, CPR3_REG_CPR_CTL); ++ if (cpr_status & CPR3_CPR_STATUS_BUSY_MASK ++ && (ctl & CPR3_CPR_CTL_LOOP_EN_MASK) ++ == CPR3_CPR_CTL_LOOP_DISABLE) ++ break; ++ udelay(10); ++ } ++ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) ++ cpr3_debug(ctrl, "CPR controller not disabled after %d us\n", ++ CPR3_REGISTER_WRITE_DELAY_US); ++ ++ /* Clear interrupt status */ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ ++ /* Wait for the interrupt clearing write to complete */ ++ mb(); ++ ++ /* Wait for IRQ_STATUS register to be cleared. */ ++ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { ++ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); ++ if (!(status & (CPR3_IRQ_UP | CPR3_IRQ_DOWN))) ++ break; ++ udelay(10); ++ } ++ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) ++ cpr3_debug(ctrl, "CPR interrupts not cleared after %d us\n", ++ CPR3_REGISTER_WRITE_DELAY_US); ++ ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ cpr3_debug(ctrl, "CPR interrupt received but no up or down status bit is set\n"); ++ ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++ } else if (up && down) { ++ cpr3_debug(ctrl, "both up and down status bits set\n"); ++ /* The up flag takes precedence over the down flag. */ ++ down = false; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) ++ reg_last_measurement ++ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); ++ dynamic_floor_volt = cpr3_regulator_get_dynamic_floor_volt(ctrl, ++ reg_last_measurement); ++ ++ local_irq_restore(flags); ++ preempt_enable(); ++ ++ irq_en = aggr->irq_en; ++ last_volt = aggr->last_volt; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ if (cpr3_thread_busy(&ctrl->thread[i])) { ++ cpr3_debug(ctrl, "CPR thread %u busy when it should be waiting for SW cont\n", ++ ctrl->thread[i].thread_id); ++ goto done; ++ } ++ } ++ ++ new_volt = up ? last_volt + ctrl->step_volt ++ : last_volt - ctrl->step_volt; ++ ++ /* Re-enable UP/DOWN interrupt when its opposite is received. */ ++ irq_en |= up ? CPR3_IRQ_DOWN : CPR3_IRQ_UP; ++ ++ if (new_volt > aggr->ceiling_volt) { ++ new_volt = aggr->ceiling_volt; ++ irq_en &= ~CPR3_IRQ_UP; ++ cpr3_debug(ctrl, "limiting to ceiling=%d uV\n", ++ aggr->ceiling_volt); ++ } else if (new_volt < aggr->floor_volt) { ++ new_volt = aggr->floor_volt; ++ irq_en &= ~CPR3_IRQ_DOWN; ++ cpr3_debug(ctrl, "limiting to floor=%d uV\n", aggr->floor_volt); ++ } ++ ++ if (down && new_volt < dynamic_floor_volt) { ++ /* ++ * The vdd-supply voltage should not be decreased below the ++ * dynamic floor voltage. However, it is not necessary (and ++ * counter productive) to force the voltage up to this level ++ * if it happened to be below it since the closed-loop voltage ++ * must have gotten there in a safe manner while the power ++ * domains for the CPR3 regulator imposing the dynamic floor ++ * were not bypassed. ++ */ ++ new_volt = last_volt; ++ irq_en &= ~CPR3_IRQ_DOWN; ++ cpr3_debug(ctrl, "limiting to dynamic floor=%d uV\n", ++ dynamic_floor_volt); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ cpr3_print_result(&ctrl->thread[i]); ++ ++ cpr3_debug(ctrl, "%s: new_volt=%d uV, last_volt=%d uV\n", ++ up ? "UP" : "DN", new_volt, last_volt); ++ ++ if (ctrl->proc_clock_throttle && last_volt == aggr->ceiling_volt ++ && new_volt < last_volt) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ ctrl->proc_clock_throttle); ++ ++ if (new_volt != last_volt) { ++ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, ++ last_volt, ++ aggr); ++ if (rc) { ++ cpr3_err(ctrl, "scale_vdd() failed to set vdd=%d uV, rc=%d\n", ++ new_volt, rc); ++ goto done; ++ } ++ cont = CPR3_CONT_CMD_ACK; ++ ++ /* ++ * Update the closed-loop voltage for all regulators managed ++ * by this CPR controller. ++ */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ cpr3_update_vreg_closed_loop_volt(vreg, ++ new_volt, reg_last_measurement); ++ } ++ } ++ } ++ ++ if (ctrl->proc_clock_throttle && new_volt == aggr->ceiling_volt) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ ++ corner = &ctrl->thread[0].vreg[0].corner[ ++ ctrl->thread[0].vreg[0].current_corner]; ++ ++ if (irq_en != aggr->irq_en) { ++ aggr->irq_en = irq_en; ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_en); ++ } ++ ++ aggr->last_volt = new_volt; ++ ++done: ++ /* Clear interrupt status */ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ ++ /* ACK or NACK the CPR controller */ ++ cpr3_write(ctrl, CPR3_REG_CONT_CMD, cont); ++ ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * cpr3_ceiling_irq_handler() - CPR ceiling reached interrupt handler callback ++ * function used for hardware closed-loop operation ++ * @irq: CPR ceiling interrupt number ++ * @data: Private data corresponding to the CPR3 controller ++ * pointer ++ * ++ * This function disables processor clock throttling and closed-loop operation ++ * when the ceiling voltage is reached. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t cpr3_ceiling_irq_handler(int irq, void *data) ++{ ++ struct cpr3_controller *ctrl = data; ++ int volt; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is disabled\n"); ++ goto done; ++ } else if (!ctrl->use_hw_closed_loop) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is using SW closed-loop\n"); ++ goto done; ++ } ++ ++ volt = regulator_get_voltage(ctrl->vdd_regulator); ++ if (volt < 0) { ++ cpr3_err(ctrl, "could not get vdd voltage, rc=%d\n", volt); ++ goto done; ++ } else if (volt != ctrl->aggr_corner.ceiling_volt) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but vdd voltage: %d uV != ceiling voltage: %d uV\n", ++ volt, ctrl->aggr_corner.ceiling_volt); ++ goto done; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ /* ++ * Since the ceiling voltage has been reached, disable processor ++ * clock throttling as well as CPR closed-loop operation. ++ */ ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ cpr3_ctrl_loop_disable(ctrl); ++ cpr3_debug(ctrl, "CPR closed-loop and throttling disabled\n"); ++ } ++ ++done: ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * cpr3_regulator_vreg_register() - register a regulator device for a CPR3 ++ * regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function initializes all regulator framework related structures and then ++ * calls regulator_register() for the CPR3 regulator. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_vreg_register(struct cpr3_regulator *vreg) ++{ ++ struct regulator_config config = {}; ++ struct regulator_desc *rdesc; ++ struct regulator_init_data *init_data; ++ int rc; ++ ++ init_data = of_get_regulator_init_data(vreg->thread->ctrl->dev, ++ vreg->of_node, &vreg->rdesc); ++ if (!init_data) { ++ cpr3_err(vreg, "regulator init data is missing\n"); ++ return -EINVAL; ++ } ++ ++ init_data->constraints.input_uV = init_data->constraints.max_uV; ++ rdesc = &vreg->rdesc; ++ init_data->constraints.valid_ops_mask |= ++ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS; ++ rdesc->ops = &cpr3_regulator_ops; ++ ++ rdesc->n_voltages = vreg->corner_count; ++ rdesc->name = init_data->constraints.name; ++ rdesc->owner = THIS_MODULE; ++ rdesc->type = REGULATOR_VOLTAGE; ++ ++ config.dev = vreg->thread->ctrl->dev; ++ config.driver_data = vreg; ++ config.init_data = init_data; ++ config.of_node = vreg->of_node; ++ ++ vreg->rdev = regulator_register(vreg->thread->ctrl->dev, rdesc, &config); ++ if (IS_ERR(vreg->rdev)) { ++ rc = PTR_ERR(vreg->rdev); ++ cpr3_err(vreg, "regulator_register failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int debugfs_int_set(void *data, u64 val) ++{ ++ *(int *)data = val; ++ return 0; ++} ++ ++static int debugfs_int_get(void *data, u64 *val) ++{ ++ *val = *(int *)data; ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(fops_int, debugfs_int_get, debugfs_int_set, "%lld\n"); ++DEFINE_SIMPLE_ATTRIBUTE(fops_int_ro, debugfs_int_get, NULL, "%lld\n"); ++DEFINE_SIMPLE_ATTRIBUTE(fops_int_wo, NULL, debugfs_int_set, "%lld\n"); ++ ++/** ++ * debugfs_create_int - create a debugfs file that is used to read and write a ++ * signed int value ++ * @name: Pointer to a string containing the name of the file to ++ * create ++ * @mode: The permissions that the file should have ++ * @parent: Pointer to the parent dentry for this file. This should ++ * be a directory dentry if set. If this parameter is ++ * %NULL, then the file will be created in the root of the ++ * debugfs filesystem. ++ * @value: Pointer to the variable that the file should read to and ++ * write from ++ * ++ * This function creates a file in debugfs with the given name that ++ * contains the value of the variable @value. If the @mode variable is so ++ * set, it can be read from, and written to. ++ * ++ * This function will return a pointer to a dentry if it succeeds. This ++ * pointer must be passed to the debugfs_remove() function when the file is ++ * to be removed. If an error occurs, %NULL will be returned. ++ */ ++static struct dentry *debugfs_create_int(const char *name, umode_t mode, ++ struct dentry *parent, int *value) ++{ ++ /* if there are no write bits set, make read only */ ++ if (!(mode & S_IWUGO)) ++ return debugfs_create_file(name, mode, parent, value, ++ &fops_int_ro); ++ /* if there are no read bits set, make write only */ ++ if (!(mode & S_IRUGO)) ++ return debugfs_create_file(name, mode, parent, value, ++ &fops_int_wo); ++ ++ return debugfs_create_file(name, mode, parent, value, &fops_int); ++} ++ ++static int debugfs_bool_get(void *data, u64 *val) ++{ ++ *val = *(bool *)data; ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%lld\n"); ++ ++/** ++ * struct cpr3_debug_corner_info - data structure used by the ++ * cpr3_debugfs_create_corner_int function ++ * @vreg: Pointer to the CPR3 regulator ++ * @index: Pointer to the corner array index ++ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner ++ * to the beginning of the value to be read from ++ * @corner: Pointer to the CPR3 corner array ++ */ ++struct cpr3_debug_corner_info { ++ struct cpr3_regulator *vreg; ++ int *index; ++ size_t member_offset; ++ struct cpr3_corner *corner; ++}; ++ ++static int cpr3_debug_corner_int_get(void *data, u64 *val) ++{ ++ struct cpr3_debug_corner_info *info = data; ++ struct cpr3_controller *ctrl = info->vreg->thread->ctrl; ++ int i; ++ ++ mutex_lock(&ctrl->lock); ++ ++ i = *info->index; ++ if (i < 0) ++ i = 0; ++ ++ *val = *(int *)((char *)&info->vreg->corner[i] + info->member_offset); ++ ++ mutex_unlock(&ctrl->lock); ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_int_fops, cpr3_debug_corner_int_get, ++ NULL, "%lld\n"); ++ ++/** ++ * cpr3_debugfs_create_corner_int - create a debugfs file that is used to read ++ * a signed int value out of a CPR3 regulator's corner array ++ * @vreg: Pointer to the CPR3 regulator ++ * @name: Pointer to a string containing the name of the file to ++ * create ++ * @mode: The permissions that the file should have ++ * @parent: Pointer to the parent dentry for this file. This should ++ * be a directory dentry if set. If this parameter is ++ * %NULL, then the file will be created in the root of the ++ * debugfs filesystem. ++ * @index: Pointer to the corner array index ++ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner ++ * to the beginning of the value to be read from ++ * ++ * This function creates a file in debugfs with the given name that ++ * contains the value of the int type variable vreg->corner[index].member ++ * where member_offset == offsetof(struct cpr3_corner, member). ++ */ ++static struct dentry *cpr3_debugfs_create_corner_int( ++ struct cpr3_regulator *vreg, const char *name, umode_t mode, ++ struct dentry *parent, int *index, size_t member_offset) ++{ ++ struct cpr3_debug_corner_info *info; ++ ++ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return NULL; ++ ++ info->vreg = vreg; ++ info->index = index; ++ info->member_offset = member_offset; ++ ++ return debugfs_create_file(name, mode, parent, info, ++ &cpr3_debug_corner_int_fops); ++} ++ ++static int cpr3_debug_quot_open(struct inode *inode, struct file *file) ++{ ++ struct cpr3_debug_corner_info *info = inode->i_private; ++ struct cpr3_thread *thread = info->vreg->thread; ++ int size, i, pos; ++ u32 *quot; ++ char *buf; ++ ++ /* ++ * Max size: ++ * - 10 digits + ' ' or '\n' = 11 bytes per number ++ * - terminating '\0' ++ */ ++ size = CPR3_RO_COUNT * 11; ++ buf = kzalloc(size + 1, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ file->private_data = buf; ++ ++ mutex_lock(&thread->ctrl->lock); ++ ++ quot = info->corner[*info->index].target_quot; ++ ++ for (i = 0, pos = 0; i < CPR3_RO_COUNT; i++) ++ pos += scnprintf(buf + pos, size - pos, "%u%c", ++ quot[i], i < CPR3_RO_COUNT - 1 ? ' ' : '\n'); ++ ++ mutex_unlock(&thread->ctrl->lock); ++ ++ return nonseekable_open(inode, file); ++} ++ ++static ssize_t cpr3_debug_quot_read(struct file *file, char __user *buf, ++ size_t len, loff_t *ppos) ++{ ++ return simple_read_from_buffer(buf, len, ppos, file->private_data, ++ strlen(file->private_data)); ++} ++ ++static int cpr3_debug_quot_release(struct inode *inode, struct file *file) ++{ ++ kfree(file->private_data); ++ ++ return 0; ++} ++ ++static const struct file_operations cpr3_debug_quot_fops = { ++ .owner = THIS_MODULE, ++ .open = cpr3_debug_quot_open, ++ .release = cpr3_debug_quot_release, ++ .read = cpr3_debug_quot_read, ++}; ++ ++/** ++ * cpr3_regulator_debugfs_corner_add() - add debugfs files to expose ++ * configuration data for the CPR corner ++ * @vreg: Pointer to the CPR3 regulator ++ * @corner_dir: Pointer to the parent corner dentry for the new files ++ * @index: Pointer to the corner array index ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_corner_add(struct cpr3_regulator *vreg, ++ struct dentry *corner_dir, int *index) ++{ ++ struct cpr3_debug_corner_info *info; ++ struct dentry *temp; ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "floor_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, floor_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "floor_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "ceiling_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, ceiling_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "ceiling_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "open_loop_volt", S_IRUGO, ++ corner_dir, index, ++ offsetof(struct cpr3_corner, open_loop_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "open_loop_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "last_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, last_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "last_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return; ++ ++ info->vreg = vreg; ++ info->index = index; ++ info->corner = vreg->corner; ++ ++ temp = debugfs_create_file("target_quots", S_IRUGO, corner_dir, ++ info, &cpr3_debug_quot_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "target_quots debugfs file creation failed\n"); ++ return; ++ } ++} ++ ++/** ++ * cpr3_debug_corner_index_set() - debugfs callback used to change the ++ * value of the CPR3 regulator debug_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: New value for debug_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_corner_index_set(void *data, u64 val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ if (val < CPR3_CORNER_OFFSET || val > vreg->corner_count) { ++ cpr3_err(vreg, "invalid corner index %llu; allowed values: %d-%d\n", ++ val, CPR3_CORNER_OFFSET, vreg->corner_count); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&vreg->thread->ctrl->lock); ++ vreg->debug_corner = val - CPR3_CORNER_OFFSET; ++ mutex_unlock(&vreg->thread->ctrl->lock); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_debug_corner_index_get() - debugfs callback used to retrieve ++ * the value of the CPR3 regulator debug_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: Output parameter written with the value of ++ * debug_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_corner_index_get(void *data, u64 *val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ *val = vreg->debug_corner + CPR3_CORNER_OFFSET; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_index_fops, ++ cpr3_debug_corner_index_get, ++ cpr3_debug_corner_index_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_current_corner_index_get() - debugfs callback used to retrieve ++ * the value of the CPR3 regulator current_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: Output parameter written with the value of ++ * current_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_current_corner_index_get(void *data, u64 *val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ *val = vreg->current_corner + CPR3_CORNER_OFFSET; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_current_corner_index_fops, ++ cpr3_debug_current_corner_index_get, ++ NULL, "%llu\n"); ++ ++/** ++ * cpr3_regulator_debugfs_vreg_add() - add debugfs files to expose configuration ++ * data for the CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @thread_dir CPR3 thread debugfs directory handle ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_vreg_add(struct cpr3_regulator *vreg, ++ struct dentry *thread_dir) ++{ ++ struct dentry *temp, *corner_dir, *vreg_dir; ++ ++ vreg_dir = debugfs_create_dir(vreg->name, thread_dir); ++ if (IS_ERR_OR_NULL(vreg_dir)) { ++ cpr3_err(vreg, "%s debugfs directory creation failed\n", ++ vreg->name); ++ return; ++ } ++ ++ temp = debugfs_create_int("speed_bin_fuse", S_IRUGO, vreg_dir, ++ &vreg->speed_bin_fuse); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "speed_bin_fuse debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("cpr_rev_fuse", S_IRUGO, vreg_dir, ++ &vreg->cpr_rev_fuse); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "cpr_rev_fuse debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("fuse_combo", S_IRUGO, vreg_dir, ++ &vreg->fuse_combo); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "fuse_combo debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("corner_count", S_IRUGO, vreg_dir, ++ &vreg->corner_count); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "corner_count debugfs file creation failed\n"); ++ return; ++ } ++ ++ corner_dir = debugfs_create_dir("corner", vreg_dir); ++ if (IS_ERR_OR_NULL(corner_dir)) { ++ cpr3_err(vreg, "corner debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("index", S_IRUGO | S_IWUSR, corner_dir, ++ vreg, &cpr3_debug_corner_index_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "index debugfs file creation failed\n"); ++ return; ++ } ++ ++ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, ++ &vreg->debug_corner); ++ ++ corner_dir = debugfs_create_dir("current_corner", vreg_dir); ++ if (IS_ERR_OR_NULL(corner_dir)) { ++ cpr3_err(vreg, "current_corner debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("index", S_IRUGO, corner_dir, ++ vreg, &cpr3_debug_current_corner_index_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "index debugfs file creation failed\n"); ++ return; ++ } ++ ++ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, ++ &vreg->current_corner); ++} ++ ++/** ++ * cpr3_regulator_debugfs_thread_add() - add debugfs files to expose ++ * configuration data for the CPR thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_thread_add(struct cpr3_thread *thread) ++{ ++ struct cpr3_controller *ctrl = thread->ctrl; ++ struct dentry *aggr_dir, *temp, *thread_dir; ++ struct cpr3_debug_corner_info *info; ++ char buf[20]; ++ int *index; ++ int i; ++ ++ scnprintf(buf, sizeof(buf), "thread%u", thread->thread_id); ++ thread_dir = debugfs_create_dir(buf, thread->ctrl->debugfs); ++ if (IS_ERR_OR_NULL(thread_dir)) { ++ cpr3_err(ctrl, "thread %u %s debugfs directory creation failed\n", ++ thread->thread_id, buf); ++ return; ++ } ++ ++ aggr_dir = debugfs_create_dir("max_aggregated_params", thread_dir); ++ if (IS_ERR_OR_NULL(aggr_dir)) { ++ cpr3_err(ctrl, "thread %u max_aggregated_params debugfs directory creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.floor_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr floor_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.ceiling_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr ceiling_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.open_loop_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr open_loop_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.last_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr last_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ info = devm_kzalloc(thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ index = devm_kzalloc(thread->ctrl->dev, sizeof(*index), GFP_KERNEL); ++ if (!info || !index) ++ return; ++ *index = 0; ++ info->vreg = &thread->vreg[0]; ++ info->index = index; ++ info->corner = &thread->aggr_corner; ++ ++ temp = debugfs_create_file("target_quots", S_IRUGO, aggr_dir, ++ info, &cpr3_debug_quot_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u target_quots debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ for (i = 0; i < thread->vreg_count; i++) ++ cpr3_regulator_debugfs_vreg_add(&thread->vreg[i], thread_dir); ++} ++ ++/** ++ * cpr3_debug_closed_loop_enable_set() - debugfs callback used to change the ++ * value of the CPR controller cpr_allowed_sw flag which enables or ++ * disables closed-loop operation ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: New value for cpr_allowed_sw ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_closed_loop_enable_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ bool enable = !!val; ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->cpr_allowed_sw == enable) ++ goto done; ++ ++ if (enable && !ctrl->cpr_allowed_hw) { ++ cpr3_err(ctrl, "CPR closed-loop operation is not allowed\n"); ++ goto done; ++ } ++ ++ ctrl->cpr_allowed_sw = enable; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n", ++ enable, rc); ++ goto done; ++ } ++ ++ if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ctrl->cpr_enabled = true; ++ ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ cpr3_debug(ctrl, "closed-loop=%s\n", enable ? "enabled" : "disabled"); ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_debug_closed_loop_enable_get() - debugfs callback used to retrieve ++ * the value of the CPR controller cpr_allowed_sw flag which ++ * indicates if closed-loop operation is enabled ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Output parameter written with the value of ++ * cpr_allowed_sw ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_closed_loop_enable_get(void *data, u64 *val) ++{ ++ struct cpr3_controller *ctrl = data; ++ ++ *val = ctrl->cpr_allowed_sw; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops, ++ cpr3_debug_closed_loop_enable_get, ++ cpr3_debug_closed_loop_enable_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the ++ * value of the CPR controller use_hw_closed_loop flag which ++ * switches between software closed-loop and hardware closed-loop ++ * operation for CPR3 and CPR4 controllers and between open-loop ++ * and full hardware closed-loop operation for CPRh controllers. ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: New value for use_hw_closed_loop ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ bool use_hw_closed_loop = !!val; ++ struct cpr3_regulator *vreg; ++ bool cpr_enabled; ++ int i, j, k, rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->use_hw_closed_loop == use_hw_closed_loop) ++ goto done; ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ ctrl->use_hw_closed_loop = use_hw_closed_loop; ++ ++ cpr_enabled = ctrl->cpr_enabled; ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ goto done; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ if (ctrl->use_hw_closed_loop) ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = regulator_enable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } else if (!ctrl->use_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = regulator_disable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ /* ++ * Due to APM and mem-acc floor restriction constraints, ++ * the closed-loop voltage may be different when using ++ * software closed-loop vs hardware closed-loop. Therefore, ++ * reset the cached closed-loop voltage for all corners to the ++ * corresponding open-loop voltage when switching between ++ * SW and HW closed-loop mode. ++ */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ for (k = 0; k < vreg->corner_count; k++) ++ vreg->corner[k].last_volt ++ = vreg->corner[k].open_loop_volt; ++ } ++ } ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n", ++ use_hw_closed_loop, rc); ++ goto done; ++ } ++ ++ cpr3_debug(ctrl, "CPR mode=%s\n", ++ use_hw_closed_loop ? ++ "HW closed-loop" : "SW closed-loop"); ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_debug_hw_closed_loop_enable_get() - debugfs callback used to retrieve ++ * the value of the CPR controller use_hw_closed_loop flag which ++ * indicates if hardware closed-loop operation is being used in ++ * place of software closed-loop operation ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Output parameter written with the value of ++ * use_hw_closed_loop ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_hw_closed_loop_enable_get(void *data, u64 *val) ++{ ++ struct cpr3_controller *ctrl = data; ++ ++ *val = ctrl->use_hw_closed_loop; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_hw_closed_loop_enable_fops, ++ cpr3_debug_hw_closed_loop_enable_get, ++ cpr3_debug_hw_closed_loop_enable_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_trigger_aging_measurement_set() - debugfs callback used to trigger ++ * another CPR measurement ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Unused ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_trigger_aging_measurement_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); ++ ctrl->aging_required = true; ++ ctrl->aging_succeeded = false; ++ ctrl->aging_failed = false; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not update the CPR controller state, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_trigger_aging_measurement_fops, ++ NULL, ++ cpr3_debug_trigger_aging_measurement_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_regulator_debugfs_ctrl_add() - add debugfs files to expose configuration ++ * data for the CPR controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_ctrl_add(struct cpr3_controller *ctrl) ++{ ++ struct dentry *temp, *aggr_dir; ++ int i; ++ ++ /* Add cpr3-regulator base directory if it isn't present already. */ ++ if (cpr3_debugfs_base == NULL) { ++ cpr3_debugfs_base = debugfs_create_dir("cpr3-regulator", NULL); ++ if (IS_ERR_OR_NULL(cpr3_debugfs_base)) { ++ cpr3_err(ctrl, "cpr3-regulator debugfs base directory creation failed\n"); ++ cpr3_debugfs_base = NULL; ++ return; ++ } ++ } ++ ++ ctrl->debugfs = debugfs_create_dir(ctrl->name, cpr3_debugfs_base); ++ if (IS_ERR_OR_NULL(ctrl->debugfs)) { ++ cpr3_err(ctrl, "cpr3-regulator controller debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("cpr_closed_loop_enable", S_IRUGO | S_IWUSR, ++ ctrl->debugfs, ctrl, ++ &cpr3_debug_closed_loop_enable_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "cpr_closed_loop_enable debugfs file creation failed\n"); ++ return; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ temp = debugfs_create_file("use_hw_closed_loop", ++ S_IRUGO | S_IWUSR, ctrl->debugfs, ctrl, ++ &cpr3_debug_hw_closed_loop_enable_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "use_hw_closed_loop debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ temp = debugfs_create_int("thread_count", S_IRUGO, ctrl->debugfs, ++ &ctrl->thread_count); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread_count debugfs file creation failed\n"); ++ return; ++ } ++ ++ if (ctrl->apm) { ++ temp = debugfs_create_int("apm_threshold_volt", S_IRUGO, ++ ctrl->debugfs, &ctrl->apm_threshold_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "apm_threshold_volt debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ if (ctrl->aging_required || ctrl->aging_succeeded ++ || ctrl->aging_failed) { ++ temp = debugfs_create_int("aging_adj_volt", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_ref_adjust_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_adj_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_succeeded", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_succeeded, &fops_bool_ro); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_succeeded debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_failed", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_failed, &fops_bool_ro); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_failed debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_trigger", S_IWUSR, ++ ctrl->debugfs, ctrl, ++ &cpr3_debug_trigger_aging_measurement_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_trigger debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ aggr_dir = debugfs_create_dir("max_aggregated_voltages", ctrl->debugfs); ++ if (IS_ERR_OR_NULL(aggr_dir)) { ++ cpr3_err(ctrl, "max_aggregated_voltages debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.floor_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr floor_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.ceiling_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr ceiling_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.open_loop_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr open_loop_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.last_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr last_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ cpr3_regulator_debugfs_thread_add(&ctrl->thread[i]); ++} ++ ++/** ++ * cpr3_regulator_debugfs_ctrl_remove() - remove debugfs files for the CPR ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Note, this function must be called after the controller has been removed from ++ * cpr3_controller_list and while the cpr3_controller_list_mutex lock is held. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_ctrl_remove(struct cpr3_controller *ctrl) ++{ ++ if (list_empty(&cpr3_controller_list)) { ++ debugfs_remove_recursive(cpr3_debugfs_base); ++ cpr3_debugfs_base = NULL; ++ } else { ++ debugfs_remove_recursive(ctrl->debugfs); ++ } ++} ++ ++/** ++ * cpr3_regulator_init_ctrl_data() - performs initialization of CPR controller ++ * elements ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ /* Read the initial vdd voltage from hardware. */ ++ ctrl->aggr_corner.last_volt ++ = regulator_get_voltage(ctrl->vdd_regulator); ++ if (ctrl->aggr_corner.last_volt < 0) { ++ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", ++ ctrl->aggr_corner.last_volt); ++ return ctrl->aggr_corner.last_volt; ++ } ++ ctrl->aggr_corner.open_loop_volt = ctrl->aggr_corner.last_volt; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_vreg_data() - performs initialization of common CPR3 ++ * regulator elements and validate aging configurations ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ bool init_aging; ++ ++ vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID; ++ vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID; ++ ++ init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; ++ vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN; ++ ++ vreg->corner[i].ro_mask = 0; ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] == 0) ++ vreg->corner[i].ro_mask |= BIT(j); ++ } ++ ++ if (init_aging) { ++ vreg->corner[i].unaged_floor_volt ++ = vreg->corner[i].floor_volt; ++ vreg->corner[i].unaged_ceiling_volt ++ = vreg->corner[i].ceiling_volt; ++ vreg->corner[i].unaged_open_loop_volt ++ = vreg->corner[i].open_loop_volt; ++ } ++ ++ if (vreg->aging_allowed) { ++ if (vreg->corner[i].unaged_floor_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_floor_volt); ++ return -EINVAL; ++ } ++ if (vreg->corner[i].unaged_ceiling_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_ceiling_volt); ++ return -EINVAL; ++ } ++ if (vreg->corner[i].unaged_open_loop_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_open_loop_volt); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt ++ > vreg->thread->ctrl->aging_ref_volt) { ++ cpr3_err(vreg, "aging corner %d ceiling voltage = %d > aging ref voltage = %d uV\n", ++ vreg->aging_corner, ++ vreg->corner[vreg->aging_corner].ceiling_volt, ++ vreg->thread->ctrl->aging_ref_volt); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_suspend() - perform common required CPR3 power down steps ++ * before the system enters suspend ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_suspend(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ mutex_unlock(&ctrl->lock); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ rc = cpr3_closed_loop_disable(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); ++ ++ ctrl->cpr_suspended = true; ++ ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_resume() - perform common required CPR3 power up steps after ++ * the system resumes from suspend ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_resume(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ ctrl->cpr_suspended = false; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); ++ ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_validate_controller() - verify the data passed in via the ++ * cpr3_controller data structure ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ int i, j, allow_boost_vreg_count = 0; ++ ++ if (!ctrl->vdd_regulator) { ++ cpr3_err(ctrl, "vdd regulator missing\n"); ++ return -EINVAL; ++ } else if (ctrl->sensor_count <= 0 ++ || ctrl->sensor_count > CPR3_MAX_SENSOR_COUNT) { ++ cpr3_err(ctrl, "invalid CPR sensor count=%d\n", ++ ctrl->sensor_count); ++ return -EINVAL; ++ } else if (!ctrl->sensor_owner) { ++ cpr3_err(ctrl, "CPR sensor ownership table missing\n"); ++ return -EINVAL; ++ } ++ ++ if (ctrl->aging_required) { ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ if (ctrl->aging_sensor[i].sensor_id ++ >= ctrl->sensor_count) { ++ cpr3_err(ctrl, "aging_sensor[%d] id=%u is not in the value range 0-%d", ++ i, ctrl->aging_sensor[i].sensor_id, ++ ctrl->sensor_count - 1); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ if (vreg->allow_boost) ++ allow_boost_vreg_count++; ++ } ++ } ++ ++ if (allow_boost_vreg_count > 1) { ++ /* ++ * Boost feature is not allowed to be used for more ++ * than one CPR3 regulator of a CPR3 controller. ++ */ ++ cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_panic_callback() - panic notification callback function. This function ++ * is invoked when a kernel panic occurs. ++ * @nfb: Notifier block pointer of CPR3 controller ++ * @event: Value passed unmodified to notifier function ++ * @data: Pointer passed unmodified to notifier function ++ * ++ * Return: NOTIFY_OK ++ */ ++static int cpr3_panic_callback(struct notifier_block *nfb, ++ unsigned long event, void *data) ++{ ++ struct cpr3_controller *ctrl = container_of(nfb, ++ struct cpr3_controller, panic_notifier); ++ struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info; ++ struct cpr3_reg_info *reg; ++ int i = 0; ++ ++ for (i = 0; i < regs_info->reg_count; i++) { ++ reg = &(regs_info->regs[i]); ++ reg->value = readl_relaxed(reg->virt_addr); ++ pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr, ++ reg->value); ++ } ++ /* ++ * Barrier to ensure that the information has been updated in the ++ * structure. ++ */ ++ mb(); ++ ++ return NOTIFY_OK; ++} ++ ++/** ++ * cpr3_regulator_register() - register the regulators for a CPR3 controller and ++ * perform CPR hardware initialization ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ int i, j, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "%s: Device tree node is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl || !ctrl->name) { ++ dev_err(dev, "%s: CPR controller data is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_regulator_validate_controller(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "controller validation failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ mutex_init(&ctrl->lock); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_ctrl"); ++ if (!res || !res->start) { ++ cpr3_err(ctrl, "CPR controller address is missing\n"); ++ return -ENXIO; ++ } ++ ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res)); ++ ++ if (ctrl->aging_possible_mask) { ++ /* ++ * Aging possible register address is required if an aging ++ * possible mask has been specified. ++ */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "aging_allowed"); ++ if (!res || !res->start) { ++ cpr3_err(ctrl, "CPR aging allowed address is missing\n"); ++ return -ENXIO; ++ } ++ ctrl->aging_possible_reg = devm_ioremap(dev, res->start, ++ resource_size(res)); ++ } ++ ++ ctrl->irq = platform_get_irq_byname(pdev, "cpr"); ++ if (ctrl->irq < 0) { ++ cpr3_err(ctrl, "missing CPR interrupt\n"); ++ return ctrl->irq; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ ctrl->ceiling_irq = platform_get_irq_byname(pdev, ++ "ceiling"); ++ if (ctrl->ceiling_irq < 0) { ++ cpr3_err(ctrl, "missing ceiling interrupt\n"); ++ return ctrl->ceiling_irq; ++ } ++ } ++ } ++ ++ rc = cpr3_regulator_init_ctrl_data(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_init_vreg_data( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) ++ return rc; ++ cpr3_print_quots(&ctrl->thread[i].vreg[j]); ++ } ++ } ++ ++ /* ++ * Add the maximum possible aging voltage margin until it is possible ++ * to perform an aging measurement. ++ */ ++ if (ctrl->aging_required) ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); ++ ++ rc = cpr3_regulator_init_ctrl(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* Register regulator devices for all threads. */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_vreg_register( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", ++ rc); ++ goto free_regulators; ++ } ++ } ++ } ++ ++ rc = devm_request_threaded_irq(dev, ctrl->irq, NULL, ++ cpr3_irq_handler, ++ IRQF_ONESHOT | ++ IRQF_TRIGGER_RISING, ++ "cpr3", ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not request IRQ %d, rc=%d\n", ++ ctrl->irq, rc); ++ goto free_regulators; ++ } ++ ++ if (ctrl->supports_hw_closed_loop && ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = devm_request_threaded_irq(dev, ctrl->ceiling_irq, NULL, ++ cpr3_ceiling_irq_handler, ++ IRQF_ONESHOT | IRQF_TRIGGER_RISING, ++ "cpr3_ceiling", ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not request ceiling IRQ %d, rc=%d\n", ++ ctrl->ceiling_irq, rc); ++ goto free_regulators; ++ } ++ } ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ cpr3_regulator_debugfs_ctrl_add(ctrl); ++ list_add(&ctrl->list, &cpr3_controller_list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->panic_regs_info) { ++ /* Register panic notification call back */ ++ ctrl->panic_notifier.notifier_call = cpr3_panic_callback; ++ atomic_notifier_chain_register(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ } ++ ++ return 0; ++ ++free_regulators: ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) ++ regulator_unregister( ++ ctrl->thread[i].vreg[j].rdev); ++ return rc; ++} ++ ++/** ++ * cpr3_open_loop_regulator_register() - register the regulators for a CPR3 ++ * controller which will always work in Open loop and ++ * won't support close loop. ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_regulator *vreg; ++ int i, j, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "%s: Device tree node is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl || !ctrl->name) { ++ dev_err(dev, "%s: CPR controller data is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl->vdd_regulator) { ++ cpr3_err(ctrl, "vdd regulator missing\n"); ++ return -EINVAL; ++ } ++ ++ mutex_init(&ctrl->lock); ++ ++ rc = cpr3_regulator_init_ctrl_data(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg->corner[i].last_volt = ++ vreg->corner[i].open_loop_volt; ++ } ++ } ++ ++ /* Register regulator devices for all threads. */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_vreg_register( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", ++ rc); ++ goto free_regulators; ++ } ++ } ++ } ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_add(&ctrl->list, &cpr3_controller_list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ return 0; ++ ++free_regulators: ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) ++ regulator_unregister( ++ ctrl->thread[i].vreg[j].rdev); ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_unregister() - unregister the regulators for a CPR3 controller ++ * and perform CPR hardware shutdown ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ int i, j, rc = 0; ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_del(&ctrl->list); ++ cpr3_regulator_debugfs_ctrl_remove(ctrl); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_closed_loop_disable(ctrl); ++ ++ if (ctrl->vdd_limit_regulator) { ++ regulator_disable(ctrl->vdd_limit_regulator); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ regulator_unregister(ctrl->thread[i].vreg[j].rdev); ++ ++ if (ctrl->panic_notifier.notifier_call) ++ atomic_notifier_chain_unregister(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_open_loop_regulator_unregister() - unregister the regulators for a CPR3 ++ * open loop controller and perform CPR hardware shutdown ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ int i, j; ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_del(&ctrl->list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->vdd_limit_regulator) { ++ regulator_disable(ctrl->vdd_limit_regulator); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ regulator_unregister(ctrl->thread[i].vreg[j].rdev); ++ ++ if (ctrl->panic_notifier.notifier_call) ++ atomic_notifier_chain_unregister(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/regulator/cpr3-regulator.h +@@ -0,0 +1,1211 @@ ++/* ++ * Copyright (c) 2015-2017, 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 __REGULATOR_CPR3_REGULATOR_H__ ++#define __REGULATOR_CPR3_REGULATOR_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct cpr3_controller; ++struct cpr3_thread; ++ ++/** ++ * struct cpr3_fuse_param - defines one contiguous segment of a fuse parameter ++ * that is contained within a given row. ++ * @row: Fuse row number ++ * @bit_start: The first bit within the row of the fuse parameter segment ++ * @bit_end: The last bit within the row of the fuse parameter segment ++ * ++ * Each fuse row is 64 bits in length. bit_start and bit_end may take values ++ * from 0 to 63. bit_start must be less than or equal to bit_end. ++ */ ++struct cpr3_fuse_param { ++ unsigned row; ++ unsigned bit_start; ++ unsigned bit_end; ++}; ++ ++/* Each CPR3 sensor has 16 ring oscillators */ ++#define CPR3_RO_COUNT 16 ++ ++/* The maximum number of sensors that can be present on a single CPR loop. */ ++#define CPR3_MAX_SENSOR_COUNT 256 ++ ++/* This constant is used when allocating array printing buffers. */ ++#define MAX_CHARS_PER_INT 10 ++ ++/** ++ * struct cpr4_sdelta - CPR4 controller specific data structure for the sdelta ++ * adjustment table which is used to adjust the VDD supply ++ * voltage automatically based upon the temperature and/or ++ * the number of online CPU cores. ++ * @allow_core_count_adj: Core count adjustments are allowed. ++ * @allow_temp_adj: Temperature based adjustments are allowed. ++ * @max_core_count: Maximum number of cores considered for core count ++ * adjustment logic. ++ * @temp_band_count: Number of temperature bands considered for temperature ++ * based adjustment logic. ++ * @cap_volt: CAP in uV to apply to SDELTA margins with multiple ++ * cpr3-regulators defined for single controller. ++ * @table: SDELTA table with per-online-core and temperature based ++ * adjustments of size (max_core_count * temp_band_count) ++ * Outer: core count ++ * Inner: temperature band ++ * Each element has units of VDD supply steps. Positive ++ * values correspond to a reduction in voltage and negative ++ * value correspond to an increase (this follows the SDELTA ++ * register semantics). ++ * @allow_boost: Voltage boost allowed. ++ * @boost_num_cores: The number of online cores at which the boost voltage ++ * adjustments will be applied ++ * @boost_table: SDELTA table with boost voltage adjustments of size ++ * temp_band_count. Each element has units of VDD supply ++ * steps. Positive values correspond to a reduction in ++ * voltage and negative value correspond to an increase ++ * (this follows the SDELTA register semantics). ++ */ ++struct cpr4_sdelta { ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ int max_core_count; ++ int temp_band_count; ++ int cap_volt; ++ int *table; ++ bool allow_boost; ++ int boost_num_cores; ++ int *boost_table; ++}; ++ ++/** ++ * struct cpr3_corner - CPR3 virtual voltage corner data structure ++ * @floor_volt: CPR closed-loop floor voltage in microvolts ++ * @ceiling_volt: CPR closed-loop ceiling voltage in microvolts ++ * @open_loop_volt: CPR open-loop voltage (i.e. initial voltage) in ++ * microvolts ++ * @last_volt: Last known settled CPR closed-loop voltage which is used ++ * when switching to a new corner ++ * @abs_ceiling_volt: The absolute CPR closed-loop ceiling voltage in ++ * microvolts. This is used to limit the ceiling_volt ++ * value when it is increased as a result of aging ++ * adjustment. ++ * @unaged_floor_volt: The CPR closed-loop floor voltage in microvolts before ++ * any aging adjustment is performed ++ * @unaged_ceiling_volt: The CPR closed-loop ceiling voltage in microvolts ++ * before any aging adjustment is performed ++ * @unaged_open_loop_volt: The CPR open-loop voltage (i.e. initial voltage) in ++ * microvolts before any aging adjusment is performed ++ * @system_volt: The system-supply voltage in microvolts or corners or ++ * levels ++ * @mem_acc_volt: The mem-acc-supply voltage in corners ++ * @proc_freq: Processor frequency in Hertz. For CPR rev. 3 and 4 ++ * conrollers, this field is only used by platform specific ++ * CPR3 driver for interpolation. For CPRh-compliant ++ * controllers, this frequency is also utilized by the ++ * clock driver to determine the corner to CPU clock ++ * frequency mappings. ++ * @cpr_fuse_corner: Fused corner index associated with this virtual corner ++ * (only used by platform specific CPR3 driver for ++ * mapping purposes) ++ * @target_quot: Array of target quotient values to use for each ring ++ * oscillator (RO) for this corner. A value of 0 should be ++ * specified as the target quotient for each RO that is ++ * unused by this corner. ++ * @ro_scale: Array of CPR ring oscillator (RO) scaling factors. The ++ * scaling factor for each RO is defined from RO0 to RO15 ++ * with units of QUOT/V. A value of 0 may be specified for ++ * an RO that is unused. ++ * @ro_mask: Bitmap where each of the 16 LSBs indicate if the ++ * corresponding ROs should be masked for this corner ++ * @irq_en: Bitmap of the CPR interrupts to enable for this corner ++ * @aging_derate: The amount to derate the aging voltage adjustment ++ * determined for the reference corner in units of uV/mV. ++ * E.g. a value of 900 would imply that the adjustment for ++ * this corner should be 90% (900/1000) of that for the ++ * reference corner. ++ * @use_open_loop: Boolean indicating that open-loop (i.e CPR disabled) as ++ * opposed to closed-loop operation must be used for this ++ * corner on CPRh controllers. ++ * @sdelta: The CPR4 controller specific data for this corner. This ++ * field is applicable for CPR4 controllers. ++ * ++ * The value of last_volt is initialized inside of the cpr3_regulator_register() ++ * call with the open_loop_volt value. It can later be updated to the settled ++ * VDD supply voltage. The values for unaged_floor_volt, unaged_ceiling_volt, ++ * and unaged_open_loop_volt are initialized inside of cpr3_regulator_register() ++ * if ctrl->aging_required == true. These three values must be pre-initialized ++ * if cpr3_regulator_register() is called with ctrl->aging_required == false and ++ * ctrl->aging_succeeded == true. ++ * ++ * The values of ro_mask and irq_en are initialized inside of the ++ * cpr3_regulator_register() call. ++ */ ++struct cpr3_corner { ++ int floor_volt; ++ int ceiling_volt; ++ int cold_temp_open_loop_volt; ++ int normal_temp_open_loop_volt; ++ int open_loop_volt; ++ int last_volt; ++ int abs_ceiling_volt; ++ int unaged_floor_volt; ++ int unaged_ceiling_volt; ++ int unaged_open_loop_volt; ++ int system_volt; ++ int mem_acc_volt; ++ u32 proc_freq; ++ int cpr_fuse_corner; ++ u32 target_quot[CPR3_RO_COUNT]; ++ u32 ro_scale[CPR3_RO_COUNT]; ++ u32 ro_mask; ++ u32 irq_en; ++ int aging_derate; ++ bool use_open_loop; ++ struct cpr4_sdelta *sdelta; ++}; ++ ++/** ++ * struct cprh_corner_band - CPRh controller specific data structure which ++ * encapsulates the range of corners and the SDELTA ++ * adjustment table to be applied to the corners within ++ * the min and max bounds of the corner band. ++ * @corner: Corner number which defines the corner band boundary ++ * @sdelta: The SDELTA adjustment table which contains core-count ++ * and temp based margin adjustments that are applicable ++ * to the corner band. ++ */ ++struct cprh_corner_band { ++ int corner; ++ struct cpr4_sdelta *sdelta; ++}; ++ ++/** ++ * struct cpr3_fuse_parameters - CPR4 fuse specific data structure which has ++ * the required fuse parameters need for Close Loop CPR ++ * @(*apss_ro_sel_param)[2]: Pointer to RO select fuse details ++ * @(*apss_init_voltage_param)[2]: Pointer to Target voltage fuse details ++ * @(*apss_target_quot_param)[2]: Pointer to Target quot fuse details ++ * @(*apss_quot_offset_param)[2]: Pointer to quot offset fuse details ++ * @cpr_fusing_rev_param: Pointer to CPR revision fuse details ++ * @apss_speed_bin_param: Pointer to Speed bin fuse details ++ * @cpr_boost_fuse_cfg_param: Pointer to Boost fuse cfg details ++ * @apss_boost_fuse_volt_param: Pointer to Boost fuse volt details ++ * @misc_fuse_volt_adj_param: Pointer to Misc fuse volt fuse details ++ */ ++struct cpr3_fuse_parameters { ++ struct cpr3_fuse_param (*apss_ro_sel_param)[2]; ++ struct cpr3_fuse_param (*apss_init_voltage_param)[2]; ++ struct cpr3_fuse_param (*apss_target_quot_param)[2]; ++ struct cpr3_fuse_param (*apss_quot_offset_param)[2]; ++ struct cpr3_fuse_param *cpr_fusing_rev_param; ++ struct cpr3_fuse_param *apss_speed_bin_param; ++ struct cpr3_fuse_param *cpr_boost_fuse_cfg_param; ++ struct cpr3_fuse_param *apss_boost_fuse_volt_param; ++ struct cpr3_fuse_param *misc_fuse_volt_adj_param; ++}; ++ ++struct cpr4_mem_acc_func { ++ void (*set_mem_acc)(struct regulator_dev *); ++ void (*clear_mem_acc)(struct regulator_dev *); ++}; ++ ++/** ++ * struct cpr4_reg_data - CPR4 regulator specific data structure which is ++ * target specific ++ * @cpr_valid_fuse_count: Number of valid fuse corners ++ * @fuse_ref_volt: Pointer to fuse reference voltage ++ * @fuse_step_volt: CPR step voltage available in fuse ++ * @cpr_clk_rate: CPR clock rate ++ * @boost_fuse_ref_volt: Boost fuse reference voltage ++ * @boost_ceiling_volt: Boost ceiling voltage ++ * @boost_floor_volt: Boost floor voltage ++ * @cpr3_fuse_params: Pointer to CPR fuse parameters ++ * @mem_acc_funcs: Pointer to MEM ACC set/clear functions ++ **/ ++struct cpr4_reg_data { ++ u32 cpr_valid_fuse_count; ++ int *fuse_ref_volt; ++ u32 fuse_step_volt; ++ u32 cpr_clk_rate; ++ int boost_fuse_ref_volt; ++ int boost_ceiling_volt; ++ int boost_floor_volt; ++ struct cpr3_fuse_parameters *cpr3_fuse_params; ++ struct cpr4_mem_acc_func *mem_acc_funcs; ++}; ++/** ++ * struct cpr3_reg_data - CPR3 regulator specific data structure which is ++ * target specific ++ * @cpr_valid_fuse_count: Number of valid fuse corners ++ * @(*init_voltage_param)[2]: Pointer to Target voltage fuse details ++ * @fuse_ref_volt: Pointer to fuse reference voltage ++ * @fuse_step_volt: CPR step voltage available in fuse ++ * @cpr_clk_rate: CPR clock rate ++ * @cpr3_fuse_params: Pointer to CPR fuse parameters ++ **/ ++struct cpr3_reg_data { ++ u32 cpr_valid_fuse_count; ++ struct cpr3_fuse_param (*init_voltage_param)[2]; ++ int *fuse_ref_volt; ++ u32 fuse_step_volt; ++ u32 cpr_clk_rate; ++}; ++ ++/** ++ * struct cpr3_regulator - CPR3 logical regulator instance associated with a ++ * given CPR3 hardware thread ++ * @of_node: Device node associated with the device tree child node ++ * of this CPR3 regulator ++ * @thread: Pointer to the CPR3 thread which manages this CPR3 ++ * regulator ++ * @name: Unique name for this CPR3 regulator which is filled ++ * using the device tree regulator-name property ++ * @rdesc: Regulator description for this CPR3 regulator ++ * @rdev: Regulator device pointer for the regulator registered ++ * for this CPR3 regulator ++ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used ++ * to manage memory circuitry settings based upon CPR3 ++ * regulator output voltage. ++ * @corner: Array of all corners supported by this CPR3 regulator ++ * @corner_count: The number of elements in the corner array ++ * @corner_band: Array of all corner bands supported by CPRh compatible ++ * controllers ++ * @cpr4_regulator_data Target specific cpr4 regulator data ++ * @cpr3_regulator_data Target specific cpr3 regulator data ++ * @corner_band_count: The number of elements in the corner band array ++ * @platform_fuses: Pointer to platform specific CPR fuse data (only used by ++ * platform specific CPR3 driver) ++ * @speed_bin_fuse: Value read from the speed bin fuse parameter ++ * @speed_bins_supported: The number of speed bins supported by the device tree ++ * configuration for this CPR3 regulator ++ * @cpr_rev_fuse: Value read from the CPR fusing revision fuse parameter ++ * @fuse_combo: Platform specific enum value identifying the specific ++ * combination of fuse values found on a given chip ++ * @fuse_combos_supported: The number of fuse combinations supported by the ++ * device tree configuration for this CPR3 regulator ++ * @fuse_corner_count: Number of corners defined by fuse parameters ++ * @fuse_corner_map: Array of length fuse_corner_count which specifies the ++ * highest corner associated with each fuse corner. Note ++ * that each element must correspond to a valid corner ++ * and that element values must be strictly increasing. ++ * Also, it is acceptable for the lowest fuse corner to map ++ * to a corner other than the lowest. Likewise, it is ++ * acceptable for the highest fuse corner to map to a ++ * corner other than the highest. ++ * @fuse_combo_corner_sum: The sum of the corner counts across all fuse combos ++ * @fuse_combo_offset: The device tree property array offset for the selected ++ * fuse combo ++ * @speed_bin_corner_sum: The sum of the corner counts across all speed bins ++ * This may be specified as 0 if per speed bin parsing ++ * support is not required. ++ * @speed_bin_offset: The device tree property array offset for the selected ++ * speed bin ++ * @fuse_combo_corner_band_sum: The sum of the corner band counts across all ++ * fuse combos ++ * @fuse_combo_corner_band_offset: The device tree property array offset for ++ * the corner band count corresponding to the selected ++ * fuse combo ++ * @speed_bin_corner_band_sum: The sum of the corner band counts across all ++ * speed bins. This may be specified as 0 if per speed bin ++ * parsing support is not required ++ * @speed_bin_corner_band_offset: The device tree property array offset for the ++ * corner band count corresponding to the selected speed ++ * bin ++ * @pd_bypass_mask: Bit mask of power domains associated with this CPR3 ++ * regulator ++ * @dynamic_floor_corner: Index identifying the voltage corner for the CPR3 ++ * regulator whose last_volt value should be used as the ++ * global CPR floor voltage if all of the power domains ++ * associated with this CPR3 regulator are bypassed ++ * @uses_dynamic_floor: Boolean flag indicating that dynamic_floor_corner should ++ * be utilized for the CPR3 regulator ++ * @current_corner: Index identifying the currently selected voltage corner ++ * for the CPR3 regulator or less than 0 if no corner has ++ * been requested ++ * @last_closed_loop_corner: Index identifying the last voltage corner for the ++ * CPR3 regulator which was configured when operating in ++ * CPR closed-loop mode or less than 0 if no corner has ++ * been requested. CPR registers are only written to when ++ * using closed-loop mode. ++ * @aggregated: Boolean flag indicating that this CPR3 regulator ++ * participated in the last aggregation event ++ * @debug_corner: Index identifying voltage corner used for displaying ++ * corner configuration values in debugfs ++ * @vreg_enabled: Boolean defining the enable state of the CPR3 ++ * regulator's regulator within the regulator framework. ++ * @aging_allowed: Boolean defining if CPR aging adjustments are allowed ++ * for this CPR3 regulator given the fuse combo of the ++ * device ++ * @aging_allow_open_loop_adj: Boolean defining if the open-loop voltage of each ++ * corner of this regulator should be adjusted as a result ++ * of an aging measurement. This flag can be set to false ++ * when the open-loop voltage adjustments have been ++ * specified such that they include the maximum possible ++ * aging adjustment. This flag is only used if ++ * aging_allowed == true. ++ * @aging_corner: The corner that should be configured for this regulator ++ * when an aging measurement is performed. ++ * @aging_max_adjust_volt: The maximum aging voltage margin in microvolts that ++ * may be added to the target quotients of this regulator. ++ * A value of 0 may be specified if this regulator does not ++ * require any aging adjustment. ++ * @allow_core_count_adj: Core count adjustments are allowed for this regulator. ++ * @allow_temp_adj: Temperature based adjustments are allowed for this ++ * regulator. ++ * @max_core_count: Maximum number of cores considered for core count ++ * adjustment logic. ++ * @allow_boost: Voltage boost allowed for this regulator. ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements current_corner, last_closed_loop_corner, aggregated, debug_corner, ++ * and vreg_enabled are state variables. ++ */ ++struct cpr3_regulator { ++ struct device_node *of_node; ++ struct cpr3_thread *thread; ++ const char *name; ++ struct regulator_desc rdesc; ++ struct regulator_dev *rdev; ++ struct regulator *mem_acc_regulator; ++ struct cpr3_corner *corner; ++ int corner_count; ++ struct cprh_corner_band *corner_band; ++ struct cpr4_reg_data *cpr4_regulator_data; ++ struct cpr3_reg_data *cpr3_regulator_data; ++ u32 corner_band_count; ++ ++ void *platform_fuses; ++ int speed_bin_fuse; ++ int speed_bins_supported; ++ int cpr_rev_fuse; ++ int part_type; ++ int part_type_supported; ++ int fuse_combo; ++ int fuse_combos_supported; ++ int fuse_corner_count; ++ int *fuse_corner_map; ++ int fuse_combo_corner_sum; ++ int fuse_combo_offset; ++ int speed_bin_corner_sum; ++ int speed_bin_offset; ++ int fuse_combo_corner_band_sum; ++ int fuse_combo_corner_band_offset; ++ int speed_bin_corner_band_sum; ++ int speed_bin_corner_band_offset; ++ u32 pd_bypass_mask; ++ int dynamic_floor_corner; ++ bool uses_dynamic_floor; ++ ++ int current_corner; ++ int last_closed_loop_corner; ++ bool aggregated; ++ int debug_corner; ++ bool vreg_enabled; ++ ++ bool aging_allowed; ++ bool aging_allow_open_loop_adj; ++ int aging_corner; ++ int aging_max_adjust_volt; ++ ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ int max_core_count; ++ bool allow_boost; ++}; ++ ++/** ++ * struct cpr3_thread - CPR3 hardware thread data structure ++ * @thread_id: Hardware thread ID ++ * @of_node: Device node associated with the device tree child node ++ * of this CPR3 thread ++ * @ctrl: Pointer to the CPR3 controller which manages this thread ++ * @vreg: Array of CPR3 regulators handled by the CPR3 thread ++ * @vreg_count: Number of elements in the vreg array ++ * @aggr_corner: CPR corner containing the in process aggregated voltage ++ * and target quotient configurations which will be applied ++ * @last_closed_loop_aggr_corner: CPR corner containing the most recent ++ * configurations which were written into hardware ++ * registers when operating in closed loop mode (i.e. with ++ * CPR enabled) ++ * @consecutive_up: The number of consecutive CPR step up events needed to ++ * to trigger an up interrupt ++ * @consecutive_down: The number of consecutive CPR step down events needed to ++ * to trigger a down interrupt ++ * @up_threshold: The number CPR error steps required to generate an up ++ * event ++ * @down_threshold: The number CPR error steps required to generate a down ++ * event ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements aggr_corner and last_closed_loop_aggr_corner are state variables. ++ */ ++struct cpr3_thread { ++ u32 thread_id; ++ struct device_node *of_node; ++ struct cpr3_controller *ctrl; ++ struct cpr3_regulator *vreg; ++ int vreg_count; ++ struct cpr3_corner aggr_corner; ++ struct cpr3_corner last_closed_loop_aggr_corner; ++ ++ u32 consecutive_up; ++ u32 consecutive_down; ++ u32 up_threshold; ++ u32 down_threshold; ++}; ++ ++/* Per CPR controller data */ ++/** ++ * enum cpr3_mem_acc_corners - Constants which define the number of mem-acc ++ * regulator corners available in the mem-acc corner map array. ++ * %CPR3_MEM_ACC_LOW_CORNER: Index in mem-acc corner map array mapping to the ++ * mem-acc regulator corner ++ * to be used for low voltage vdd supply ++ * %CPR3_MEM_ACC_HIGH_CORNER: Index in mem-acc corner map array mapping to the ++ * mem-acc regulator corner to be used for high ++ * voltage vdd supply ++ * %CPR3_MEM_ACC_CORNERS: Number of elements in the mem-acc corner map ++ * array ++ */ ++enum cpr3_mem_acc_corners { ++ CPR3_MEM_ACC_LOW_CORNER = 0, ++ CPR3_MEM_ACC_HIGH_CORNER = 1, ++ CPR3_MEM_ACC_CORNERS = 2, ++}; ++ ++/** ++ * enum cpr3_count_mode - CPR3 controller count mode which defines the ++ * method that CPR sensor data is acquired ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MIN: Capture all CPR sensor readings ++ * simultaneously and report the minimum ++ * value seen in successive measurements ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MAX: Capture all CPR sensor readings ++ * simultaneously and report the maximum ++ * value seen in successive measurements ++ * %CPR3_COUNT_MODE_STAGGERED: Read one sensor at a time in a ++ * sequential fashion ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_AGE: Capture all CPR aging sensor readings ++ * simultaneously. ++ */ ++enum cpr3_count_mode { ++ CPR3_COUNT_MODE_ALL_AT_ONCE_MIN = 0, ++ CPR3_COUNT_MODE_ALL_AT_ONCE_MAX = 1, ++ CPR3_COUNT_MODE_STAGGERED = 2, ++ CPR3_COUNT_MODE_ALL_AT_ONCE_AGE = 3, ++}; ++ ++/** ++ * enum cpr_controller_type - supported CPR controller hardware types ++ * %CPR_CTRL_TYPE_CPR3: HW has CPR3 controller ++ * %CPR_CTRL_TYPE_CPR4: HW has CPR4 controller ++ */ ++enum cpr_controller_type { ++ CPR_CTRL_TYPE_CPR3, ++ CPR_CTRL_TYPE_CPR4, ++}; ++ ++/** ++ * cpr_setting - supported CPR global settings ++ * %CPR_DEFAULT: default mode from dts will be used ++ * %CPR_DISABLED: ceiling voltage will be used for all the corners ++ * %CPR_OPEN_LOOP_EN: CPR will work in OL ++ * %CPR_CLOSED_LOOP_EN: CPR will work in CL, if supported ++ */ ++enum cpr_setting { ++ CPR_DEFAULT = 0, ++ CPR_DISABLED = 1, ++ CPR_OPEN_LOOP_EN = 2, ++ CPR_CLOSED_LOOP_EN = 3, ++}; ++ ++/** ++ * struct cpr3_aging_sensor_info - CPR3 aging sensor information ++ * @sensor_id The index of the CPR3 sensor to be used in the aging ++ * measurement. ++ * @ro_scale The CPR ring oscillator (RO) scaling factor for the ++ * aging sensor with units of QUOT/V. ++ * @init_quot_diff: The fused quotient difference between aged and un-aged ++ * paths that was measured at manufacturing time. ++ * @measured_quot_diff: The quotient difference measured at runtime. ++ * @bypass_mask: Bit mask of the CPR sensors that must be bypassed during ++ * the aging measurement for this sensor ++ * ++ * This structure contains both configuration and runtime state data. The ++ * element measured_quot_diff is a state variable. ++ */ ++struct cpr3_aging_sensor_info { ++ u32 sensor_id; ++ u32 ro_scale; ++ int init_quot_diff; ++ int measured_quot_diff; ++ u32 bypass_mask[CPR3_MAX_SENSOR_COUNT / 32]; ++}; ++ ++/** ++ * struct cpr3_reg_info - Register information data structure ++ * @name: Register name ++ * @addr: Register physical address ++ * @value: Register content ++ * @virt_addr: Register virtual address ++ * ++ * This data structure is used to dump some critical register contents ++ * when the device crashes due to a kernel panic. ++ */ ++struct cpr3_reg_info { ++ const char *name; ++ u32 addr; ++ u32 value; ++ void __iomem *virt_addr; ++}; ++ ++/** ++ * struct cpr3_panic_regs_info - Data structure to dump critical register ++ * contents. ++ * @reg_count: Number of elements in the regs array ++ * @regs: Array of critical registers information ++ * ++ * This data structure is used to dump critical register contents when ++ * the device crashes due to a kernel panic. ++ */ ++struct cpr3_panic_regs_info { ++ int reg_count; ++ struct cpr3_reg_info *regs; ++}; ++ ++/** ++ * struct cpr3_controller - CPR3 controller data structure ++ * @dev: Device pointer for the CPR3 controller device ++ * @name: Unique name for the CPR3 controller ++ * @ctrl_id: Controller ID corresponding to the VDD supply number ++ * that this CPR3 controller manages. ++ * @cpr_ctrl_base: Virtual address of the CPR3 controller base register ++ * @fuse_base: Virtual address of fuse row 0 ++ * @aging_possible_reg: Virtual address of an optional platform-specific ++ * register that must be ready to determine if it is ++ * possible to perform an aging measurement. ++ * @list: list head used in a global cpr3-regulator list so that ++ * cpr3-regulator structs can be found easily in RAM dumps ++ * @thread: Array of CPR3 threads managed by the CPR3 controller ++ * @thread_count: Number of elements in the thread array ++ * @sensor_owner: Array of thread IDs indicating which thread owns a given ++ * CPR sensor ++ * @sensor_count: The number of CPR sensors found on the CPR loop managed ++ * by this CPR controller. Must be equal to the number of ++ * elements in the sensor_owner array ++ * @soc_revision: Revision number of the SoC. This may be unused by ++ * platforms that do not have different behavior for ++ * different SoC revisions. ++ * @lock: Mutex lock used to ensure mutual exclusion between ++ * all of the threads associated with the controller ++ * @vdd_regulator: Pointer to the VDD supply regulator which this CPR3 ++ * controller manages ++ * @system_regulator: Pointer to the optional system-supply regulator upon ++ * which the VDD supply regulator depends. ++ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used ++ * to manage memory circuitry settings based upon the ++ * VDD supply output voltage. ++ * @vdd_limit_regulator: Pointer to the VDD supply limit regulator which is used ++ * for hardware closed-loop in order specify ceiling and ++ * floor voltage limits (platform specific) ++ * @system_supply_max_volt: Voltage in microvolts which corresponds to the ++ * absolute ceiling voltage of the system-supply ++ * @mem_acc_threshold_volt: mem-acc threshold voltage in microvolts ++ * @mem_acc_corner_map: mem-acc regulator corners mapping to low and high ++ * voltage mem-acc settings for the memories powered by ++ * this CPR3 controller and its associated CPR3 regulators ++ * @mem_acc_crossover_volt: Voltage in microvolts corresponding to the voltage ++ * that the VDD supply must be set to while a MEM ACC ++ * switch is in progress. This element must be initialized ++ * for CPRh controllers when a MEM ACC threshold voltage is ++ * defined. ++ * @core_clk: Pointer to the CPR3 controller core clock ++ * @iface_clk: Pointer to the CPR3 interface clock (platform specific) ++ * @bus_clk: Pointer to the CPR3 bus clock (platform specific) ++ * @irq: CPR interrupt number ++ * @irq_affinity_mask: The cpumask for the CPUs which the CPR interrupt should ++ * have affinity for ++ * @cpu_hotplug_notifier: CPU hotplug notifier used to reset IRQ affinity when a ++ * CPU is brought back online ++ * @ceiling_irq: Interrupt number for the interrupt that is triggered ++ * when hardware closed-loop attempts to exceed the ceiling ++ * voltage ++ * @apm: Handle to the array power mux (APM) ++ * @apm_threshold_volt: Voltage in microvolts which defines the threshold ++ * voltage to determine the APM supply selection for ++ * each corner ++ * @apm_crossover_volt: Voltage in microvolts corresponding to the voltage that ++ * the VDD supply must be set to while an APM switch is in ++ * progress. This element must be initialized for CPRh ++ * controllers when an APM threshold voltage is defined ++ * @apm_adj_volt: Minimum difference between APM threshold voltage and ++ * open-loop voltage which allows the APM threshold voltage ++ * to be used as a ceiling ++ * @apm_high_supply: APM supply to configure if VDD voltage is greater than ++ * or equal to the APM threshold voltage ++ * @apm_low_supply: APM supply to configure if the VDD voltage is less than ++ * the APM threshold voltage ++ * @base_volt: Minimum voltage in microvolts supported by the VDD ++ * supply managed by this CPR controller ++ * @corner_switch_delay_time: The delay time in nanoseconds used by the CPR ++ * controller to wait for voltage settling before ++ * acknowledging the OSM block after corner changes ++ * @cpr_clock_rate: CPR reference clock frequency in Hz. ++ * @sensor_time: The time in nanoseconds that each sensor takes to ++ * perform a measurement. ++ * @loop_time: The time in nanoseconds between consecutive CPR ++ * measurements. ++ * @up_down_delay_time: The time to delay in nanoseconds between consecutive CPR ++ * measurements when the last measurement recommended ++ * increasing or decreasing the vdd-supply voltage. ++ * (platform specific) ++ * @idle_clocks: Number of CPR reference clock ticks that the CPR ++ * controller waits in transitional states. ++ * @step_quot_init_min: The default minimum CPR step quotient value. The step ++ * quotient is the number of additional ring oscillator ++ * ticks observed when increasing one step in vdd-supply ++ * output voltage. ++ * @step_quot_init_max: The default maximum CPR step quotient value. ++ * @step_volt: Step size in microvolts between available set points ++ * of the VDD supply ++ * @down_error_step_limit: CPR4 hardware closed-loop down error step limit which ++ * defines the maximum number of VDD supply regulator steps ++ * that the voltage may be reduced as the result of a ++ * single CPR measurement. ++ * @up_error_step_limit: CPR4 hardware closed-loop up error step limit which ++ * defines the maximum number of VDD supply regulator steps ++ * that the voltage may be increased as the result of a ++ * single CPR measurement. ++ * @count_mode: CPR controller count mode ++ * @count_repeat: Number of times to perform consecutive sensor ++ * measurements when using all-at-once count modes. ++ * @proc_clock_throttle: Defines the processor clock frequency throttling ++ * register value to use. This can be used to reduce the ++ * clock frequency when a power domain exits a low power ++ * mode until CPR settles at a new voltage. ++ * (platform specific) ++ * @cpr_allowed_hw: Boolean which indicates if closed-loop CPR operation is ++ * permitted for a given chip based upon hardware fuse ++ * values ++ * @cpr_allowed_sw: Boolean which indicates if closed-loop CPR operation is ++ * permitted based upon software policies ++ * @supports_hw_closed_loop: Boolean which indicates if this CPR3/4 controller ++ * physically supports hardware closed-loop CPR operation ++ * @use_hw_closed_loop: Boolean which indicates that this controller will be ++ * using hardware closed-loop operation in place of ++ * software closed-loop operation. ++ * @ctrl_type: CPR controller type ++ * @saw_use_unit_mV: Boolean which indicates the unit used in SAW PVC ++ * interface is mV. ++ * @aggr_corner: CPR corner containing the most recently aggregated ++ * voltage configurations which are being used currently ++ * @cpr_enabled: Boolean which indicates that the CPR controller is ++ * enabled and operating in closed-loop mode. CPR clocks ++ * have been prepared and enabled whenever this flag is ++ * true. ++ * @last_corner_was_closed_loop: Boolean indicating if the last known corners ++ * were updated during closed loop operation. ++ * @cpr_suspended: Boolean which indicates that CPR has been temporarily ++ * disabled while enterring system suspend. ++ * @debugfs: Pointer to the debugfs directory of this CPR3 controller ++ * @aging_ref_volt: Reference voltage in microvolts to configure when ++ * performing CPR aging measurements. ++ * @aging_vdd_mode: vdd-supply regulator mode to configure before performing ++ * a CPR aging measurement. It should be one of ++ * REGULATOR_MODE_*. ++ * @aging_complete_vdd_mode: vdd-supply regulator mode to configure after ++ * performing a CPR aging measurement. It should be one of ++ * REGULATOR_MODE_*. ++ * @aging_ref_adjust_volt: The reference aging voltage margin in microvolts that ++ * should be added to the target quotients of the ++ * regulators managed by this controller after derating. ++ * @aging_required: Flag which indicates that a CPR aging measurement still ++ * needs to be performed for this CPR3 controller. ++ * @aging_succeeded: Flag which indicates that a CPR aging measurement has ++ * completed successfully. ++ * @aging_failed: Flag which indicates that a CPR aging measurement has ++ * failed to complete successfully. ++ * @aging_sensor: Array of CPR3 aging sensors which are used to perform ++ * aging measurements at a runtime. ++ * @aging_sensor_count: Number of elements in the aging_sensor array ++ * @aging_possible_mask: Optional bitmask used to mask off the ++ * aging_possible_reg register. ++ * @aging_possible_val: Optional value that the masked aging_possible_reg ++ * register must have in order for a CPR aging measurement ++ * to be possible. ++ * @step_quot_fixed: Fixed step quotient value used for target quotient ++ * adjustment if use_dynamic_step_quot is not set. ++ * This parameter is only relevant for CPR4 controllers ++ * when using the per-online-core or per-temperature ++ * adjustments. ++ * @initial_temp_band: Temperature band used for calculation of base-line ++ * target quotients (fused). ++ * @use_dynamic_step_quot: Boolean value which indicates that margin adjustment ++ * of target quotient will be based on the step quotient ++ * calculated dynamically in hardware for each RO. ++ * @allow_core_count_adj: Core count adjustments are allowed for this controller ++ * @allow_temp_adj: Temperature based adjustments are allowed for ++ * this controller ++ * @allow_boost: Voltage boost allowed for this controller. ++ * @temp_band_count: Number of temperature bands used for temperature based ++ * adjustment logic ++ * @temp_points: Array of temperature points in decidegrees Celsius used ++ * to specify the ranges for selected temperature bands. ++ * The array must have (temp_band_count - 1) elements ++ * allocated. ++ * @temp_sensor_id_start: Start ID of temperature sensors used for temperature ++ * based adjustments. ++ * @temp_sensor_id_end: End ID of temperature sensors used for temperature ++ * based adjustments. ++ * @voltage_settling_time: The time in nanoseconds that it takes for the ++ * VDD supply voltage to settle after being increased or ++ * decreased by step_volt microvolts which is used when ++ * SDELTA voltage margin adjustments are applied. ++ * @cpr_global_setting: Global setting for this CPR controller ++ * @panic_regs_info: Array of panic registers information which provides the ++ * list of registers to dump when the device crashes. ++ * @panic_notifier: Notifier block registered to global panic notifier list. ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled, ++ * last_corner_was_closed_loop, cpr_suspended, aging_ref_adjust_volt, ++ * aging_required, aging_succeeded, and aging_failed are state variables. ++ * ++ * The apm* elements do not need to be initialized if the VDD supply managed by ++ * the CPR3 controller does not utilize an APM. ++ * ++ * The elements step_quot_fixed, initial_temp_band, allow_core_count_adj, ++ * allow_temp_adj and temp* need to be initialized for CPR4 controllers which ++ * are using per-online-core or per-temperature adjustments. ++ */ ++struct cpr3_controller { ++ struct device *dev; ++ const char *name; ++ int ctrl_id; ++ void __iomem *cpr_ctrl_base; ++ void __iomem *fuse_base; ++ void __iomem *aging_possible_reg; ++ struct list_head list; ++ struct cpr3_thread *thread; ++ int thread_count; ++ u8 *sensor_owner; ++ int sensor_count; ++ int soc_revision; ++ struct mutex lock; ++ struct regulator *vdd_regulator; ++ struct regulator *system_regulator; ++ struct regulator *mem_acc_regulator; ++ struct regulator *vdd_limit_regulator; ++ int system_supply_max_volt; ++ int mem_acc_threshold_volt; ++ int mem_acc_corner_map[CPR3_MEM_ACC_CORNERS]; ++ int mem_acc_crossover_volt; ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ struct clk *bus_clk; ++ int irq; ++ struct cpumask irq_affinity_mask; ++ struct notifier_block cpu_hotplug_notifier; ++ int ceiling_irq; ++ struct msm_apm_ctrl_dev *apm; ++ int apm_threshold_volt; ++ int apm_crossover_volt; ++ int apm_adj_volt; ++ enum msm_apm_supply apm_high_supply; ++ enum msm_apm_supply apm_low_supply; ++ int base_volt; ++ u32 corner_switch_delay_time; ++ u32 cpr_clock_rate; ++ u32 sensor_time; ++ u32 loop_time; ++ u32 up_down_delay_time; ++ u32 idle_clocks; ++ u32 step_quot_init_min; ++ u32 step_quot_init_max; ++ int step_volt; ++ u32 down_error_step_limit; ++ u32 up_error_step_limit; ++ enum cpr3_count_mode count_mode; ++ u32 count_repeat; ++ u32 proc_clock_throttle; ++ bool cpr_allowed_hw; ++ bool cpr_allowed_sw; ++ bool supports_hw_closed_loop; ++ bool use_hw_closed_loop; ++ enum cpr_controller_type ctrl_type; ++ bool saw_use_unit_mV; ++ struct cpr3_corner aggr_corner; ++ bool cpr_enabled; ++ bool last_corner_was_closed_loop; ++ bool cpr_suspended; ++ struct dentry *debugfs; ++ ++ int aging_ref_volt; ++ unsigned int aging_vdd_mode; ++ unsigned int aging_complete_vdd_mode; ++ int aging_ref_adjust_volt; ++ bool aging_required; ++ bool aging_succeeded; ++ bool aging_failed; ++ struct cpr3_aging_sensor_info *aging_sensor; ++ int aging_sensor_count; ++ u32 cur_sensor_state; ++ u32 aging_possible_mask; ++ u32 aging_possible_val; ++ ++ u32 step_quot_fixed; ++ u32 initial_temp_band; ++ bool use_dynamic_step_quot; ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ bool allow_boost; ++ int temp_band_count; ++ int *temp_points; ++ u32 temp_sensor_id_start; ++ u32 temp_sensor_id_end; ++ u32 voltage_settling_time; ++ enum cpr_setting cpr_global_setting; ++ struct cpr3_panic_regs_info *panic_regs_info; ++ struct notifier_block panic_notifier; ++}; ++ ++/* Used for rounding voltages to the closest physically available set point. */ ++#define CPR3_ROUND(n, d) (DIV_ROUND_UP(n, d) * (d)) ++ ++#define cpr3_err(cpr3_thread, message, ...) \ ++ pr_err("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++#define cpr3_info(cpr3_thread, message, ...) \ ++ pr_info("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++#define cpr3_debug(cpr3_thread, message, ...) \ ++ pr_debug("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++ ++/* ++ * Offset subtracted from voltage corner values passed in from the regulator ++ * framework in order to get internal voltage corner values. This is needed ++ * since the regulator framework treats 0 as an error value at regulator ++ * registration time. ++ */ ++#define CPR3_CORNER_OFFSET 1 ++ ++#ifdef CONFIG_REGULATOR_CPR3 ++ ++int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++int cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++int cpr3_regulator_unregister(struct cpr3_controller *ctrl); ++int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl); ++int cpr3_regulator_suspend(struct cpr3_controller *ctrl); ++int cpr3_regulator_resume(struct cpr3_controller *ctrl); ++ ++int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, ++ u32 max_thread_id); ++int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev); ++int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end); ++int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value); ++int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, ++ int fuse_len); ++u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x); ++int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg); ++int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max); ++int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max); ++int cpr3_parse_common_thread_data(struct cpr3_thread *thread); ++int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl); ++int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl); ++int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg); ++void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg); ++int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg); ++void cpr3_print_quots(struct cpr3_regulator *vreg); ++int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt); ++int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt); ++int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, ++ int *fuse_volt); ++int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg); ++int cpr3_quot_adjustment(int ro_scale, int volt_adjust); ++int cpr3_voltage_adjustment(int ro_scale, int quot_adjust); ++int cpr3_parse_closed_loop_voltage_adjustments(struct cpr3_regulator *vreg, ++ u64 *ro_sel, int *volt_adjust, ++ int *volt_adjust_fuse, int *ro_scale); ++int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg, ++ bool use_corner_band); ++int cpr3_apm_init(struct cpr3_controller *ctrl); ++int cpr3_mem_acc_init(struct cpr3_regulator *vreg); ++void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg); ++void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg); ++int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust); ++int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold); ++int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp); ++bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg); ++ ++#else ++ ++static inline int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int ++cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int ++cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_suspend(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_resume(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_get_thread_name(struct cpr3_thread *thread, ++ struct device_node *thread_node) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_allocate_threads(struct cpr3_controller *ctrl, ++ u32 min_thread_id, u32 max_thread_id) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end) ++{ ++ return 0; ++} ++ ++static inline int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_convert_open_loop_voltage_fuse(int ref_volt, ++ int step_volt, u32 fuse, int fuse_len) ++{ ++ return -EPERM; ++} ++ ++static inline u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) ++{ ++ return 0; ++} ++ ++static inline int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_corner_band_array_property( ++ struct cpr3_regulator *vreg, const char *prop_name, ++ int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_thread_u32(struct cpr3_thread *thread, ++ const char *propname, u32 *out_value, u32 value_min, ++ u32 value_max) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, ++ const char *propname, u32 *out_value, u32 value_min, ++ u32 value_max) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_thread_data(struct cpr3_thread *thread) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ return -EPERM; ++} ++ ++static inline int ++cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline void cpr3_open_loop_voltage_as_ceiling( ++ struct cpr3_regulator *vreg) ++{ ++ return; ++} ++ ++static inline int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline void cpr3_print_quots(struct cpr3_regulator *vreg) ++{ ++ return; ++} ++ ++static inline int ++cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int ++cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_adjust_fused_open_loop_voltages( ++ struct cpr3_regulator *vreg, int *fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_quot_adjustment(int ro_scale, int volt_adjust) ++{ ++ return 0; ++} ++ ++static inline int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) ++{ ++ return 0; ++} ++ ++static inline int cpr3_parse_closed_loop_voltage_adjustments( ++ struct cpr3_regulator *vreg, u64 *ro_sel, ++ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) ++{ ++ return 0; ++} ++ ++static inline int cpr4_parse_core_count_temp_voltage_adj( ++ struct cpr3_regulator *vreg, bool use_corner_band) ++{ ++ return 0; ++} ++ ++static inline int cpr3_apm_init(struct cpr3_controller *ctrl) ++{ ++ return 0; ++} ++ ++static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg) ++{ ++ return 0; ++} ++ ++static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) ++{ ++} ++ ++static inline void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) ++{ ++} ++ ++static inline int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust) ++{ ++ return 0; ++} ++ ++static inline int ++cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold) ++{ ++ return 0; ++} ++ ++static inline bool ++cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) ++{ ++ return false; ++} ++ ++static inline int ++cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) ++{ ++ return 0; ++} ++#endif /* CONFIG_REGULATOR_CPR3 */ ++ ++#endif /* __REGULATOR_CPR_REGULATOR_H__ */ +--- /dev/null ++++ b/drivers/regulator/cpr3-util.c +@@ -0,0 +1,2750 @@ ++/* ++ * Copyright (c) 2015-2017, 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. ++ */ ++ ++/* ++ * This file contains utility functions to be used by platform specific CPR3 ++ * regulator drivers. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define BYTES_PER_FUSE_ROW 8 ++#define MAX_FUSE_ROW_BIT 63 ++ ++#define CPR3_CONSECUTIVE_UP_DOWN_MIN 0 ++#define CPR3_CONSECUTIVE_UP_DOWN_MAX 15 ++#define CPR3_UP_DOWN_THRESHOLD_MIN 0 ++#define CPR3_UP_DOWN_THRESHOLD_MAX 31 ++#define CPR3_STEP_QUOT_MIN 0 ++#define CPR3_STEP_QUOT_MAX 63 ++#define CPR3_IDLE_CLOCKS_MIN 0 ++#define CPR3_IDLE_CLOCKS_MAX 31 ++ ++/* This constant has units of uV/mV so 1000 corresponds to 100%. */ ++#define CPR3_AGING_DERATE_UNITY 1000 ++ ++/** ++ * cpr3_allocate_regulators() - allocate and initialize CPR3 regulators for a ++ * given thread based upon device tree data ++ * @thread: Pointer to the CPR3 thread ++ * ++ * This function allocates the thread->vreg array based upon the number of ++ * device tree regulator subnodes. It also initializes generic elements of each ++ * regulator struct such as name, of_node, and thread. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_allocate_regulators(struct cpr3_thread *thread) ++{ ++ struct device_node *node; ++ int i, rc; ++ ++ thread->vreg_count = 0; ++ ++ for_each_available_child_of_node(thread->of_node, node) { ++ thread->vreg_count++; ++ } ++ ++ thread->vreg = devm_kcalloc(thread->ctrl->dev, thread->vreg_count, ++ sizeof(*thread->vreg), GFP_KERNEL); ++ if (!thread->vreg) ++ return -ENOMEM; ++ ++ i = 0; ++ for_each_available_child_of_node(thread->of_node, node) { ++ thread->vreg[i].of_node = node; ++ thread->vreg[i].thread = thread; ++ ++ rc = of_property_read_string(node, "regulator-name", ++ &thread->vreg[i].name); ++ if (rc) { ++ dev_err(thread->ctrl->dev, "could not find regulator name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_allocate_threads() - allocate and initialize CPR3 threads for a given ++ * controller based upon device tree data ++ * @ctrl: Pointer to the CPR3 controller ++ * @min_thread_id: Minimum allowed hardware thread ID for this controller ++ * @max_thread_id: Maximum allowed hardware thread ID for this controller ++ * ++ * This function allocates the ctrl->thread array based upon the number of ++ * device tree thread subnodes. It also initializes generic elements of each ++ * thread struct such as thread_id, of_node, ctrl, and vreg array. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, ++ u32 max_thread_id) ++{ ++ struct device *dev = ctrl->dev; ++ struct device_node *thread_node; ++ int i, j, rc; ++ ++ ctrl->thread_count = 0; ++ ++ for_each_available_child_of_node(dev->of_node, thread_node) { ++ ctrl->thread_count++; ++ } ++ ++ ctrl->thread = devm_kcalloc(dev, ctrl->thread_count, ++ sizeof(*ctrl->thread), GFP_KERNEL); ++ if (!ctrl->thread) ++ return -ENOMEM; ++ ++ i = 0; ++ for_each_available_child_of_node(dev->of_node, thread_node) { ++ ctrl->thread[i].of_node = thread_node; ++ ctrl->thread[i].ctrl = ctrl; ++ ++ rc = of_property_read_u32(thread_node, "qcom,cpr-thread-id", ++ &ctrl->thread[i].thread_id); ++ if (rc) { ++ dev_err(dev, "could not read DT property qcom,cpr-thread-id, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread[i].thread_id < min_thread_id || ++ ctrl->thread[i].thread_id > max_thread_id) { ++ dev_err(dev, "invalid thread id = %u; not within [%u, %u]\n", ++ ctrl->thread[i].thread_id, min_thread_id, ++ max_thread_id); ++ return -EINVAL; ++ } ++ ++ /* Verify that the thread ID is unique for all child nodes. */ ++ for (j = 0; j < i; j++) { ++ if (ctrl->thread[j].thread_id ++ == ctrl->thread[i].thread_id) { ++ dev_err(dev, "duplicate thread id = %u found\n", ++ ctrl->thread[i].thread_id); ++ return -EINVAL; ++ } ++ } ++ ++ rc = cpr3_allocate_regulators(&ctrl->thread[i]); ++ if (rc) ++ return rc; ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_map_fuse_base() - ioremap the base address of the fuse region ++ * @ctrl: Pointer to the CPR3 controller ++ * @pdev: Platform device pointer for the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse_base"); ++ if (!res || !res->start) { ++ dev_err(&pdev->dev, "fuse base address is missing\n"); ++ return -ENXIO; ++ } ++ ++ ctrl->fuse_base = devm_ioremap(&pdev->dev, res->start, ++ resource_size(res)); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_read_tcsr_setting - reads the CPR setting bits from TCSR register ++ * @ctrl: Pointer to the CPR3 controller ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @start: start bit in TCSR register ++ * @end: end bit in TCSR register ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end) ++{ ++ struct resource *res; ++ void __iomem *tcsr_reg; ++ u32 val; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "cpr_tcsr_reg"); ++ if (!res || !res->start) ++ return 0; ++ ++ tcsr_reg = ioremap(res->start, resource_size(res)); ++ if (!tcsr_reg) { ++ dev_err(&pdev->dev, "tcsr ioremap failed\n"); ++ return 0; ++ } ++ ++ val = readl_relaxed(tcsr_reg); ++ val &= GENMASK(end, start); ++ val >>= start; ++ ++ switch (val) { ++ case 1: ++ ctrl->cpr_global_setting = CPR_DISABLED; ++ break; ++ case 2: ++ ctrl->cpr_global_setting = CPR_OPEN_LOOP_EN; ++ break; ++ case 3: ++ ctrl->cpr_global_setting = CPR_CLOSED_LOOP_EN; ++ break; ++ default: ++ ctrl->cpr_global_setting = CPR_DEFAULT; ++ } ++ ++ iounmap(tcsr_reg); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_read_fuse_param() - reads a CPR3 fuse parameter out of eFuses ++ * @fuse_base_addr: Virtual memory address of the eFuse base address ++ * @param: Null terminated array of fuse param segments to read ++ * from ++ * @param_value: Output with value read from the eFuses ++ * ++ * This function reads from each of the parameter segments listed in the param ++ * array and concatenates their values together. Reading stops when an element ++ * is reached which has all 0 struct values. The total number of bits specified ++ * for the fuse parameter across all segments must be less than or equal to 64. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value) ++{ ++ u64 fuse_val, val; ++ int bits; ++ int bits_total = 0; ++ ++ *param_value = 0; ++ ++ while (param->row || param->bit_start || param->bit_end) { ++ if (param->bit_start > param->bit_end ++ || param->bit_end > MAX_FUSE_ROW_BIT) { ++ pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n", ++ param->row, param->bit_start, param->bit_end); ++ return -EINVAL; ++ } ++ ++ bits = param->bit_end - param->bit_start + 1; ++ if (bits_total + bits > 64) { ++ pr_err("Invalid fuse parameter segments; total bits = %d\n", ++ bits_total + bits); ++ return -EINVAL; ++ } ++ ++ fuse_val = readq_relaxed(fuse_base_addr ++ + param->row * BYTES_PER_FUSE_ROW); ++ val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1); ++ *param_value |= val << bits_total; ++ bits_total += bits; ++ ++ param++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_convert_open_loop_voltage_fuse() - converts an open loop voltage fuse ++ * value into an absolute voltage with units of microvolts ++ * @ref_volt: Reference voltage in microvolts ++ * @step_volt: The step size in microvolts of the fuse LSB ++ * @fuse: Open loop voltage fuse value ++ * @fuse_len: The bit length of the fuse value ++ * ++ * The MSB of the fuse parameter corresponds to a sign bit. If it is set, then ++ * the lower bits correspond to the number of steps to go down from the ++ * reference voltage. If it is not set, then the lower bits correspond to the ++ * number of steps to go up from the reference voltage. ++ */ ++int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, ++ int fuse_len) ++{ ++ int sign, steps; ++ ++ sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1; ++ steps = fuse & ((1 << (fuse_len - 1)) - 1); ++ ++ return ref_volt + sign * steps * step_volt; ++} ++ ++/** ++ * cpr3_interpolate() - performs linear interpolation ++ * @x1 Lower known x value ++ * @y1 Lower known y value ++ * @x2 Upper known x value ++ * @y2 Upper known y value ++ * @x Intermediate x value ++ * ++ * Returns y where (x, y) falls on the line between (x1, y1) and (x2, y2). ++ * It is required that x1 < x2, y1 <= y2, and x1 <= x <= x2. If these ++ * conditions are not met, then y2 will be returned. ++ */ ++u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) ++{ ++ u64 temp; ++ ++ if (x1 >= x2 || y1 > y2 || x1 > x || x > x2) ++ return y2; ++ ++ temp = (x2 - x) * (y2 - y1); ++ do_div(temp, (u32)(x2 - x1)); ++ ++ return y2 - temp; ++} ++ ++/** ++ * cpr3_parse_array_property() - fill an array from a portion of the values ++ * specified for a device tree property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each tuple ++ * @out: Output data array which must be of size tuple_size ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combos_supported ++ * (reading begins at index tuple_size * vreg->fuse_combo) ++ * 3. Length == tuple_size * vreg->speed_bins_supported ++ * (reading begins at index tuple_size * vreg->speed_bin_fuse) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo; ++ } else if (vreg->speed_bins_supported > 0 && ++ len == tuple_size * vreg->speed_bins_supported * sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_fuse; ++ } else { ++ if (vreg->speed_bins_supported > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * sizeof(u32), ++ tuple_size * vreg->speed_bins_supported ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * sizeof(u32), ++ tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_corner_array_property() - fill a per-corner array from a portion ++ * of the values specified for a device tree property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each per-corner tuple ++ * @out: Output data array which must be of size: ++ * tuple_size * vreg->corner_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size * vreg->corner_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combo_corner_sum ++ * (reading begins at index tuple_size * vreg->fuse_combo_offset) ++ * 3. Length == tuple_size * vreg->speed_bin_corner_sum ++ * (reading begins at index tuple_size * vreg->speed_bin_offset) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * vreg->corner_count * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo_offset; ++ } else if (vreg->speed_bin_corner_sum > 0 && ++ len == tuple_size * vreg->speed_bin_corner_sum * sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_offset; ++ } else { ++ if (vreg->speed_bin_corner_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_count * sizeof(u32), ++ tuple_size * vreg->speed_bin_corner_sum ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_count * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size * vreg->corner_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_corner_band_array_property() - fill a per-corner band array ++ * from a portion of the values specified for a device tree ++ * property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each per-corner band tuple ++ * @out: Output data array which must be of size: ++ * tuple_size * vreg->corner_band_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * In addition, corner band fuse combo and speed bin sum and offset elements ++ * must be initialized prior to executing this function. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size * vreg->corner_band_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combo_corner_band_sum ++ * (reading begins at index tuple_size * ++ * vreg->fuse_combo_corner_band_offset) ++ * 3. Length == tuple_size * vreg->speed_bin_corner_band_sum ++ * (reading begins at index tuple_size * ++ * vreg->speed_bin_corner_band_offset) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * vreg->corner_band_count * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo_corner_band_offset; ++ } else if (vreg->speed_bin_corner_band_sum > 0 && ++ len == tuple_size * vreg->speed_bin_corner_band_sum * ++ sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_corner_band_offset; ++ } else { ++ if (vreg->speed_bin_corner_band_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_band_count * ++ sizeof(u32), ++ tuple_size * vreg->speed_bin_corner_band_sum ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_band_count * ++ sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size * vreg->corner_band_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_common_corner_data() - parse common CPR3 properties relating to ++ * the corners supported by a CPR3 regulator from device tree ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function reads, validates, and utilizes the following device tree ++ * properties: qcom,cpr-fuse-corners, qcom,cpr-fuse-combos, qcom,cpr-speed-bins, ++ * qcom,cpr-speed-bin-corners, qcom,cpr-corners, qcom,cpr-voltage-ceiling, ++ * qcom,cpr-voltage-floor, qcom,corner-frequencies, ++ * and qcom,cpr-corner-fmax-map. ++ * ++ * It initializes these CPR3 regulator elements: corner, corner_count, ++ * fuse_combos_supported, fuse_corner_map, and speed_bins_supported. It ++ * initializes these elements for each corner: ceiling_volt, floor_volt, ++ * proc_freq, and cpr_fuse_corner. ++ * ++ * It requires that the following CPR3 regulator elements be initialized before ++ * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ u32 max_fuse_combos, fuse_corners, aging_allowed = 0; ++ u32 max_speed_bins = 0; ++ u32 *combo_corners; ++ u32 *speed_bin_corners; ++ u32 *temp; ++ int i, j, rc; ++ ++ rc = of_property_read_u32(node, "qcom,cpr-fuse-corners", &fuse_corners); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-fuse-corners, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (vreg->fuse_corner_count != fuse_corners) { ++ cpr3_err(vreg, "device tree config supports %d fuse corners but the hardware has %d fuse corners\n", ++ fuse_corners, vreg->fuse_corner_count); ++ return -EINVAL; ++ } ++ ++ rc = of_property_read_u32(node, "qcom,cpr-fuse-combos", ++ &max_fuse_combos); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Sanity check against arbitrarily large value to avoid excessive ++ * memory allocation. ++ */ ++ if (max_fuse_combos > 100 || max_fuse_combos == 0) { ++ cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n", ++ max_fuse_combos); ++ return -EINVAL; ++ } ++ ++ if (vreg->fuse_combo >= max_fuse_combos) { ++ cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n", ++ max_fuse_combos - 1, vreg->fuse_combo); ++ BUG_ON(1); ++ return -EINVAL; ++ } ++ ++ vreg->fuse_combos_supported = max_fuse_combos; ++ ++ of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins); ++ ++ /* ++ * Sanity check against arbitrarily large value to avoid excessive ++ * memory allocation. ++ */ ++ if (max_speed_bins > 100) { ++ cpr3_err(vreg, "qcom,cpr-speed-bins is invalid: %u\n", ++ max_speed_bins); ++ return -EINVAL; ++ } ++ ++ if (max_speed_bins && vreg->speed_bin_fuse >= max_speed_bins) { ++ cpr3_err(vreg, "device tree config supports speed bins 0-%u but the hardware has speed bin %d\n", ++ max_speed_bins - 1, vreg->speed_bin_fuse); ++ BUG(); ++ return -EINVAL; ++ } ++ ++ vreg->speed_bins_supported = max_speed_bins; ++ ++ combo_corners = kcalloc(vreg->fuse_combos_supported, ++ sizeof(*combo_corners), GFP_KERNEL); ++ if (!combo_corners) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(node, "qcom,cpr-corners", combo_corners, ++ vreg->fuse_combos_supported); ++ if (rc == -EOVERFLOW) { ++ /* Single value case */ ++ rc = of_property_read_u32(node, "qcom,cpr-corners", ++ combo_corners); ++ for (i = 1; i < vreg->fuse_combos_supported; i++) ++ combo_corners[i] = combo_corners[0]; ++ } ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-corners, rc=%d\n", ++ rc); ++ kfree(combo_corners); ++ return rc; ++ } ++ ++ vreg->fuse_combo_offset = 0; ++ vreg->fuse_combo_corner_sum = 0; ++ for (i = 0; i < vreg->fuse_combos_supported; i++) { ++ vreg->fuse_combo_corner_sum += combo_corners[i]; ++ if (i < vreg->fuse_combo) ++ vreg->fuse_combo_offset += combo_corners[i]; ++ } ++ ++ vreg->corner_count = combo_corners[vreg->fuse_combo]; ++ ++ kfree(combo_corners); ++ ++ vreg->speed_bin_offset = 0; ++ vreg->speed_bin_corner_sum = 0; ++ if (vreg->speed_bins_supported > 0) { ++ speed_bin_corners = kcalloc(vreg->speed_bins_supported, ++ sizeof(*speed_bin_corners), GFP_KERNEL); ++ if (!speed_bin_corners) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(node, ++ "qcom,cpr-speed-bin-corners", speed_bin_corners, ++ vreg->speed_bins_supported); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corners, rc=%d\n", ++ rc); ++ kfree(speed_bin_corners); ++ return rc; ++ } ++ ++ for (i = 0; i < vreg->speed_bins_supported; i++) { ++ vreg->speed_bin_corner_sum += speed_bin_corners[i]; ++ if (i < vreg->speed_bin_fuse) ++ vreg->speed_bin_offset += speed_bin_corners[i]; ++ } ++ ++ if (speed_bin_corners[vreg->speed_bin_fuse] ++ != vreg->corner_count) { ++ cpr3_err(vreg, "qcom,cpr-corners and qcom,cpr-speed-bin-corners conflict on number of corners: %d vs %u\n", ++ vreg->corner_count, ++ speed_bin_corners[vreg->speed_bin_fuse]); ++ kfree(speed_bin_corners); ++ return -EINVAL; ++ } ++ ++ kfree(speed_bin_corners); ++ } ++ ++ vreg->corner = devm_kcalloc(ctrl->dev, vreg->corner_count, ++ sizeof(*vreg->corner), GFP_KERNEL); ++ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); ++ if (!vreg->corner || !temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-ceiling", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) { ++ vreg->corner[i].ceiling_volt ++ = CPR3_ROUND(temp[i], ctrl->step_volt); ++ vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].floor_volt ++ = CPR3_ROUND(temp[i], ctrl->step_volt); ++ ++ /* Validate ceiling and floor values */ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].floor_volt ++ > vreg->corner[i].ceiling_volt) { ++ cpr3_err(vreg, "CPR floor[%d]=%d > ceiling[%d]=%d uV\n", ++ i, vreg->corner[i].floor_volt, ++ i, vreg->corner[i].ceiling_volt); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ ++ /* Load optional system-supply voltages */ ++ if (of_find_property(vreg->of_node, "qcom,system-voltage", NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,system-voltage", 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].system_volt = temp[i]; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,corner-frequencies", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].proc_freq = temp[i]; ++ ++ /* Validate frequencies */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].proc_freq ++ < vreg->corner[i - 1].proc_freq) { ++ cpr3_err(vreg, "invalid frequency: freq[%d]=%u < freq[%d]=%u\n", ++ i, vreg->corner[i].proc_freq, i - 1, ++ vreg->corner[i - 1].proc_freq); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ ++ vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count, ++ sizeof(*vreg->fuse_corner_map), GFP_KERNEL); ++ if (!vreg->fuse_corner_map) { ++ rc = -ENOMEM; ++ goto free_temp; ++ } ++ ++ rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map", ++ vreg->fuse_corner_count, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET; ++ if (temp[i] < CPR3_CORNER_OFFSET ++ || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) { ++ cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n", ++ temp[i]); ++ rc = -EINVAL; ++ goto free_temp; ++ } else if (i > 0 && temp[i - 1] >= temp[i]) { ++ cpr3_err(vreg, "invalid corner %u less than or equal to previous corner %u\n", ++ temp[i], temp[i - 1]); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) ++ cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", ++ temp[vreg->fuse_corner_count - 1], ++ vreg->corner_count); ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0; j < vreg->fuse_corner_count; j++) { ++ if (i + CPR3_CORNER_OFFSET <= temp[j]) { ++ vreg->corner[i].cpr_fuse_corner = j; ++ break; ++ } ++ } ++ if (j == vreg->fuse_corner_count) { ++ /* ++ * Handle the case where the highest fuse corner maps ++ * to a corner below the highest corner. ++ */ ++ vreg->corner[i].cpr_fuse_corner ++ = vreg->fuse_corner_count - 1; ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,allow-aging-voltage-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,allow-aging-voltage-adjustment", ++ 1, &aging_allowed); ++ if (rc) ++ goto free_temp; ++ ++ vreg->aging_allowed = aging_allowed; ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,allow-aging-open-loop-voltage-adjustment", ++ 1, &aging_allowed); ++ if (rc) ++ goto free_temp; ++ ++ vreg->aging_allow_open_loop_adj = aging_allowed; ++ } ++ ++ if (vreg->aging_allowed) { ++ if (ctrl->aging_ref_volt <= 0) { ++ cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n"); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-aging-max-voltage-adjustment", ++ 1, &vreg->aging_max_adjust_volt); ++ if (rc) ++ goto free_temp; ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-aging-ref-corner", 1, &vreg->aging_corner); ++ if (rc) { ++ goto free_temp; ++ } else if (vreg->aging_corner < CPR3_CORNER_OFFSET ++ || vreg->aging_corner > vreg->corner_count - 1 ++ + CPR3_CORNER_OFFSET) { ++ cpr3_err(vreg, "aging reference corner=%d not in range [%d, %d]\n", ++ vreg->aging_corner, CPR3_CORNER_OFFSET, ++ vreg->corner_count - 1 + CPR3_CORNER_OFFSET); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ vreg->aging_corner -= CPR3_CORNER_OFFSET; ++ ++ if (of_find_property(vreg->of_node, "qcom,cpr-aging-derate", ++ NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-aging-derate", 1, temp); ++ if (rc) ++ goto free_temp; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].aging_derate = temp[i]; ++ } else { ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].aging_derate ++ = CPR3_AGING_DERATE_UNITY; ++ } ++ } ++ ++free_temp: ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr3_parse_thread_u32() - parse the specified property from the CPR3 thread's ++ * device tree node and verify that it is within the allowed limits ++ * @thread: Pointer to the CPR3 thread ++ * @propname: The name of the device tree property to read ++ * @out_value: The output pointer to fill with the value read ++ * @value_min: The minimum allowed property value ++ * @value_max: The maximum allowed property value ++ * ++ * This function prints a verbose error message if the property is missing or ++ * has a value which is not within the specified range. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(thread->of_node, propname, out_value); ++ if (rc) { ++ cpr3_err(thread->ctrl, "thread %u error reading property %s, rc=%d\n", ++ thread->thread_id, propname, rc); ++ return rc; ++ } ++ ++ if (*out_value < value_min || *out_value > value_max) { ++ cpr3_err(thread->ctrl, "thread %u %s=%u is invalid; allowed range: [%u, %u]\n", ++ thread->thread_id, propname, *out_value, value_min, ++ value_max); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_ctrl_u32() - parse the specified property from the CPR3 ++ * controller's device tree node and verify that it is within the ++ * allowed limits ++ * @ctrl: Pointer to the CPR3 controller ++ * @propname: The name of the device tree property to read ++ * @out_value: The output pointer to fill with the value read ++ * @value_min: The minimum allowed property value ++ * @value_max: The maximum allowed property value ++ * ++ * This function prints a verbose error message if the property is missing or ++ * has a value which is not within the specified range. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, propname, out_value); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property %s, rc=%d\n", ++ propname, rc); ++ return rc; ++ } ++ ++ if (*out_value < value_min || *out_value > value_max) { ++ cpr3_err(ctrl, "%s=%u is invalid; allowed range: [%u, %u]\n", ++ propname, *out_value, value_min, value_max); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_common_thread_data() - parse common CPR3 thread properties from ++ * device tree ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_thread_data(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-up", ++ &thread->consecutive_up, CPR3_CONSECUTIVE_UP_DOWN_MIN, ++ CPR3_CONSECUTIVE_UP_DOWN_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-down", ++ &thread->consecutive_down, CPR3_CONSECUTIVE_UP_DOWN_MIN, ++ CPR3_CONSECUTIVE_UP_DOWN_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-up-threshold", ++ &thread->up_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, ++ CPR3_UP_DOWN_THRESHOLD_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-down-threshold", ++ &thread->down_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, ++ CPR3_UP_DOWN_THRESHOLD_MAX); ++ if (rc) ++ return rc; ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl) ++{ ++ struct device_node *cpu_node; ++ int i, cpu; ++ int len = 0; ++ ++ if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity", ++ &len)) { ++ /* No IRQ affinity required */ ++ return 0; ++ } ++ ++ len /= sizeof(u32); ++ ++ for (i = 0; i < len; i++) { ++ cpu_node = of_parse_phandle(ctrl->dev->of_node, ++ "qcom,cpr-interrupt-affinity", i); ++ if (!cpu_node) { ++ cpr3_err(ctrl, "could not find CPU node %d\n", i); ++ return -EINVAL; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ if (of_get_cpu_node(cpu, NULL) == cpu_node) { ++ cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask); ++ break; ++ } ++ } ++ of_node_put(cpu_node); ++ } ++ ++ return 0; ++} ++ ++static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl) ++{ ++ struct device_node *node = ctrl->dev->of_node; ++ struct cpr3_panic_regs_info *panic_regs_info; ++ struct cpr3_reg_info *regs; ++ int i, reg_count, len, rc = 0; ++ ++ if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) { ++ /* panic register address list not specified */ ++ return rc; ++ } ++ ++ reg_count = len / sizeof(u32); ++ if (!reg_count) { ++ cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n", ++ len); ++ return -EINVAL; ++ } ++ ++ if (!of_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) { ++ cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n"); ++ return -EINVAL; ++ } ++ ++ len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list"); ++ if (reg_count != len) { ++ cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n", ++ reg_count); ++ return -EINVAL; ++ } ++ ++ panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info), ++ GFP_KERNEL); ++ if (!panic_regs_info) ++ return -ENOMEM; ++ ++ regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL); ++ if (!regs) ++ return -ENOMEM; ++ ++ for (i = 0; i < reg_count; i++) { ++ rc = of_property_read_string_index(node, ++ "qcom,cpr-panic-reg-name-list", i, ++ &(regs[i].name)); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32_index(node, ++ "qcom,cpr-panic-reg-addr-list", i, ++ &(regs[i].addr)); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n", ++ rc); ++ return rc; ++ } ++ regs[i].virt_addr = devm_ioremap(ctrl->dev, regs[i].addr, 0x4); ++ if (!regs[i].virt_addr) { ++ pr_err("Unable to map panic register addr 0x%08x\n", ++ regs[i].addr); ++ return -EINVAL; ++ } ++ regs[i].value = 0xFFFFFFFF; ++ } ++ ++ panic_regs_info->reg_count = reg_count; ++ panic_regs_info->regs = regs; ++ ctrl->panic_regs_info = panic_regs_info; ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from ++ * device tree ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-sensor-time", ++ &ctrl->sensor_time, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-loop-time", ++ &ctrl->loop_time, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-idle-cycles", ++ &ctrl->idle_clocks, CPR3_IDLE_CLOCKS_MIN, ++ CPR3_IDLE_CLOCKS_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-min", ++ &ctrl->step_quot_init_min, CPR3_STEP_QUOT_MIN, ++ CPR3_STEP_QUOT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-max", ++ &ctrl->step_quot_init_max, CPR3_STEP_QUOT_MIN, ++ CPR3_STEP_QUOT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", ++ &ctrl->step_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", ++ rc); ++ return rc; ++ } ++ if (ctrl->step_volt <= 0) { ++ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", ++ ctrl->step_volt); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-count-mode", ++ &ctrl->count_mode, CPR3_COUNT_MODE_ALL_AT_ONCE_MIN, ++ CPR3_COUNT_MODE_STAGGERED); ++ if (rc) ++ return rc; ++ ++ /* Count repeat is optional */ ++ ctrl->count_repeat = 0; ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-count-repeat", ++ &ctrl->count_repeat); ++ ++ ctrl->cpr_allowed_sw = ++ of_property_read_bool(ctrl->dev->of_node, "qcom,cpr-enable") || ++ ctrl->cpr_global_setting == CPR_CLOSED_LOOP_EN; ++ ++ rc = cpr3_parse_irq_affinity(ctrl); ++ if (rc) ++ return rc; ++ ++ /* Aging reference voltage is optional */ ++ ctrl->aging_ref_volt = 0; ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage", ++ &ctrl->aging_ref_volt); ++ ++ /* Aging possible bitmask is optional */ ++ ctrl->aging_possible_mask = 0; ++ of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-aging-allowed-reg-mask", ++ &ctrl->aging_possible_mask); ++ ++ if (ctrl->aging_possible_mask) { ++ /* ++ * Aging possible register value required if bitmask is ++ * specified ++ */ ++ rc = cpr3_parse_ctrl_u32(ctrl, ++ "qcom,cpr-aging-allowed-reg-value", ++ &ctrl->aging_possible_val, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ } ++ ++ if (of_find_property(ctrl->dev->of_node, "clock-names", NULL)) { ++ ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk"); ++ if (IS_ERR(ctrl->core_clk)) { ++ rc = PTR_ERR(ctrl->core_clk); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable request core clock, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_panic_notifier_init(ctrl); ++ if (rc) ++ return rc; ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); ++ if (IS_ERR(ctrl->vdd_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ cpr3_err(ctrl, "vdd supply is not defined\n"); ++ return -ENODEV; ++ } ++ ++ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, ++ "system"); ++ if (IS_ERR(ctrl->system_regulator)) { ++ rc = PTR_ERR(ctrl->system_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->system_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, ++ "mem-acc"); ++ if (IS_ERR(ctrl->mem_acc_regulator)) { ++ rc = PTR_ERR(ctrl->mem_acc_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->mem_acc_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_open_loop_common_ctrl_data() - parse common open loop CPR3 ++ * controller properties from device tree ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", ++ &ctrl->step_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->step_volt <= 0) { ++ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", ++ ctrl->step_volt); ++ return -EINVAL; ++ } ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); ++ if (IS_ERR(ctrl->vdd_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ cpr3_err(ctrl, "vdd supply is not defined\n"); ++ return -ENODEV; ++ } ++ ++ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, ++ "system"); ++ if (IS_ERR(ctrl->system_regulator)) { ++ rc = PTR_ERR(ctrl->system_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->system_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } else { ++ rc = regulator_enable(ctrl->system_regulator); ++ } ++ ++ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, ++ "mem-acc"); ++ if (IS_ERR(ctrl->mem_acc_regulator)) { ++ rc = PTR_ERR(ctrl->mem_acc_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->mem_acc_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_limit_open_loop_voltages() - modify the open-loop voltage of each corner ++ * so that it fits within the floor to ceiling ++ * voltage range of the corner ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function clips the open-loop voltage for each corner so that it is ++ * limited to the floor to ceiling range. It also rounds each open-loop voltage ++ * so that it corresponds to a set point available to the underlying regulator. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ int i, volt; ++ ++ cpr3_debug(vreg, "open-loop voltages after trimming and rounding:\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ volt = CPR3_ROUND(vreg->corner[i].open_loop_volt, ++ vreg->thread->ctrl->step_volt); ++ if (volt < vreg->corner[i].floor_volt) ++ volt = vreg->corner[i].floor_volt; ++ else if (volt > vreg->corner[i].ceiling_volt) ++ volt = vreg->corner[i].ceiling_volt; ++ vreg->corner[i].open_loop_volt = volt; ++ cpr3_debug(vreg, "corner[%2d]: open-loop=%d uV\n", i, volt); ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_open_loop_voltage_as_ceiling() - configures the ceiling voltage for each ++ * corner to equal the open-loop voltage if the relevant device ++ * tree property is found for the CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function assumes that the the open-loop voltage for each corner has ++ * already been rounded to the nearest allowed set point and that it falls ++ * within the floor to ceiling range. ++ * ++ * Return: none ++ */ ++void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg) ++{ ++ int i; ++ ++ if (!of_property_read_bool(vreg->of_node, ++ "qcom,cpr-scaled-open-loop-voltage-as-ceiling")) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].open_loop_volt; ++} ++ ++/** ++ * cpr3_limit_floor_voltages() - raise the floor voltage of each corner so that ++ * the optional maximum floor to ceiling voltage range specified in ++ * device tree is satisfied ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function also ensures that the open-loop voltage for each corner falls ++ * within the final floor to ceiling voltage range and that floor voltages ++ * increase monotonically. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) ++{ ++ char *prop = "qcom,cpr-floor-to-ceiling-max-range"; ++ int i, floor_new; ++ u32 *floor_range; ++ int rc = 0; ++ ++ if (!of_find_property(vreg->of_node, prop, NULL)) ++ goto enforce_monotonicity; ++ ++ floor_range = kcalloc(vreg->corner_count, sizeof(*floor_range), ++ GFP_KERNEL); ++ if (!floor_range) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, prop, 1, floor_range); ++ if (rc) ++ goto free_floor_adjust; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if ((s32)floor_range[i] >= 0) { ++ floor_new = CPR3_ROUND(vreg->corner[i].ceiling_volt ++ - floor_range[i], ++ vreg->thread->ctrl->step_volt); ++ ++ vreg->corner[i].floor_volt = max(floor_new, ++ vreg->corner[i].floor_volt); ++ if (vreg->corner[i].open_loop_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].floor_volt; ++ } ++ } ++ ++free_floor_adjust: ++ kfree(floor_range); ++ ++enforce_monotonicity: ++ /* Ensure that floor voltages increase monotonically. */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].floor_volt ++ < vreg->corner[i - 1].floor_volt) { ++ cpr3_debug(vreg, "corner %d floor voltage=%d uV < corner %d voltage=%d uV; overriding: corner %d voltage=%d\n", ++ i, vreg->corner[i].floor_volt, ++ i - 1, vreg->corner[i - 1].floor_volt, ++ i, vreg->corner[i - 1].floor_volt); ++ vreg->corner[i].floor_volt ++ = vreg->corner[i - 1].floor_volt; ++ ++ if (vreg->corner[i].open_loop_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].floor_volt; ++ if (vreg->corner[i].ceiling_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].floor_volt; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_print_quots() - print CPR target quotients into the kernel log for ++ * debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: none ++ */ ++void cpr3_print_quots(struct cpr3_regulator *vreg) ++{ ++ int i, j, pos; ++ size_t buflen; ++ char *buf; ++ ++ buflen = sizeof(*buf) * CPR3_RO_COUNT * (MAX_CHARS_PER_INT + 2); ++ buf = kzalloc(buflen, GFP_KERNEL); ++ if (!buf) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0, pos = 0; j < CPR3_RO_COUNT; j++) ++ pos += scnprintf(buf + pos, buflen - pos, " %u", ++ vreg->corner[i].target_quot[j]); ++ cpr3_debug(vreg, "target quots[%2d]:%s\n", i, buf); ++ } ++ ++ kfree(buf); ++} ++ ++/** ++ * cpr3_determine_part_type() - determine the part type (SS/TT/FF). ++ * ++ * qcom,cpr-part-types prop tells the number of part types for which correction ++ * voltages are different. Another prop qcom,cpr-parts-voltage will contain the ++ * open loop fuse voltage which will be compared with this part voltage ++ * and accordingly part type will de determined. ++ * ++ * if qcom,cpr-part-types has value n, then qcom,cpr-parts-voltage will be ++ * array of n - 1 elements which will contain the voltage in increasing order. ++ * This function compares the fused volatge with all these voltage and returns ++ * the first index for which the fused volatge is greater. ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt: fused open loop voltage which will be compared with ++ * qcom,cpr-parts-voltage array ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) ++{ ++ int i, rc, len; ++ u32 volt; ++ int soc_version_major; ++ char prop_name[100]; ++ const char prop_name_def[] = "qcom,cpr-parts-voltage"; ++ const char prop_name_v2[] = "qcom,cpr-parts-voltage-v2"; ++ ++ soc_version_major = read_ipq_soc_version_major(); ++ BUG_ON(soc_version_major <= 0); ++ ++ if (of_property_read_u32(vreg->of_node, "qcom,cpr-part-types", ++ &vreg->part_type_supported)) ++ return 0; ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_name, prop_name_v2, sizeof(prop_name_v2)); ++ else ++ strlcpy(prop_name, prop_name_def, sizeof(prop_name_def)); ++ ++ if (!of_find_property(vreg->of_node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len != (vreg->part_type_supported - 1) * sizeof(u32)) { ++ cpr3_err(vreg, "wrong len in qcom,cpr-parts-voltage\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < vreg->part_type_supported - 1; i++) { ++ rc = of_property_read_u32_index(vreg->of_node, ++ prop_name, i, &volt); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ ++ if (fuse_volt < volt) ++ break; ++ } ++ ++ vreg->part_type = i; ++ return 0; ++} ++ ++int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ int i, rc, prev_volt; ++ int *volt_adjust; ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_info(vreg, "No cold temperature adjustment required.\n"); ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ if (!volt_adjust) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, prop_str, ++ vreg->fuse_corner_count, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load cold temp voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = fuse_volt[i]; ++ fuse_volt[i] += volt_adjust[i]; ++ cpr3_debug(vreg, ++ "adjusted fuse corner %d open-loop voltage: %d -> %d uV\n", ++ i, prev_volt, fuse_volt[i]); ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_can_adjust_cold_temp() - Is cold temperature adjustment available ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function checks the cold temperature threshold is available ++ * ++ * Return: true on cold temperature threshold is available, else false ++ */ ++bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) ++{ ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ return false; ++ } else ++ return true; ++} ++ ++/** ++ * cpr3_get_cold_temp_threshold() - get cold temperature threshold ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * @cold_temp: cold temperature read. ++ * ++ * This function reads the cold temperature threshold below which ++ * cold temperature adjustment margins will be applied. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) ++{ ++ int rc; ++ u32 temp; ++ char req_prop_str[75], prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(req_prop_str, sizeof(req_prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(req_prop_str, sizeof(req_prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(req_prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(req_prop_str)); ++ } ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, req_prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_info(vreg, "Cold temperature adjustment not required.\n"); ++ return 0; ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_err(vreg, "Missing %s required for %s\n", ++ prop_str, req_prop_str); ++ return -EINVAL; ++ } ++ ++ rc = of_property_read_u32(vreg->of_node, prop_str, &temp); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_str, rc); ++ return rc; ++ } ++ ++ *cold_temp = temp; ++ return 0; ++} ++ ++/** ++ * cpr3_adjust_fused_open_loop_voltages() - adjust the fused open-loop voltages ++ * for each fuse corner according to device tree values ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt: Pointer to an array of the fused open-loop voltage ++ * values ++ * ++ * Voltage values in fuse_volt are modified in place. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ int i, rc, prev_volt; ++ int *volt_adjust; ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-open-loop-voltage-fuse-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-open-loop-voltage-fuse-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(prop_str, "qcom,cpr-open-loop-voltage-fuse-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ if (!volt_adjust) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ++ prop_str, vreg->fuse_corner_count, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load open-loop fused voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = fuse_volt[i]; ++ fuse_volt[i] += volt_adjust[i]; ++ cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", ++ i, prev_volt, fuse_volt[i]); ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_adjust_open_loop_voltages() - adjust the open-loop voltages for each ++ * corner according to device tree values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ int i, rc, prev_volt, min_volt; ++ int *volt_adjust, *volt_diff; ++ ++ if (!of_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-adjustment", NULL)) { ++ /* No adjustment required. */ ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ volt_diff = kcalloc(vreg->corner_count, sizeof(*volt_diff), GFP_KERNEL); ++ if (!volt_adjust || !volt_diff) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-open-loop-voltage-adjustment", 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load open-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = vreg->corner[i].open_loop_volt; ++ vreg->corner[i].open_loop_volt += volt_adjust[i]; ++ cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", ++ i, prev_volt, vreg->corner[i].open_loop_volt); ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-min-diff", NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-open-loop-voltage-min-diff", 1, volt_diff); ++ if (rc) { ++ cpr3_err(vreg, "could not load minimum open-loop voltage differences, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ /* ++ * Ensure that open-loop voltages increase monotonically with respect ++ * to configurable minimum allowed differences. ++ */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; ++ if (vreg->corner[i].open_loop_volt < min_volt) { ++ cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", ++ i, vreg->corner[i].open_loop_volt, ++ i - 1, vreg->corner[i - 1].open_loop_volt, ++ volt_diff[i], i, min_volt); ++ vreg->corner[i].open_loop_volt = min_volt; ++ } ++ } ++ ++done: ++ kfree(volt_diff); ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_quot_adjustment() - returns the quotient adjustment value resulting from ++ * the specified voltage adjustment and RO scaling factor ++ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units ++ * of QUOT/V ++ * @volt_adjust: The amount to adjust the voltage by in units of ++ * microvolts. This value may be positive or negative. ++ */ ++int cpr3_quot_adjustment(int ro_scale, int volt_adjust) ++{ ++ unsigned long long temp; ++ int quot_adjust; ++ int sign = 1; ++ ++ if (ro_scale < 0) { ++ sign = -sign; ++ ro_scale = -ro_scale; ++ } ++ ++ if (volt_adjust < 0) { ++ sign = -sign; ++ volt_adjust = -volt_adjust; ++ } ++ ++ temp = (unsigned long long)ro_scale * (unsigned long long)volt_adjust; ++ do_div(temp, 1000000); ++ ++ quot_adjust = temp; ++ quot_adjust *= sign; ++ ++ return quot_adjust; ++} ++ ++/** ++ * cpr3_voltage_adjustment() - returns the voltage adjustment value resulting ++ * from the specified quotient adjustment and RO scaling factor ++ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units ++ * of QUOT/V ++ * @quot_adjust: The amount to adjust the quotient by in units of ++ * QUOT. This value may be positive or negative. ++ */ ++int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) ++{ ++ unsigned long long temp; ++ int volt_adjust; ++ int sign = 1; ++ ++ if (ro_scale < 0) { ++ sign = -sign; ++ ro_scale = -ro_scale; ++ } ++ ++ if (quot_adjust < 0) { ++ sign = -sign; ++ quot_adjust = -quot_adjust; ++ } ++ ++ if (ro_scale == 0) ++ return 0; ++ ++ temp = (unsigned long long)quot_adjust * 1000000; ++ do_div(temp, ro_scale); ++ ++ volt_adjust = temp; ++ volt_adjust *= sign; ++ ++ return volt_adjust; ++} ++ ++/** ++ * cpr3_parse_closed_loop_voltage_adjustments() - load per-fuse-corner and ++ * per-corner closed-loop adjustment values from device tree ++ * @vreg: Pointer to the CPR3 regulator ++ * @ro_sel: Array of ring oscillator values selected for each ++ * fuse corner ++ * @volt_adjust: Pointer to array which will be filled with the ++ * per-corner closed-loop adjustment voltages ++ * @volt_adjust_fuse: Pointer to array which will be filled with the ++ * per-fuse-corner closed-loop adjustment voltages ++ * @ro_scale: Pointer to array which will be filled with the ++ * per-fuse-corner RO scaling factor values with units of ++ * QUOT/V ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_closed_loop_voltage_adjustments( ++ struct cpr3_regulator *vreg, u64 *ro_sel, ++ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) ++{ ++ int i, rc; ++ u32 *ro_all_scale; ++ ++ char volt_adj[] = "qcom,cpr-closed-loop-voltage-adjustment"; ++ char volt_fuse_adj[] = "qcom,cpr-closed-loop-voltage-fuse-adjustment"; ++ char ro_scaling[] = "qcom,cpr-ro-scaling-factor"; ++ ++ if (!of_find_property(vreg->of_node, volt_adj, NULL) ++ && !of_find_property(vreg->of_node, volt_fuse_adj, NULL) ++ && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ro_scaling, NULL)) { ++ cpr3_err(vreg, "Missing %s required for closed-loop voltage adjustment.\n", ++ ro_scaling); ++ return -EINVAL; ++ } ++ ++ ro_all_scale = kcalloc(vreg->fuse_corner_count * CPR3_RO_COUNT, ++ sizeof(*ro_all_scale), GFP_KERNEL); ++ if (!ro_all_scale) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ro_scaling, ++ vreg->fuse_corner_count * CPR3_RO_COUNT, ro_all_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) ++ ro_scale[i] = ro_all_scale[i * CPR3_RO_COUNT + ro_sel[i]]; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ memcpy(vreg->corner[i].ro_scale, ++ &ro_all_scale[vreg->corner[i].cpr_fuse_corner * CPR3_RO_COUNT], ++ sizeof(*ro_all_scale) * CPR3_RO_COUNT); ++ ++ if (of_find_property(vreg->of_node, volt_fuse_adj, NULL)) { ++ rc = cpr3_parse_array_property(vreg, volt_fuse_adj, ++ vreg->fuse_corner_count, volt_adjust_fuse); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop fused voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, volt_adj, NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, volt_adj, ++ 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++done: ++ kfree(ro_all_scale); ++ return rc; ++} ++ ++/** ++ * cpr3_apm_init() - initialize APM data for a CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function loads memory array power mux (APM) data from device tree ++ * if it is present and requests a handle to the appropriate APM controller ++ * device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_apm_init(struct cpr3_controller *ctrl) ++{ ++ struct device_node *node = ctrl->dev->of_node; ++ int rc; ++ ++ if (!of_find_property(node, "qcom,apm-ctrl", NULL)) { ++ /* No APM used */ ++ return 0; ++ } ++ ++ ctrl->apm = msm_apm_ctrl_dev_get(ctrl->dev); ++ if (IS_ERR(ctrl->apm)) { ++ rc = PTR_ERR(ctrl->apm); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "APM get failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(node, "qcom,apm-threshold-voltage", ++ &ctrl->apm_threshold_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,apm-threshold-voltage, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ctrl->apm_threshold_volt ++ = CPR3_ROUND(ctrl->apm_threshold_volt, ctrl->step_volt); ++ ++ /* No error check since this is an optional property. */ ++ of_property_read_u32(node, "qcom,apm-hysteresis-voltage", ++ &ctrl->apm_adj_volt); ++ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); ++ ++ ctrl->apm_high_supply = MSM_APM_SUPPLY_APCC; ++ ctrl->apm_low_supply = MSM_APM_SUPPLY_MX; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_mem_acc_init() - initialize mem-acc regulator data for ++ * a CPR3 regulator ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_mem_acc_init(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ u32 *temp; ++ int i, rc; ++ ++ if (!ctrl->mem_acc_regulator) { ++ cpr3_info(ctrl, "not using memory accelerator regulator\n"); ++ return 0; ++ } ++ ++ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); ++ if (!temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,mem-acc-voltage", ++ 1, temp); ++ if (rc) { ++ cpr3_err(ctrl, "could not load mem-acc corners, rc=%d\n", rc); ++ } else { ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].mem_acc_volt = temp[i]; ++ } ++ ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr4_load_core_and_temp_adj() - parse amount of voltage adjustment for ++ * per-online-core and per-temperature voltage adjustment for a ++ * given corner or corner band from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * @num: Corner number or corner band number ++ * @use_corner_band: Boolean indicating if the CPR3 regulator supports ++ * adjustments per corner band ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_load_core_and_temp_adj(struct cpr3_regulator *vreg, ++ int num, bool use_corner_band) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr4_sdelta *sdelta; ++ int sdelta_size, i, j, pos, rc = 0; ++ char str[75]; ++ size_t buflen; ++ char *buf; ++ ++ sdelta = use_corner_band ? vreg->corner_band[num].sdelta : ++ vreg->corner[num].sdelta; ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) { ++ /* corner doesn't need sdelta table */ ++ sdelta->max_core_count = 0; ++ sdelta->temp_band_count = 0; ++ return rc; ++ } ++ ++ sdelta_size = sdelta->max_core_count * sdelta->temp_band_count; ++ if (use_corner_band) ++ snprintf(str, sizeof(str), ++ "corner_band=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", ++ num, sdelta->max_core_count, ++ sdelta->temp_band_count, sdelta_size); ++ else ++ snprintf(str, sizeof(str), ++ "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", ++ num, sdelta->max_core_count, ++ sdelta->temp_band_count, sdelta_size); ++ ++ cpr3_debug(vreg, "%s", str); ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size, ++ sizeof(*sdelta->table), GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ if (use_corner_band) ++ snprintf(str, sizeof(str), ++ "qcom,cpr-corner-band%d-temp-core-voltage-adjustment", ++ num + CPR3_CORNER_OFFSET); ++ else ++ snprintf(str, sizeof(str), ++ "qcom,cpr-corner%d-temp-core-voltage-adjustment", ++ num + CPR3_CORNER_OFFSET); ++ ++ rc = cpr3_parse_array_property(vreg, str, sdelta_size, ++ sdelta->table); ++ if (rc) { ++ cpr3_err(vreg, "could not load %s, rc=%d\n", str, rc); ++ return rc; ++ } ++ ++ /* ++ * Convert sdelta margins from uV to PMIC steps and apply negation to ++ * follow the SDELTA register semantics. ++ */ ++ for (i = 0; i < sdelta_size; i++) ++ sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt); ++ ++ buflen = sizeof(*buf) * sdelta_size * (MAX_CHARS_PER_INT + 2); ++ buf = kzalloc(buflen, GFP_KERNEL); ++ if (!buf) ++ return rc; ++ ++ for (i = 0; i < sdelta->max_core_count; i++) { ++ for (j = 0, pos = 0; j < sdelta->temp_band_count; j++) ++ pos += scnprintf(buf + pos, buflen - pos, " %u", ++ sdelta->table[i * sdelta->temp_band_count + j]); ++ cpr3_debug(vreg, "sdelta[%d]:%s\n", i, buf); ++ } ++ ++ kfree(buf); ++ return rc; ++} ++ ++/** ++ * cpr4_parse_core_count_temp_voltage_adj() - parse configuration data for ++ * per-online-core and per-temperature voltage adjustment for ++ * a CPR3 regulator from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * @use_corner_band: Boolean indicating if the CPR3 regulator supports ++ * adjustments per corner band ++ * ++ * This function supports parsing of per-online-core and per-temperature ++ * adjustments per corner or per corner band. CPR controllers which support ++ * corner bands apply the same adjustments to all corners within a corner band. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr4_parse_core_count_temp_voltage_adj( ++ struct cpr3_regulator *vreg, bool use_corner_band) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct device_node *node = vreg->of_node; ++ struct cpr3_corner *corner; ++ struct cpr4_sdelta *sdelta; ++ int i, sdelta_table_count, rc = 0; ++ int *allow_core_count_adj = NULL, *allow_temp_adj = NULL; ++ char prop_str[75]; ++ ++ if (of_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-temp-adjustment" ++ : "qcom,corner-allow-temp-adjustment", NULL)) { ++ if (!ctrl->allow_temp_adj) { ++ cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); ++ return -EINVAL; ++ } ++ ++ vreg->allow_temp_adj = true; ++ } ++ ++ if (of_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-core-count-adjustment" ++ : "qcom,corner-allow-core-count-adjustment", ++ NULL)) { ++ rc = of_property_read_u32(node, "qcom,max-core-count", ++ &vreg->max_core_count); ++ if (rc) { ++ cpr3_err(vreg, "error reading qcom,max-core-count, rc=%d\n", ++ rc); ++ return -EINVAL; ++ } ++ ++ vreg->allow_core_count_adj = true; ++ ctrl->allow_core_count_adj = true; ++ } ++ ++ if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) { ++ /* ++ * Both per-online-core and temperature based adjustments are ++ * disabled for this regulator. ++ */ ++ return 0; ++ } else if (!vreg->allow_core_count_adj) { ++ /* ++ * Only per-temperature voltage adjusments are allowed. ++ * Keep max core count value as 1 to allocate SDELTA. ++ */ ++ vreg->max_core_count = 1; ++ } ++ ++ if (vreg->allow_core_count_adj) { ++ allow_core_count_adj = kcalloc(vreg->corner_count, ++ sizeof(*allow_core_count_adj), ++ GFP_KERNEL); ++ if (!allow_core_count_adj) ++ return -ENOMEM; ++ ++ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? ++ "qcom,corner-band-allow-core-count-adjustment" : ++ "qcom,corner-allow-core-count-adjustment"); ++ ++ rc = use_corner_band ? ++ cpr3_parse_corner_band_array_property(vreg, prop_str, ++ 1, allow_core_count_adj) : ++ cpr3_parse_corner_array_property(vreg, prop_str, ++ 1, allow_core_count_adj); ++ if (rc) { ++ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, ++ rc); ++ goto done; ++ } ++ } ++ ++ if (vreg->allow_temp_adj) { ++ allow_temp_adj = kcalloc(vreg->corner_count, ++ sizeof(*allow_temp_adj), GFP_KERNEL); ++ if (!allow_temp_adj) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? ++ "qcom,corner-band-allow-temp-adjustment" : ++ "qcom,corner-allow-temp-adjustment"); ++ ++ rc = use_corner_band ? ++ cpr3_parse_corner_band_array_property(vreg, prop_str, ++ 1, allow_temp_adj) : ++ cpr3_parse_corner_array_property(vreg, prop_str, ++ 1, allow_temp_adj); ++ if (rc) { ++ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, ++ rc); ++ goto done; ++ } ++ } ++ ++ sdelta_table_count = use_corner_band ? vreg->corner_band_count : ++ vreg->corner_count; ++ ++ for (i = 0; i < sdelta_table_count; i++) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta), ++ GFP_KERNEL); ++ if (!sdelta) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ if (allow_core_count_adj) ++ sdelta->allow_core_count_adj = allow_core_count_adj[i]; ++ if (allow_temp_adj) ++ sdelta->allow_temp_adj = allow_temp_adj[i]; ++ sdelta->max_core_count = vreg->max_core_count; ++ sdelta->temp_band_count = ctrl->temp_band_count; ++ ++ if (use_corner_band) ++ vreg->corner_band[i].sdelta = sdelta; ++ else ++ vreg->corner[i].sdelta = sdelta; ++ ++ rc = cpr4_load_core_and_temp_adj(vreg, i, use_corner_band); ++ if (rc) { ++ cpr3_err(vreg, "corner/band %d core and temp adjustment loading failed, rc=%d\n", ++ i, rc); ++ goto done; ++ } ++ } ++ ++done: ++ kfree(allow_core_count_adj); ++ kfree(allow_temp_adj); ++ ++ return rc; ++} ++ ++/** ++ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages ++ * so that they do not overlap the APM threshold voltage. ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * The memory array power mux (APM) must be configured for a specific supply ++ * based upon where the VDD voltage lies with respect to the APM threshold ++ * voltage. When using CPR hardware closed-loop, the voltage may vary anywhere ++ * between the floor and ceiling voltage without software notification. ++ * Therefore, it is required that the floor to ceiling range for every corner ++ * not intersect the APM threshold voltage. This function adjusts the floor to ++ * ceiling range for each corner which violates this requirement. ++ * ++ * The following algorithm is applied: ++ * if floor < threshold <= ceiling: ++ * if open_loop >= threshold, then floor = threshold - adj ++ * else ceiling = threshold - step ++ * where: ++ * adj = APM hysteresis voltage established to minimize the number of ++ * corners with artificially increased floor voltages ++ * step = voltage in microvolts of a single step of the VDD supply ++ * ++ * The open-loop voltage is also bounded by the new floor or ceiling value as ++ * needed. ++ * ++ * Return: none ++ */ ++void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop; ++ ++ if (!ctrl->apm_threshold_volt) { ++ /* APM not being used. */ ++ return; ++ } ++ ++ ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt, ++ ctrl->step_volt); ++ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); ++ ++ threshold = ctrl->apm_threshold_volt; ++ adj = ctrl->apm_adj_volt; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ ++ if (threshold <= corner->floor_volt ++ || threshold > corner->ceiling_volt) ++ continue; ++ ++ prev_floor = corner->floor_volt; ++ prev_ceiling = corner->ceiling_volt; ++ prev_open_loop = corner->open_loop_volt; ++ ++ if (corner->open_loop_volt >= threshold) { ++ corner->floor_volt = max(corner->floor_volt, ++ threshold - adj); ++ if (corner->open_loop_volt < corner->floor_volt) ++ corner->open_loop_volt = corner->floor_volt; ++ } else { ++ corner->ceiling_volt = threshold - ctrl->step_volt; ++ } ++ ++ if (corner->floor_volt != prev_floor ++ || corner->ceiling_volt != prev_ceiling ++ || corner->open_loop_volt != prev_open_loop) ++ cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", ++ threshold, adj, i, prev_floor, prev_ceiling, ++ prev_open_loop, corner->floor_volt, ++ corner->ceiling_volt, corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling ++ * voltages so that they do not intersect the MEM ACC threshold ++ * voltage ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * The following algorithm is applied: ++ * if floor < threshold <= ceiling: ++ * if open_loop >= threshold, then floor = threshold ++ * else ceiling = threshold - step ++ * where: ++ * step = voltage in microvolts of a single step of the VDD supply ++ * ++ * The open-loop voltage is also bounded by the new floor or ceiling value as ++ * needed. ++ * ++ * Return: none ++ */ ++void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ int i, threshold, prev_ceiling, prev_floor, prev_open_loop; ++ ++ if (!ctrl->mem_acc_threshold_volt) { ++ /* MEM ACC not being used. */ ++ return; ++ } ++ ++ ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt, ++ ctrl->step_volt); ++ ++ threshold = ctrl->mem_acc_threshold_volt; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ ++ if (threshold <= corner->floor_volt ++ || threshold > corner->ceiling_volt) ++ continue; ++ ++ prev_floor = corner->floor_volt; ++ prev_ceiling = corner->ceiling_volt; ++ prev_open_loop = corner->open_loop_volt; ++ ++ if (corner->open_loop_volt >= threshold) { ++ corner->floor_volt = max(corner->floor_volt, threshold); ++ if (corner->open_loop_volt < corner->floor_volt) ++ corner->open_loop_volt = corner->floor_volt; ++ } else { ++ corner->ceiling_volt = threshold - ctrl->step_volt; ++ } ++ ++ if (corner->floor_volt != prev_floor ++ || corner->ceiling_volt != prev_ceiling ++ || corner->open_loop_volt != prev_open_loop) ++ cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", ++ threshold, i, prev_floor, prev_ceiling, ++ prev_open_loop, corner->floor_volt, ++ corner->ceiling_volt, corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cpr3_apply_closed_loop_offset_voltages() - modify the closed-loop voltage ++ * adjustments by the amounts that are needed for this ++ * fuse combo ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Array of closed-loop voltage adjustment values of length ++ * vreg->corner_count which is further adjusted based upon ++ * offset voltage fuse values. ++ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length ++ * vreg->fuse_corner_count. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_apply_closed_loop_offset_voltages(struct cpr3_regulator *vreg, ++ int *volt_adjust, int *fuse_volt_adjust) ++{ ++ u32 *corner_map; ++ int rc = 0, i; ++ ++ if (!of_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { ++ /* No closed-loop offset required. */ ++ return 0; ++ } ++ ++ corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), ++ GFP_KERNEL); ++ if (!corner_map) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", ++ 1, corner_map); ++ if (rc) ++ goto done; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (corner_map[i] == 0) { ++ continue; ++ } else if (corner_map[i] > vreg->fuse_corner_count) { ++ cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", ++ i, corner_map[i]); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ volt_adjust[i] += fuse_volt_adjust[corner_map[i] - 1]; ++ } ++ ++done: ++ kfree(corner_map); ++ return rc; ++} ++ ++/** ++ * cpr3_enforce_inc_quotient_monotonicity() - Ensure that target quotients ++ * increase monotonically from lower to higher corners ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static void cpr3_enforce_inc_quotient_monotonicity(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ ++ for (i = 1; i < vreg->corner_count; i++) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] ++ && vreg->corner[i].target_quot[j] ++ < vreg->corner[i - 1].target_quot[j]) { ++ cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, j, ++ vreg->corner[i].target_quot[j], ++ i - 1, j, ++ vreg->corner[i - 1].target_quot[j], ++ i, j, ++ vreg->corner[i - 1].target_quot[j]); ++ vreg->corner[i].target_quot[j] ++ = vreg->corner[i - 1].target_quot[j]; ++ } ++ } ++ } ++} ++ ++/** ++ * cpr3_enforce_dec_quotient_monotonicity() - Ensure that target quotients ++ * decrease monotonically from higher to lower corners ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static void cpr3_enforce_dec_quotient_monotonicity(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ ++ for (i = vreg->corner_count - 2; i >= 0; i--) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i + 1].target_quot[j] ++ && vreg->corner[i].target_quot[j] ++ > vreg->corner[i + 1].target_quot[j]) { ++ cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, j, ++ vreg->corner[i].target_quot[j], ++ i + 1, j, ++ vreg->corner[i + 1].target_quot[j], ++ i, j, ++ vreg->corner[i + 1].target_quot[j]); ++ vreg->corner[i].target_quot[j] ++ = vreg->corner[i + 1].target_quot[j]; ++ } ++ } ++ } ++} ++ ++/** ++ * _cpr3_adjust_target_quotients() - adjust the target quotients for each ++ * corner of the regulator according to input adjustment and ++ * scaling arrays ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Pointer to an array of closed-loop voltage adjustments ++ * with units of microvolts. The array must have ++ * vreg->corner_count number of elements. ++ * @ro_scale: Pointer to a flattened 2D array of RO scaling factors. ++ * The array must have an inner dimension of CPR3_RO_COUNT ++ * and an outer dimension of vreg->corner_count ++ * @label: Null terminated string providing a label for the type ++ * of adjustment. ++ * ++ * Return: true if any corners received a positive voltage adjustment (> 0), ++ * else false ++ */ ++static bool _cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ const int *volt_adjust, const int *ro_scale, const char *label) ++{ ++ int i, j, quot_adjust; ++ bool is_increasing = false; ++ u32 prev_quot; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j]) { ++ quot_adjust = cpr3_quot_adjustment( ++ ro_scale[i * CPR3_RO_COUNT + j], ++ volt_adjust[i]); ++ if (quot_adjust) { ++ prev_quot = vreg->corner[i]. ++ target_quot[j]; ++ vreg->corner[i].target_quot[j] ++ += quot_adjust; ++ cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", ++ i, j, label, prev_quot, ++ vreg->corner[i].target_quot[j], ++ volt_adjust[i]); ++ } ++ } ++ } ++ if (volt_adjust[i] > 0) ++ is_increasing = true; ++ } ++ ++ return is_increasing; ++} ++ ++/** ++ * cpr3_adjust_target_quotients() - adjust the target quotients for each ++ * corner according to device tree values and fuse values ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length ++ * vreg->fuse_corner_count. This parameter could be null ++ * pointer when no fused adjustments are needed. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust) ++{ ++ int i, rc; ++ int *volt_adjust, *ro_scale; ++ bool explicit_adjustment, fused_adjustment, is_increasing; ++ ++ explicit_adjustment = of_find_property(vreg->of_node, ++ "qcom,cpr-closed-loop-voltage-adjustment", NULL); ++ fused_adjustment = of_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); ++ ++ if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ++ "qcom,cpr-ro-scaling-factor", NULL)) { ++ cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n"); ++ return -EINVAL; ++ } ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ ro_scale = kcalloc(vreg->corner_count * CPR3_RO_COUNT, ++ sizeof(*ro_scale), GFP_KERNEL); ++ if (!volt_adjust || !ro_scale) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-ro-scaling-factor", CPR3_RO_COUNT, ro_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ memcpy(vreg->corner[i].ro_scale, &ro_scale[i * CPR3_RO_COUNT], ++ sizeof(*ro_scale) * CPR3_RO_COUNT); ++ ++ if (explicit_adjustment) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-closed-loop-voltage-adjustment", ++ 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ _cpr3_adjust_target_quotients(vreg, volt_adjust, ro_scale, ++ "from DT"); ++ cpr3_enforce_inc_quotient_monotonicity(vreg); ++ } ++ ++ if (fused_adjustment && fuse_volt_adjust) { ++ memset(volt_adjust, 0, ++ sizeof(*volt_adjust) * vreg->corner_count); ++ ++ rc = cpr3_apply_closed_loop_offset_voltages(vreg, volt_adjust, ++ fuse_volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ is_increasing = _cpr3_adjust_target_quotients(vreg, volt_adjust, ++ ro_scale, "from fuse"); ++ if (is_increasing) ++ cpr3_enforce_inc_quotient_monotonicity(vreg); ++ else ++ cpr3_enforce_dec_quotient_monotonicity(vreg); ++ } ++ ++done: ++ kfree(volt_adjust); ++ kfree(ro_scale); ++ return rc; ++} +--- /dev/null ++++ b/drivers/regulator/cpr4-apss-regulator.c +@@ -0,0 +1,1819 @@ ++/* ++ * Copyright (c) 2015-2016, 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. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define IPQ807x_APSS_FUSE_CORNERS 4 ++#define IPQ817x_APPS_FUSE_CORNERS 2 ++#define IPQ6018_APSS_FUSE_CORNERS 4 ++#define IPQ9574_APSS_FUSE_CORNERS 4 ++ ++u32 g_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS; ++ ++/** ++ * struct cpr4_ipq807x_apss_fuses - APSS specific fuse data for IPQ807x ++ * @ro_sel: Ring oscillator select fuse parameter value for each ++ * fuse corner ++ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value ++ * for each fuse corner (raw, not converted to a voltage) ++ * @target_quot: CPR target quotient fuse parameter value for each fuse ++ * corner ++ * @quot_offset: CPR target quotient offset fuse parameter value for each ++ * fuse corner (raw, not unpacked) used for target quotient ++ * interpolation ++ * @speed_bin: Application processor speed bin fuse parameter value for ++ * the given chip ++ * @cpr_fusing_rev: CPR fusing revision fuse parameter value ++ * @boost_cfg: CPR boost configuration fuse parameter value ++ * @boost_voltage: CPR boost voltage fuse parameter value (raw, not ++ * converted to a voltage) ++ * ++ * This struct holds the values for all of the fuses read from memory. ++ */ ++struct cpr4_ipq807x_apss_fuses { ++ u64 ro_sel[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 init_voltage[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 target_quot[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 quot_offset[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 speed_bin; ++ u64 cpr_fusing_rev; ++ u64 boost_cfg; ++ u64 boost_voltage; ++ u64 misc; ++}; ++ ++/* ++ * fuse combo = fusing revision + 8 * (speed bin) ++ * where: fusing revision = 0 - 7 and speed bin = 0 - 7 ++ */ ++#define CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT 64 ++ ++/* ++ * Constants which define the name of each fuse corner. ++ */ ++enum cpr4_ipq807x_apss_fuse_corner { ++ CPR4_IPQ807x_APSS_FUSE_CORNER_SVS = 0, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_NOM = 1, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO = 2, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO = 3, ++}; ++ ++static const char * const cpr4_ipq807x_apss_fuse_corner_name[] = { ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_SVS] = "SVS", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_NOM] = "NOM", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO] = "TURBO", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO] = "STURBO", ++}; ++ ++/* ++ * IPQ807x APSS fuse parameter locations: ++ * ++ * Structs are organized with the following dimensions: ++ * Outer: 0 to 3 for fuse corners from lowest to highest corner ++ * Inner: large enough to hold the longest set of parameter segments which ++ * fully defines a fuse parameter, +1 (for NULL termination). ++ * Each segment corresponds to a contiguous group of bits from a ++ * single fuse row. These segments are concatentated together in ++ * order to form the full fuse parameter value. The segments for ++ * a given parameter may correspond to different fuse rows. ++ */ ++static struct cpr3_fuse_param ++ipq807x_apss_ro_sel_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{73, 8, 11}, {} }, ++ {{73, 4, 7}, {} }, ++ {{73, 0, 3}, {} }, ++ {{73, 12, 15}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_init_voltage_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{71, 18, 23}, {} }, ++ {{71, 12, 17}, {} }, ++ {{71, 6, 11}, {} }, ++ {{71, 0, 5}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_target_quot_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{72, 32, 43}, {} }, ++ {{72, 20, 31}, {} }, ++ {{72, 8, 19}, {} }, ++ {{72, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_quot_offset_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{71, 46, 52}, {} }, ++ {{71, 39, 45}, {} }, ++ {{71, 32, 38}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq807x_cpr_fusing_rev_param[] = { ++ {71, 53, 55}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_apss_speed_bin_param[] = { ++ {36, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_cpr_boost_fuse_cfg_param[] = { ++ {36, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_apss_boost_fuse_volt_param[] = { ++ {71, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_misc_fuse_volt_adj_param[] = { ++ {36, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq807x_fuse_params = { ++ .apss_ro_sel_param = ipq807x_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq807x_apss_init_voltage_param, ++ .apss_target_quot_param = ipq807x_apss_target_quot_param, ++ .apss_quot_offset_param = ipq807x_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq807x_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq807x_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq807x_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq807x_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq807x_misc_fuse_volt_adj_param ++}; ++ ++/* ++ * The number of possible values for misc fuse is ++ * 2^(#bits defined for misc fuse) ++ */ ++#define IPQ807x_MISC_FUSE_VAL_COUNT BIT(1) ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ipq807x_apss_fuse_ref_volt ++ [IPQ807x_APSS_FUSE_CORNERS] = { ++ 720000, ++ 864000, ++ 992000, ++ 1064000, ++}; ++ ++#define IPQ807x_APSS_FUSE_STEP_VOLT 8000 ++#define IPQ807x_APSS_VOLTAGE_FUSE_SIZE 6 ++#define IPQ807x_APSS_QUOT_OFFSET_SCALE 5 ++ ++#define IPQ807x_APSS_CPR_SENSOR_COUNT 6 ++ ++#define IPQ807x_APSS_CPR_CLOCK_RATE 19200000 ++ ++#define IPQ807x_APSS_MAX_TEMP_POINTS 3 ++#define IPQ807x_APSS_TEMP_SENSOR_ID_START 4 ++#define IPQ807x_APSS_TEMP_SENSOR_ID_END 13 ++/* ++ * Boost voltage fuse reference and ceiling voltages in microvolts for ++ * IPQ807x. ++ */ ++#define IPQ807x_APSS_BOOST_FUSE_REF_VOLT 1140000 ++#define IPQ807x_APSS_BOOST_CEILING_VOLT 1140000 ++#define IPQ807x_APSS_BOOST_FLOOR_VOLT 900000 ++#define MAX_BOOST_CONFIG_FUSE_VALUE 8 ++ ++#define IPQ807x_APSS_CPR_SDELTA_CORE_COUNT 15 ++ ++#define IPQ807x_APSS_CPR_TCSR_START 8 ++#define IPQ807x_APSS_CPR_TCSR_END 9 ++ ++/* ++ * Array of integer values mapped to each of the boost config fuse values to ++ * indicate boost enable/disable status. ++ */ ++static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1}; ++ ++/* ++ * IPQ6018 (Few parameters are changed, remaining are same as IPQ807x) ++ */ ++#define IPQ6018_APSS_FUSE_STEP_VOLT 12500 ++#define IPQ6018_APSS_CPR_CLOCK_RATE 24000000 ++ ++static struct cpr3_fuse_param ++ipq6018_apss_ro_sel_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{75, 8, 11}, {} }, ++ {{75, 4, 7}, {} }, ++ {{75, 0, 3}, {} }, ++ {{75, 12, 15}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_init_voltage_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{73, 18, 23}, {} }, ++ {{73, 12, 17}, {} }, ++ {{73, 6, 11}, {} }, ++ {{73, 0, 5}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_target_quot_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{74, 32, 43}, {} }, ++ {{74, 20, 31}, {} }, ++ {{74, 8, 19}, {} }, ++ {{74, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_quot_offset_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{73, 48, 55}, {} }, ++ {{73, 40, 47}, {} }, ++ {{73, 32, 39}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq6018_cpr_fusing_rev_param[] = { ++ {75, 16, 18}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_apss_speed_bin_param[] = { ++ {36, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_cpr_boost_fuse_cfg_param[] = { ++ {36, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_apss_boost_fuse_volt_param[] = { ++ {73, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_misc_fuse_volt_adj_param[] = { ++ {36, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq6018_fuse_params = { ++ .apss_ro_sel_param = ipq6018_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq6018_apss_init_voltage_param, ++ .apss_target_quot_param = ipq6018_apss_target_quot_param, ++ .apss_quot_offset_param = ipq6018_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq6018_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq6018_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq6018_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq6018_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq6018_misc_fuse_volt_adj_param ++}; ++ ++ ++/* ++ * Boost voltage fuse reference and ceiling voltages in microvolts for ++ * IPQ6018. ++ */ ++#define IPQ6018_APSS_BOOST_FUSE_REF_VOLT 1140000 ++#define IPQ6018_APSS_BOOST_CEILING_VOLT 1140000 ++#define IPQ6018_APSS_BOOST_FLOOR_VOLT 900000 ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ipq6018_apss_fuse_ref_volt ++ [IPQ6018_APSS_FUSE_CORNERS] = { ++ 725000, ++ 862500, ++ 987500, ++ 1062500, ++}; ++ ++/* ++ * IPQ6018 Memory ACC settings on TCSR ++ * ++ * Turbo_L1: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x10 ++ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x1 ++ * Other modes: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x0 ++ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x0 ++ * ++ */ ++#define IPQ6018_APSS_MEM_ACC_TCSR_COUNT 2 ++#define TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x1946178 ++#define TCSR_CUSTOM_VDDAPC0_ACC_1 0x1946124 ++ ++struct mem_acc_tcsr { ++ u32 phy_addr; ++ void __iomem *ioremap_addr; ++ u32 value; ++}; ++ ++static struct mem_acc_tcsr ipq6018_mem_acc_tcsr[IPQ6018_APSS_MEM_ACC_TCSR_COUNT] = { ++ {TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0, NULL, 0x10}, ++ {TCSR_CUSTOM_VDDAPC0_ACC_1, NULL, 0x1}, ++}; ++ ++/* ++ * IPQ9574 (Few parameters are changed, remaining are same as IPQ6018) ++ */ ++#define IPQ9574_APSS_FUSE_STEP_VOLT 10000 ++ ++static struct cpr3_fuse_param ++ipq9574_apss_ro_sel_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{107, 4, 7}, {} }, ++ {{107, 0, 3}, {} }, ++ {{106, 4, 7}, {} }, ++ {{106, 0, 3}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_init_voltage_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{104, 24, 29}, {} }, ++ {{104, 18, 23}, {} }, ++ {{104, 12, 17}, {} }, ++ {{104, 6, 11}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_target_quot_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{106, 32, 43}, {} }, ++ {{106, 20, 31}, {} }, ++ {{106, 8, 19}, {} }, ++ {{106, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_quot_offset_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{105, 48, 55}, {} }, ++ {{105, 40, 47}, {} }, ++ {{105, 32, 39}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq9574_cpr_fusing_rev_param[] = { ++ {107, 8, 10}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_apss_speed_bin_param[] = { ++ {0, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_cpr_boost_fuse_cfg_param[] = { ++ {0, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_apss_boost_fuse_volt_param[] = { ++ {104, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_misc_fuse_volt_adj_param[] = { ++ {0, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq9574_fuse_params = { ++ .apss_ro_sel_param = ipq9574_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq9574_apss_init_voltage_param, ++ .apss_target_quot_param = ipq9574_apss_target_quot_param, ++ .apss_quot_offset_param = ipq9574_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq9574_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq9574_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq9574_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq9574_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq9574_misc_fuse_volt_adj_param ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 ++ */ ++static int ipq9574_apss_fuse_ref_volt ++ [IPQ9574_APSS_FUSE_CORNERS] = { ++ 725000, ++ 862500, ++ 987500, ++ 1062500, ++}; ++ ++/** ++ * cpr4_ipq807x_apss_read_fuse_data() - load APSS specific fuse parameter values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function allocates a cpr4_ipq807x_apss_fuses struct, fills it with ++ * values read out of hardware fuses, and finally copies common fuse values ++ * into the CPR3 regulator struct. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_read_fuse_data(struct cpr3_regulator *vreg) ++{ ++ void __iomem *base = vreg->thread->ctrl->fuse_base; ++ struct cpr4_ipq807x_apss_fuses *fuse; ++ int i, rc; ++ ++ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); ++ if (!fuse) ++ return -ENOMEM; ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->apss_speed_bin_param, ++ &fuse->speed_bin); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc); ++ return rc; ++ } ++ cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_fusing_rev_param, ++ &fuse->cpr_fusing_rev); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->misc_fuse_volt_adj_param, ++ &fuse->misc); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc); ++ if (fuse->misc >= IPQ807x_MISC_FUSE_VAL_COUNT) { ++ cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n", ++ fuse->misc, IPQ807x_MISC_FUSE_VAL_COUNT); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < g_valid_fuse_count; i++) { ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_init_voltage_param[i], ++ &fuse->init_voltage[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_target_quot_param[i], ++ &fuse->target_quot[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_ro_sel_param[i], ++ &fuse->ro_sel[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_quot_offset_param[i], ++ &fuse->quot_offset[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_boost_fuse_cfg_param, ++ &fuse->boost_cfg); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n", ++ fuse->boost_cfg, boost_fuse[fuse->boost_cfg] ++ ? "enable" : "disable"); ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_boost_fuse_volt_param, ++ &fuse->boost_voltage); ++ if (rc) { ++ cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; ++ if (vreg->fuse_combo >= CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT) { ++ cpr3_err(vreg, "invalid CPR fuse combo = %d found\n", ++ vreg->fuse_combo); ++ return -EINVAL; ++ } ++ ++ vreg->speed_bin_fuse = fuse->speed_bin; ++ vreg->cpr_rev_fuse = fuse->cpr_fusing_rev; ++ vreg->fuse_corner_count = g_valid_fuse_count; ++ vreg->platform_fuses = fuse; ++ ++ return 0; ++} ++ ++/** ++ * cpr4_apss_parse_corner_data() - parse APSS corner data from device tree ++ * properties of the CPR3 regulator's device node ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ u32 *temp = NULL; ++ int i, rc; ++ ++ rc = cpr3_parse_common_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ /* If fuse has incorrect RO Select values and dtsi has "qcom,cpr-ro-sel" ++ * entry with RO select values other than zero, then dtsi values will ++ * be used. ++ */ ++ if (of_find_property(node, "qcom,cpr-ro-sel", NULL)) { ++ temp = kcalloc(vreg->fuse_corner_count, sizeof(*temp), ++ GFP_KERNEL); ++ if (!temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, "qcom,cpr-ro-sel", ++ vreg->fuse_corner_count, temp); ++ if (rc) ++ goto done; ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (temp[i] != 0) ++ fuse->ro_sel[i] = temp[i]; ++ } ++ } ++done: ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a ++ * portion of the voltage adjustments specified based on ++ * miscellaneous fuse bits. ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Voltage adjustment output data array which must be ++ * of size vreg->corner_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that speed bin size elements are initialized. ++ * ++ * Two formats are supported for the device tree property: ++ * 1. Length == tuple_list_size * vreg->corner_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_list_size * vreg->speed_bin_corner_sum ++ * (reading begins at index tuple_list_size * vreg->speed_bin_offset) ++ * ++ * Here, tuple_list_size is the number of possible values for misc fuse. ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_misc_fuse_voltage_adjustments( ++ struct cpr3_regulator *vreg, u32 *volt_adjust) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ int tuple_list_size = IPQ807x_MISC_FUSE_VAL_COUNT; ++ int i, offset, rc, len = 0; ++ const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment"; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) { ++ offset = 0; ++ } else if (vreg->speed_bin_corner_sum > 0 && ++ len == tuple_list_size * vreg->speed_bin_corner_sum ++ * sizeof(u32)) { ++ offset = tuple_list_size * vreg->speed_bin_offset ++ + fuse->misc * vreg->corner_count; ++ } else { ++ if (vreg->speed_bin_corner_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_list_size * vreg->corner_count ++ * sizeof(u32), ++ tuple_list_size * vreg->speed_bin_corner_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n", ++ prop_name, len, ++ tuple_list_size * vreg->corner_count ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &volt_adjust[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_ipq807x_apss_calculate_open_loop_voltages() - calculate the open-loop ++ * voltage for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * If open-loop voltage interpolation is allowed in device tree, then ++ * this function calculates the open-loop voltage for a given corner using ++ * linear interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with their fused ++ * open-loop voltages. ++ * ++ * If open-loop voltage interpolation is not allowed, then this function uses ++ * the Fmax fused open-loop voltage for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_calculate_open_loop_voltages( ++ struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int i, j, rc = 0; ++ bool allow_interpolation; ++ u64 freq_low, volt_low, freq_high, volt_high; ++ int *fuse_volt, *misc_adj_volt; ++ int *fmax_corner; ++ ++ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ if (!fuse_volt || !fmax_corner) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (ctrl->cpr_global_setting == CPR_DISABLED) ++ fuse_volt[i] = vreg->cpr4_regulator_data->fuse_ref_volt[i]; ++ else ++ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr4_regulator_data->fuse_ref_volt[i], ++ vreg->cpr4_regulator_data->fuse_step_volt, ++ fuse->init_voltage[i], ++ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); ++ ++ /* Log fused open-loop voltage values for debugging purposes. */ ++ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", ++ cpr4_ipq807x_apss_fuse_corner_name[i], ++ fuse_volt[i]); ++ } ++ ++ rc = cpr3_determine_part_type(vreg, ++ fuse_volt[vreg->fuse_corner_count - 1]); ++ if (rc) { ++ cpr3_err(vreg, "fused part type detection failed failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ allow_interpolation = of_property_read_bool(node, ++ "qcom,allow-voltage-interpolation"); ++ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ if (fuse_volt[i] < fuse_volt[i - 1]) { ++ cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", ++ i, fuse_volt[i], i - 1, fuse_volt[i - 1], ++ i, fuse_volt[i - 1]); ++ fuse_volt[i] = fuse_volt[i - 1]; ++ } ++ } ++ ++ if (!allow_interpolation) { ++ /* Use fused open-loop voltage for lower frequencies. */ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].open_loop_volt ++ = fuse_volt[vreg->corner[i].cpr_fuse_corner]; ++ goto done; ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ for (i = 0; i <= fmax_corner[0]; i++) ++ vreg->corner[i].open_loop_volt = fuse_volt[0]; ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ volt_low = fuse_volt[i - 1]; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ volt_high = fuse_volt[i]; ++ ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].open_loop_volt = cpr3_interpolate( ++ freq_low, volt_low, freq_high, volt_high, ++ vreg->corner[j].proc_freq); ++ } ++ ++done: ++ if (rc == 0) { ++ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); ++ for (i = 0; i < vreg->corner_count; i++) ++ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, ++ vreg->corner[i].open_loop_volt); ++ ++ rc = cpr3_adjust_open_loop_voltages(vreg); ++ if (rc) ++ cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ ++ if (of_find_property(node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", ++ NULL)) { ++ misc_adj_volt = kcalloc(vreg->corner_count, ++ sizeof(*misc_adj_volt), GFP_KERNEL); ++ if (!misc_adj_volt) { ++ rc = -ENOMEM; ++ goto _exit; ++ } ++ ++ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, ++ misc_adj_volt); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", ++ rc); ++ kfree(misc_adj_volt); ++ goto _exit; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].open_loop_volt ++ += misc_adj_volt[i]; ++ kfree(misc_adj_volt); ++ } ++ } ++ ++_exit: ++ kfree(fuse_volt); ++ kfree(fmax_corner); ++ return rc; ++} ++ ++/** ++ * cpr4_ipq807x_apss_set_no_interpolation_quotients() - use the fused target ++ * quotient values for lower frequencies. ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Pointer to array of per-corner closed-loop adjustment ++ * voltages ++ * @volt_adjust_fuse: Pointer to array of per-fuse-corner closed-loop ++ * adjustment voltages ++ * @ro_scale: Pointer to array of per-fuse-corner RO scaling factor ++ * values with units of QUOT/V ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_set_no_interpolation_quotients( ++ struct cpr3_regulator *vreg, int *volt_adjust, ++ int *volt_adjust_fuse, int *ro_scale) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ u32 quot, ro; ++ int quot_adjust; ++ int i, fuse_corner; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ fuse_corner = vreg->corner[i].cpr_fuse_corner; ++ quot = fuse->target_quot[fuse_corner]; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], ++ volt_adjust_fuse[fuse_corner] + ++ volt_adjust[i]); ++ ro = fuse->ro_sel[fuse_corner]; ++ vreg->corner[i].target_quot[ro] = quot + quot_adjust; ++ cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n", ++ i, ro, quot); ++ ++ if (quot_adjust) ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", ++ i, ro, quot, vreg->corner[i].target_quot[ro], ++ volt_adjust_fuse[fuse_corner] + ++ volt_adjust[i]); ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_ipq807x_apss_calculate_target_quotients() - calculate the CPR target ++ * quotient for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * If target quotient interpolation is allowed in device tree, then this ++ * function calculates the target quotient for a given corner using linear ++ * interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with the fused ++ * target quotient and quotient offset of the higher Fmax corner. ++ * ++ * If target quotient interpolation is not allowed, then this function uses ++ * the Fmax fused target quotient for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_calculate_target_quotients( ++ struct cpr3_regulator *vreg) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ int rc; ++ bool allow_interpolation; ++ u64 freq_low, freq_high, prev_quot; ++ u64 *quot_low; ++ u64 *quot_high; ++ u32 quot, ro; ++ int i, j, fuse_corner, quot_adjust; ++ int *fmax_corner; ++ int *volt_adjust, *volt_adjust_fuse, *ro_scale; ++ int *voltage_adj_misc; ++ ++ /* Log fused quotient values for debugging purposes. */ ++ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; ++ i < vreg->fuse_corner_count; i++) ++ cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n", ++ cpr4_ipq807x_apss_fuse_corner_name[i], ++ fuse->ro_sel[i], fuse->target_quot[i], ++ fuse->ro_sel[i], fuse->quot_offset[i] * ++ IPQ807x_APSS_QUOT_OFFSET_SCALE); ++ ++ allow_interpolation = of_property_read_bool(vreg->of_node, ++ "qcom,allow-quotient-interpolation"); ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ volt_adjust_fuse = kcalloc(vreg->fuse_corner_count, ++ sizeof(*volt_adjust_fuse), GFP_KERNEL); ++ ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low), ++ GFP_KERNEL); ++ quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high), ++ GFP_KERNEL); ++ if (!volt_adjust || !volt_adjust_fuse || !ro_scale || ++ !fmax_corner || !quot_low || !quot_high) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0], ++ volt_adjust, volt_adjust_fuse, ro_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", NULL)) { ++ voltage_adj_misc = kcalloc(vreg->corner_count, ++ sizeof(*voltage_adj_misc), GFP_KERNEL); ++ if (!voltage_adj_misc) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, ++ voltage_adj_misc); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", ++ rc); ++ kfree(voltage_adj_misc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ volt_adjust[i] += voltage_adj_misc[i]; ++ ++ kfree(voltage_adj_misc); ++ } ++ ++ if (!allow_interpolation) { ++ /* Use fused target quotients for lower frequencies. */ ++ return cpr4_ipq807x_apss_set_no_interpolation_quotients( ++ vreg, volt_adjust, volt_adjust_fuse, ro_scale); ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]); ++ quot = fuse->target_quot[i] + quot_adjust; ++ quot_high[i] = quot_low[i] = quot; ++ ro = fuse->ro_sel[i]; ++ if (quot_adjust) ++ cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", ++ i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]); ++ ++ for (i = 0; i <= fmax_corner[CPR4_IPQ807x_APSS_FUSE_CORNER_SVS]; ++ i++) ++ vreg->corner[i].target_quot[ro] = quot; ++ ++ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_NOM; ++ i < vreg->fuse_corner_count; i++) { ++ quot_high[i] = fuse->target_quot[i]; ++ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) ++ quot_low[i] = quot_high[i - 1]; ++ else ++ quot_low[i] = quot_high[i] ++ - fuse->quot_offset[i] ++ * IPQ807x_APSS_QUOT_OFFSET_SCALE; ++ if (quot_high[i] < quot_low[i]) { ++ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", ++ i, quot_high[i], i, quot_low[i], ++ i, quot_low[i]); ++ quot_high[i] = quot_low[i]; ++ } ++ } ++ ++ /* Perform per-fuse-corner target quotient adjustment */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ quot_adjust = cpr3_quot_adjustment(ro_scale[i], ++ volt_adjust_fuse[i]); ++ if (quot_adjust) { ++ prev_quot = quot_high[i]; ++ quot_high[i] += quot_adjust; ++ cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", ++ i, fuse->ro_sel[i], prev_quot, quot_high[i], ++ volt_adjust_fuse[i]); ++ } ++ ++ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) ++ quot_low[i] = quot_high[i - 1]; ++ else ++ quot_low[i] += cpr3_quot_adjustment(ro_scale[i], ++ volt_adjust_fuse[i - 1]); ++ ++ if (quot_high[i] < quot_low[i]) { ++ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", ++ i, quot_high[i], i, quot_low[i], ++ i, quot_low[i]); ++ quot_high[i] = quot_low[i]; ++ } ++ } ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ ++ ro = fuse->ro_sel[i]; ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].target_quot[ro] = cpr3_interpolate( ++ freq_low, quot_low[i], freq_high, quot_high[i], ++ vreg->corner[j].proc_freq); ++ } ++ ++ /* Perform per-corner target quotient adjustment */ ++ for (i = 0; i < vreg->corner_count; i++) { ++ fuse_corner = vreg->corner[i].cpr_fuse_corner; ++ ro = fuse->ro_sel[fuse_corner]; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], ++ volt_adjust[i]); ++ if (quot_adjust) { ++ prev_quot = vreg->corner[i].target_quot[ro]; ++ vreg->corner[i].target_quot[ro] += quot_adjust; ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", ++ i, ro, prev_quot, ++ vreg->corner[i].target_quot[ro], ++ volt_adjust[i]); ++ } ++ } ++ ++ /* Ensure that target quotients increase monotonically */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner]; ++ if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro ++ && vreg->corner[i].target_quot[ro] ++ < vreg->corner[i - 1].target_quot[ro]) { ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, ro, vreg->corner[i].target_quot[ro], ++ i - 1, ro, vreg->corner[i - 1].target_quot[ro], ++ i, ro, vreg->corner[i - 1].target_quot[ro]); ++ vreg->corner[i].target_quot[ro] ++ = vreg->corner[i - 1].target_quot[ro]; ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ kfree(volt_adjust_fuse); ++ kfree(ro_scale); ++ kfree(fmax_corner); ++ kfree(quot_low); ++ kfree(quot_high); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_print_settings() - print out APSS CPR configuration settings into ++ * the kernel log for debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++static void cpr4_apss_print_settings(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_corner *corner; ++ int i; ++ ++ cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", ++ i, corner->proc_freq, corner->cpr_fuse_corner, ++ corner->floor_volt, corner->open_loop_volt, ++ corner->ceiling_volt); ++ } ++ ++ if (vreg->thread->ctrl->apm) ++ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", ++ vreg->thread->ctrl->apm_threshold_volt, ++ vreg->thread->ctrl->apm_adj_volt); ++} ++ ++/** ++ * cpr4_apss_init_thread() - perform steps necessary to initialize the ++ * configuration data for a CPR3 thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_thread(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_thread_data(thread); ++ if (rc) { ++ cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n", ++ thread->thread_id, rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_apss_parse_temp_adj_properties() - parse temperature based ++ * adjustment properties from device tree. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl) ++{ ++ struct device_node *of_node = ctrl->dev->of_node; ++ int rc, i, len, temp_point_count; ++ ++ if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) { ++ /* ++ * Temperature based adjustments are not defined. Single ++ * temperature band is still valid for per-online-core ++ * adjustments. ++ */ ++ ctrl->temp_band_count = 1; ++ return 0; ++ } ++ ++ temp_point_count = len / sizeof(u32); ++ if (temp_point_count <= 0 || ++ temp_point_count > IPQ807x_APSS_MAX_TEMP_POINTS) { ++ cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n", ++ temp_point_count, IPQ807x_APSS_MAX_TEMP_POINTS); ++ return -EINVAL; ++ } ++ ++ ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count, ++ sizeof(*ctrl->temp_points), GFP_KERNEL); ++ if (!ctrl->temp_points) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map", ++ ctrl->temp_points, temp_point_count); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < temp_point_count; i++) ++ cpr3_debug(ctrl, "Temperature Point %d=%d\n", i, ++ ctrl->temp_points[i]); ++ ++ /* ++ * If t1, t2, and t3 are the temperature points, then the temperature ++ * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). ++ */ ++ ctrl->temp_band_count = temp_point_count + 1; ++ cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count); ++ ++ rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band", ++ &ctrl->initial_temp_band); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->initial_temp_band >= ctrl->temp_band_count) { ++ cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n", ++ ctrl->initial_temp_band, ctrl->temp_band_count - 1); ++ return -EINVAL; ++ } ++ ++ ctrl->temp_sensor_id_start = IPQ807x_APSS_TEMP_SENSOR_ID_START; ++ ctrl->temp_sensor_id_end = IPQ807x_APSS_TEMP_SENSOR_ID_END; ++ ctrl->allow_temp_adj = true; ++ return rc; ++} ++ ++/** ++ * cpr4_apss_parse_boost_properties() - parse configuration data for boost ++ * voltage adjustment for CPR3 regulator from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_boost_properties(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_corner *corner; ++ int i, boost_voltage, final_boost_volt, rc = 0; ++ int *boost_table = NULL, *boost_temp_adj = NULL; ++ int boost_voltage_adjust = 0, boost_num_cores = 0; ++ u32 boost_allowed = 0; ++ ++ if (!boost_fuse[fuse->boost_cfg]) ++ /* Voltage boost is disabled in fuse */ ++ return 0; ++ ++ if (of_find_property(vreg->of_node, "qcom,allow-boost", NULL)) { ++ rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1, ++ &boost_allowed); ++ if (rc) ++ return rc; ++ } ++ ++ if (!boost_allowed) { ++ /* Voltage boost is not enabled for this regulator */ ++ return 0; ++ } ++ ++ boost_voltage = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr4_regulator_data->boost_fuse_ref_volt, ++ vreg->cpr4_regulator_data->fuse_step_volt, ++ fuse->boost_voltage, ++ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); ++ ++ /* Log boost voltage value for debugging purposes. */ ++ cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage); ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-boost-voltage-fuse-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-boost-voltage-fuse-adjustment", ++ 1, &boost_voltage_adjust); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ boost_voltage += boost_voltage_adjust; ++ /* Log boost voltage value for debugging purposes. */ ++ cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n", ++ boost_voltage); ++ } ++ ++ /* Limit boost voltage value between ceiling and floor voltage limits */ ++ boost_voltage = min(boost_voltage, vreg->cpr4_regulator_data->boost_ceiling_volt); ++ boost_voltage = max(boost_voltage, vreg->cpr4_regulator_data->boost_floor_volt); ++ ++ /* ++ * The boost feature can only be used for the highest voltage corner. ++ * Also, keep core-count adjustments disabled when the boost feature ++ * is enabled. ++ */ ++ corner = &vreg->corner[vreg->corner_count - 1]; ++ if (!corner->sdelta) { ++ /* ++ * If core-count/temp adjustments are not defined, the cpr4 ++ * sdelta for this corner will not be allocated. Allocate it ++ * here for boost configuration. ++ */ ++ corner->sdelta = devm_kzalloc(ctrl->dev, ++ sizeof(*corner->sdelta), GFP_KERNEL); ++ if (!corner->sdelta) ++ return -ENOMEM; ++ } ++ corner->sdelta->temp_band_count = ctrl->temp_band_count; ++ ++ rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores", ++ &boost_num_cores); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (boost_num_cores <= 0 || ++ boost_num_cores > IPQ807x_APSS_CPR_SDELTA_CORE_COUNT) { ++ cpr3_err(vreg, "Invalid boost number of cores = %d\n", ++ boost_num_cores); ++ return -EINVAL; ++ } ++ corner->sdelta->boost_num_cores = boost_num_cores; ++ ++ boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count, ++ sizeof(*boost_table), GFP_KERNEL); ++ if (!boost_table) ++ return -ENOMEM; ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-boost-temp-adjustment", NULL)) { ++ boost_temp_adj = kcalloc(corner->sdelta->temp_band_count, ++ sizeof(*boost_temp_adj), GFP_KERNEL); ++ if (!boost_temp_adj) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-boost-temp-adjustment", ++ corner->sdelta->temp_band_count, ++ boost_temp_adj); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ for (i = 0; i < corner->sdelta->temp_band_count; i++) { ++ /* Apply static adjustments to boost voltage */ ++ final_boost_volt = boost_voltage + (boost_temp_adj == NULL ++ ? 0 : boost_temp_adj[i]); ++ /* ++ * Limit final adjusted boost voltage value between ceiling ++ * and floor voltage limits ++ */ ++ final_boost_volt = min(final_boost_volt, ++ vreg->cpr4_regulator_data->boost_ceiling_volt); ++ final_boost_volt = max(final_boost_volt, ++ vreg->cpr4_regulator_data->boost_floor_volt); ++ ++ boost_table[i] = (corner->open_loop_volt - final_boost_volt) ++ / ctrl->step_volt; ++ cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n", ++ i, boost_table[i]); ++ } ++ ++ corner->ceiling_volt = vreg->cpr4_regulator_data->boost_ceiling_volt; ++ corner->sdelta->boost_table = boost_table; ++ corner->sdelta->allow_boost = true; ++ corner->sdelta->allow_core_count_adj = false; ++ vreg->allow_boost = true; ++ ctrl->allow_boost = true; ++done: ++ kfree(boost_temp_adj); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_init_regulator() - perform all steps necessary to initialize the ++ * configuration data for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse; ++ int rc; ++ ++ rc = cpr4_ipq807x_apss_read_fuse_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ fuse = vreg->platform_fuses; ++ ++ rc = cpr4_apss_parse_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_mem_acc_init(vreg); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_ipq807x_apss_calculate_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_limit_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr3_open_loop_voltage_as_ceiling(vreg); ++ ++ rc = cpr3_limit_floor_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr4_ipq807x_apss_calculate_target_quotients(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false); ++ if (rc) { ++ cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0 ++ || vreg->max_core_count > ++ IPQ807x_APSS_CPR_SDELTA_CORE_COUNT)) { ++ cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n", ++ vreg->max_core_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr4_apss_parse_boost_properties(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr4_apss_print_settings(vreg); ++ ++ return rc; ++} ++ ++/** ++ * cpr4_apss_init_controller() - perform APSS CPR4 controller specific ++ * initializations ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_controller(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_ctrl_data(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-down-error-step-limit", ++ &ctrl->down_error_step_limit); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-up-error-step-limit", ++ &ctrl->up_error_step_limit); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Use fixed step quotient if specified otherwise use dynamic ++ * calculated per RO step quotient ++ */ ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed", ++ &ctrl->step_quot_fixed); ++ ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true; ++ ++ ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node, ++ "qcom,cpr-saw-use-unit-mV"); ++ ++ of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-voltage-settling-time", ++ &ctrl->voltage_settling_time); ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-limit-supply", NULL)) { ++ ctrl->vdd_limit_regulator = ++ devm_regulator_get(ctrl->dev, "vdd-limit"); ++ if (IS_ERR(ctrl->vdd_limit_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_limit_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd-limit regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_apm_init(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_apss_parse_temp_adj_properties(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ ctrl->sensor_count = IPQ807x_APSS_CPR_SENSOR_COUNT; ++ ++ /* ++ * APSS only has one thread (0) per controller so the zeroed ++ * array does not need further modification. ++ */ ++ ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count, ++ sizeof(*ctrl->sensor_owner), GFP_KERNEL); ++ if (!ctrl->sensor_owner) ++ return -ENOMEM; ++ ++ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4; ++ ctrl->supports_hw_closed_loop = false; ++ ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node, ++ "qcom,cpr-hw-closed-loop"); ++ return 0; ++} ++ ++static int cpr4_apss_regulator_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_suspend(ctrl); ++} ++ ++static int cpr4_apss_regulator_resume(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_resume(ctrl); ++} ++ ++static void ipq6018_set_mem_acc(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ ipq6018_mem_acc_tcsr[0].ioremap_addr = ++ ioremap(ipq6018_mem_acc_tcsr[0].phy_addr, 0x4); ++ ipq6018_mem_acc_tcsr[1].ioremap_addr = ++ ioremap(ipq6018_mem_acc_tcsr[1].phy_addr, 0x4); ++ ++ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && ++ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && ++ (vreg->current_corner == (vreg->corner_count - CPR3_CORNER_OFFSET))) { ++ ++ writel_relaxed(ipq6018_mem_acc_tcsr[0].value, ++ ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ writel_relaxed(ipq6018_mem_acc_tcsr[1].value, ++ ipq6018_mem_acc_tcsr[1].ioremap_addr); ++ } ++} ++ ++static void ipq6018_clr_mem_acc(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && ++ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && ++ (vreg->current_corner != vreg->corner_count - CPR3_CORNER_OFFSET)) { ++ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[1].ioremap_addr); ++ } ++ ++ iounmap(ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ iounmap(ipq6018_mem_acc_tcsr[1].ioremap_addr); ++} ++ ++static struct cpr4_mem_acc_func ipq6018_mem_acc_funcs = { ++ .set_mem_acc = ipq6018_set_mem_acc, ++ .clear_mem_acc = ipq6018_clr_mem_acc ++}; ++ ++static const struct cpr4_reg_data ipq807x_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq807x_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static const struct cpr4_reg_data ipq817x_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ817x_APPS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq807x_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static const struct cpr4_reg_data ipq6018_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ6018_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq6018_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ6018_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq6018_fuse_params, ++ .mem_acc_funcs = &ipq6018_mem_acc_funcs, ++}; ++ ++static const struct cpr4_reg_data ipq9574_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ9574_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq9574_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ9574_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq9574_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static struct of_device_id cpr4_regulator_match_table[] = { ++ { ++ .compatible = "qcom,cpr4-ipq807x-apss-regulator", ++ .data = &ipq807x_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq817x-apss-regulator", ++ .data = &ipq817x_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq6018-apss-regulator", ++ .data = &ipq6018_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq9574-apss-regulator", ++ .data = &ipq9574_cpr_apss ++ }, ++ {} ++}; ++ ++static int cpr4_apss_regulator_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_controller *ctrl; ++ const struct of_device_id *match; ++ struct cpr4_reg_data *cpr_data; ++ int i, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -EINVAL; ++ } ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ match = of_match_device(cpr4_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr4_reg_data *)match->data; ++ g_valid_fuse_count = cpr_data->cpr_valid_fuse_count; ++ dev_info(dev, "CPR valid fuse count: %d\n", g_valid_fuse_count); ++ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; ++ ++ ctrl->dev = dev; ++ /* Set to false later if anything precludes CPR operation. */ ++ ctrl->cpr_allowed_hw = true; ++ ++ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", ++ &ctrl->name); ++ if (rc) { ++ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_map_fuse_base(ctrl, pdev); ++ if (rc) { ++ cpr3_err(ctrl, "could not map fuse base address\n"); ++ return rc; ++ } ++ ++ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_APSS_CPR_TCSR_START, ++ IPQ807x_APSS_CPR_TCSR_END); ++ if (rc) { ++ cpr3_err(ctrl, "could not read CPR tcsr setting\n"); ++ return rc; ++ } ++ ++ rc = cpr3_allocate_threads(ctrl, 0, 0); ++ if (rc) { ++ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread_count != 1) { ++ cpr3_err(ctrl, "expected 1 thread but found %d\n", ++ ctrl->thread_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr4_apss_init_controller(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_apss_init_thread(&ctrl->thread[0]); ++ if (rc) { ++ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { ++ ctrl->thread[0].vreg[i].cpr4_regulator_data = cpr_data; ++ rc = cpr4_apss_init_regulator(&ctrl->thread[0].vreg[i]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ platform_set_drvdata(pdev, ctrl); ++ ++ return cpr3_regulator_register(pdev, ctrl); ++} ++ ++static int cpr4_apss_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_unregister(ctrl); ++} ++ ++static struct platform_driver cpr4_apss_regulator_driver = { ++ .driver = { ++ .name = "qcom,cpr4-apss-regulator", ++ .of_match_table = cpr4_regulator_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = cpr4_apss_regulator_probe, ++ .remove = cpr4_apss_regulator_remove, ++ .suspend = cpr4_apss_regulator_suspend, ++ .resume = cpr4_apss_regulator_resume, ++}; ++ ++static int cpr4_regulator_init(void) ++{ ++ return platform_driver_register(&cpr4_apss_regulator_driver); ++} ++ ++static void cpr4_regulator_exit(void) ++{ ++ platform_driver_unregister(&cpr4_apss_regulator_driver); ++} ++ ++MODULE_DESCRIPTION("CPR4 APSS regulator driver"); ++MODULE_LICENSE("GPL v2"); ++ ++arch_initcall(cpr4_regulator_init); ++module_exit(cpr4_regulator_exit); +--- /dev/null ++++ b/include/soc/qcom/socinfo.h +@@ -0,0 +1,463 @@ ++/* Copyright (c) 2009-2014, 2016, 2020, 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 _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++ ++#include ++ ++#define CPU_IPQ8074 323 ++#define CPU_IPQ8072 342 ++#define CPU_IPQ8076 343 ++#define CPU_IPQ8078 344 ++#define CPU_IPQ8070 375 ++#define CPU_IPQ8071 376 ++ ++#define CPU_IPQ8072A 389 ++#define CPU_IPQ8074A 390 ++#define CPU_IPQ8076A 391 ++#define CPU_IPQ8078A 392 ++#define CPU_IPQ8070A 395 ++#define CPU_IPQ8071A 396 ++ ++#define CPU_IPQ8172 397 ++#define CPU_IPQ8173 398 ++#define CPU_IPQ8174 399 ++ ++#define CPU_IPQ6018 402 ++#define CPU_IPQ6028 403 ++#define CPU_IPQ6000 421 ++#define CPU_IPQ6010 422 ++#define CPU_IPQ6005 453 ++ ++#define CPU_IPQ5010 446 ++#define CPU_IPQ5018 447 ++#define CPU_IPQ5028 448 ++#define CPU_IPQ5000 503 ++#define CPU_IPQ0509 504 ++#define CPU_IPQ0518 505 ++ ++#define CPU_IPQ9514 510 ++#define CPU_IPQ9554 512 ++#define CPU_IPQ9570 513 ++#define CPU_IPQ9574 514 ++#define CPU_IPQ9550 511 ++#define CPU_IPQ9510 521 ++ ++static inline int read_ipq_soc_version_major(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "soc_version_major", ++ NULL); ++ ++ if (!prop) ++ return -EINVAL; ++ ++ return le32_to_cpu(*prop); ++} ++ ++static inline int read_ipq_cpu_type(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "cpu_type", NULL); ++ /* ++ * Return Default CPU type if "cpu_type" property is not found in DTSI ++ */ ++ if (!prop) ++ return CPU_IPQ8074; ++ ++ return le32_to_cpu(*prop); ++} ++ ++static inline int cpu_is_ipq8070(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8070a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8172(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8172; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8173(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8173; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8174(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8174; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6005(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6005; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0509(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0509; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0518(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0518; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9514(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9514; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9554(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9554; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9570(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9570; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9574(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9574; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9550(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9550; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9510(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9510; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq807x(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq8072() || cpu_is_ipq8074() || ++ cpu_is_ipq8076() || cpu_is_ipq8078() || ++ cpu_is_ipq8070() || cpu_is_ipq8071() || ++ cpu_is_ipq8072a() || cpu_is_ipq8074a() || ++ cpu_is_ipq8076a() || cpu_is_ipq8078a() || ++ cpu_is_ipq8070a() || cpu_is_ipq8071a() || ++ cpu_is_ipq8172() || cpu_is_ipq8173() || ++ cpu_is_ipq8174(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq60xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq6018() || cpu_is_ipq6028() || ++ cpu_is_ipq6000() || cpu_is_ipq6010() || ++ cpu_is_ipq6005(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq50xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq5010() || cpu_is_ipq5018() || ++ cpu_is_ipq5028() || cpu_is_ipq5000() || ++ cpu_is_ipq0509() || cpu_is_ipq0518(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq95xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq9514() || cpu_is_ipq9554() || ++ cpu_is_ipq9570() || cpu_is_ipq9574() || ++ cpu_is_ipq9550() || cpu_is_ipq9510(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_nss_crypto_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9570() || ++ cpu_is_ipq9550() || cpu_is_ipq9574() || ++ cpu_is_ipq9554(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_internal_wifi_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9514() || ++ cpu_is_ipq9554() || cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy1_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq9554() || cpu_is_ipq9570() || ++ cpu_is_ipq9574() || cpu_is_ipq9550(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy2_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq9570() || ++ cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++#endif /* _ARCH_ARM_MACH_MSM_SOCINFO_H_ */ diff --git a/target/linux/ipq807x/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch b/target/linux/ipq807x/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch new file mode 100644 index 00000000000..9b8b4df12b2 --- /dev/null +++ b/target/linux/ipq807x/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch @@ -0,0 +1,24 @@ +From 6baf7e4abcea6f7ac21eccf072a20078b39d064c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 9 Feb 2022 23:13:26 +0100 +Subject: [PATCH] arm64: dts: ipq8074: add label to clocks + +Add label to clocks node as that makes it easy to add the NSS fixed +clocks that are required in their DTSI. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -15,7 +15,7 @@ + compatible = "qcom,ipq8074"; + interrupt-parent = <&intc>; + +- clocks { ++ clocks: clocks { + sleep_clk: sleep_clk { + compatible = "fixed-clock"; + clock-frequency = <32768>; diff --git a/target/linux/lantiq/patches-5.15/0101-find_active_root.patch b/target/linux/lantiq/patches-5.15/0101-find_active_root.patch index 925ac9dbbac..14dc83f1f77 100644 --- a/target/linux/lantiq/patches-5.15/0101-find_active_root.patch +++ b/target/linux/lantiq/patches-5.15/0101-find_active_root.patch @@ -1,3 +1,13 @@ +From 2c82524000cca691c89c9fda251b55ef04eabcb6 Mon Sep 17 00:00:00 2001 +From: Mathias Kresin +Date: Mon, 2 May 2016 18:50:00 +0000 +Subject: [PATCH] find active root + +Signed-off-by: Mathias Kresin +--- + drivers/mtd/parsers/ofpart_core.c | 49 ++++++++++++++++++++++++++++++- + 1 file changed, 48 insertions(+), 1 deletion(-) + --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -38,6 +38,38 @@ static bool node_has_compatible(struct d diff --git a/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch b/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch index 10633199e67..0b99b91bd91 100644 --- a/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch +++ b/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch @@ -1,3 +1,26 @@ +From 1d1885f4a7abd7272f47b835b03d8662fb981d19 Mon Sep 17 00:00:00 2001 +From: Eddi De Pieri +Date: Tue, 14 Oct 2014 11:04:00 +0000 +Subject: [PATCH] MIPS: lantiq: ifxmips_pcie: use of + +Signed-off-by: Eddi De Pieri +--- + arch/mips/pci/Makefile | 2 +- + arch/mips/pci/ifxmips_pcie.c | 151 +++++++++++++++++++++++++++---- + arch/mips/pci/ifxmips_pcie_vr9.h | 105 --------------------- + 3 files changed, 133 insertions(+), 125 deletions(-) + +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,7 +43,7 @@ obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o + obj-$(CONFIG_SOC_MT7620) += pci-mt7620.o + obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o + obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o +-obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie_phy.o ifxmips_pcie.o fixup-lantiq-pcie.o ++obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie.o fixup-lantiq-pcie.o + obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o --- a/arch/mips/pci/ifxmips_pcie.c +++ b/arch/mips/pci/ifxmips_pcie.c @@ -16,8 +16,15 @@ @@ -374,14 +397,3 @@ static inline void pcie_core_pmu_setup(int pcie_port) { struct clk *clk; ---- a/arch/mips/pci/Makefile -+++ b/arch/mips/pci/Makefile -@@ -43,7 +43,7 @@ obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o - obj-$(CONFIG_SOC_MT7620) += pci-mt7620.o - obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o - obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o --obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie_phy.o ifxmips_pcie.o fixup-lantiq-pcie.o -+obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie.o fixup-lantiq-pcie.o - obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o - obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o - obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o diff --git a/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch b/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch index 49861945484..bf2962a3ffb 100644 --- a/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch +++ b/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch @@ -1,3 +1,18 @@ +From 4d48a3d1ef6f8d036bd926e3c1f70b56fcc679b2 Mon Sep 17 00:00:00 2001 +From: Stefan Koch +Date: Thu, 20 Oct 2016 21:32:00 +0200 +Subject: [PATCH] lantiq: vpe + +Signed-off-by: Stefan Koch +--- + arch/mips/Kconfig | 6 ++++ + arch/mips/include/asm/mipsmtregs.h | 5 ++++ + arch/mips/include/asm/vpe.h | 9 ++++++ + arch/mips/kernel/vpe-mt.c | 47 ++++++++++++++++++++++++++++++ + arch/mips/kernel/vpe.c | 35 ++++++++++++++++++++++ + arch/mips/lantiq/prom.c | 4 +++ + 6 files changed, 106 insertions(+) + --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2429,6 +2429,12 @@ config MIPS_VPE_LOADER @@ -13,6 +28,27 @@ config MIPS_VPE_LOADER_CMP bool default "y" +--- a/arch/mips/include/asm/mipsmtregs.h ++++ b/arch/mips/include/asm/mipsmtregs.h +@@ -32,6 +32,9 @@ + #define read_c0_vpeconf1() __read_32bit_c0_register($1, 3) + #define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val) + ++#define read_c0_vpeopt() __read_32bit_c0_register($1, 7) ++#define write_c0_vpeopt(val) __write_32bit_c0_register($1, 7, val) ++ + #define read_c0_tcstatus() __read_32bit_c0_register($2, 1) + #define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val) + +@@ -378,6 +381,8 @@ do { \ + #define write_vpe_c0_vpeconf0(val) mttc0(1, 2, val) + #define read_vpe_c0_vpeconf1() mftc0(1, 3) + #define write_vpe_c0_vpeconf1(val) mttc0(1, 3, val) ++#define read_vpe_c0_vpeopt() mftc0(1, 7) ++#define write_vpe_c0_vpeopt(val) mttc0(1, 7, val) + #define read_vpe_c0_count() mftc0(9, 0) + #define write_vpe_c0_count(val) mttc0(9, 0, val) + #define read_vpe_c0_status() mftc0(12, 0) --- a/arch/mips/include/asm/vpe.h +++ b/arch/mips/include/asm/vpe.h @@ -124,4 +124,13 @@ void cleanup_tc(struct tc *tc); @@ -157,24 +193,3 @@ int ltq_soc_type(void) { ---- a/arch/mips/include/asm/mipsmtregs.h -+++ b/arch/mips/include/asm/mipsmtregs.h -@@ -32,6 +32,9 @@ - #define read_c0_vpeconf1() __read_32bit_c0_register($1, 3) - #define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val) - -+#define read_c0_vpeopt() __read_32bit_c0_register($1, 7) -+#define write_c0_vpeopt(val) __write_32bit_c0_register($1, 7, val) -+ - #define read_c0_tcstatus() __read_32bit_c0_register($2, 1) - #define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val) - -@@ -378,6 +381,8 @@ do { \ - #define write_vpe_c0_vpeconf0(val) mttc0(1, 2, val) - #define read_vpe_c0_vpeconf1() mftc0(1, 3) - #define write_vpe_c0_vpeconf1(val) mttc0(1, 3, val) -+#define read_vpe_c0_vpeopt() mftc0(1, 7) -+#define write_vpe_c0_vpeopt(val) mttc0(1, 7, val) - #define read_vpe_c0_count() mftc0(9, 0) - #define write_vpe_c0_count(val) mttc0(9, 0, val) - #define read_vpe_c0_status() mftc0(12, 0) diff --git a/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch b/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch index d6556d115df..9214f786d7b 100644 --- a/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch +++ b/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch @@ -1,3 +1,13 @@ +From 3c92a781de062064e36b867c0ab22f9aba48f3d3 Mon Sep 17 00:00:00 2001 +From: Eddi De Pieri +Date: Tue, 8 Nov 2016 17:38:00 +0100 +Subject: [PATCH] lantiq: pci: bar11mask fix + +Signed-off-by: Eddi De Pieri +--- + arch/mips/pci/pci-lantiq.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -59,6 +59,8 @@ diff --git a/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch b/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch index 898c2d48212..6562dc8bdab 100644 --- a/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch +++ b/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch @@ -1,3 +1,13 @@ +From 07ce9e9bc4dcd5ac4728e587901112eef95bbe7b Mon Sep 17 00:00:00 2001 +From: Stefan Koch +Date: Mon, 13 Mar 2017 23:42:00 +0100 +Subject: [PATCH] lantiq: vpe nosmp + +Signed-off-by: Stefan Koch +--- + arch/mips/kernel/vpe-mt.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + --- a/arch/mips/kernel/vpe-mt.c +++ b/arch/mips/kernel/vpe-mt.c @@ -132,7 +132,10 @@ int vpe_run(struct vpe *v) diff --git a/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch b/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch index 796220b2bc2..a83325c094d 100644 --- a/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch +++ b/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch @@ -1,3 +1,13 @@ +From ebaae1cd68cd79c7eee67c9c5c0fa45809e84525 Mon Sep 17 00:00:00 2001 +From: Maikel Bloemendal +Date: Fri, 14 Nov 2014 17:06:00 +0000 +Subject: [PATCH] owrt: lantiq: multiple flash + +Signed-off-by: Maikel Bloemendal +--- + drivers/mtd/maps/lantiq-flash.c | 168 +++++++++++++++++++++----------- + 1 file changed, 109 insertions(+), 59 deletions(-) + --- a/drivers/mtd/maps/lantiq-flash.c +++ b/drivers/mtd/maps/lantiq-flash.c @@ -17,6 +17,7 @@ diff --git a/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch b/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch index d153c521d33..f62d1670784 100644 --- a/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch +++ b/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch @@ -1,3 +1,13 @@ +From 5e93c85ac3e5626d1aa7e7f9c0a008b2a4224f04 Mon Sep 17 00:00:00 2001 +From: Matti Laakso +Date: Sat, 14 Feb 2015 20:48:00 +0000 +Subject: [PATCH] MTD: cfi_cmdset_0001: disable buffered writes + +Signed-off-by: Matti Laakso +--- + drivers/mtd/chips/cfi_cmdset_0001.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -39,7 +39,7 @@ diff --git a/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch b/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch index 92c4b564930..6dacba56d5a 100644 --- a/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch +++ b/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch @@ -1,3 +1,13 @@ +From 5502ef9d40ab20b2ac683660d1565a7c4968bcc8 Mon Sep 17 00:00:00 2001 +From: Mathias Kresin +Date: Mon, 2 May 2016 18:50:00 +0000 +Subject: [PATCH] xrx200: add gphy clk src device tree binding + +Signed-off-by: Mathias Kresin +--- + arch/mips/lantiq/xway/sysctrl.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -440,6 +440,20 @@ static void clkdev_add_clkout(void) diff --git a/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch b/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch index 2cc541ae3cf..7e49b47e028 100644 --- a/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch +++ b/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch @@ -1,3 +1,13 @@ +From 870ed9cae083ff8a60a739ef7e74c5a1800533be Mon Sep 17 00:00:00 2001 +From: Johann Neuhauser +Date: Thu, 17 May 2018 19:12:35 +0200 +Subject: [PATCH] net: lantiq_etop: of mdio + +Signed-off-by: Johann Neuhauser +--- + drivers/net/ethernet/lantiq_etop.c | 555 +++++++++++++++++++++++++----------- + 1 file changed, 389 insertions(+), 166 deletions(-) + --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -30,6 +30,7 @@ diff --git a/target/linux/layerscape/armv8_64b/config-5.15 b/target/linux/layerscape/armv8_64b/config-5.15 index df0a3fd849c..69edc7a3f07 100644 --- a/target/linux/layerscape/armv8_64b/config-5.15 +++ b/target/linux/layerscape/armv8_64b/config-5.15 @@ -269,7 +269,6 @@ CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set CONFIG_FRAME_POINTER=y -CONFIG_FRAME_WARN=2048 CONFIG_FREEZER=y # CONFIG_FSL_BMAN_TEST is not set CONFIG_FSL_DPAA=y @@ -291,7 +290,7 @@ CONFIG_FSL_GUTS=y CONFIG_FSL_IFC=y CONFIG_FSL_MC_BUS=y CONFIG_FSL_MC_DPIO=y -# CONFIG_FSL_MC_UAPI_SUPPORT is not set +CONFIG_FSL_MC_UAPI_SUPPORT=y # CONFIG_FSL_PPFE is not set # CONFIG_FSL_PPFE_UTIL_DISABLED is not set # CONFIG_FSL_QMAN_TEST is not set diff --git a/target/linux/malta/Makefile b/target/linux/malta/Makefile index 2ca43fa2e8b..bff63a1dd4b 100644 --- a/target/linux/malta/Makefile +++ b/target/linux/malta/Makefile @@ -11,6 +11,7 @@ INITRAMFS_EXTRA_FILES:= FEATURES:=cpiogz ext4 ramdisk squashfs targz KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 include $(INCLUDE_DIR)/target.mk diff --git a/target/linux/malta/config-6.1 b/target/linux/malta/config-6.1 new file mode 100644 index 00000000000..9411879835b --- /dev/null +++ b/target/linux/malta/config-6.1 @@ -0,0 +1,267 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ATA=y +CONFIG_ATA_PIIX=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_BSG_COMMON=y +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BOARD_SCACHE=y +CONFIG_BOOT_ELF32=y +CONFIG_BUILTIN_DTB=y +CONFIG_CEVT_R4K=y +CONFIG_CLKBLD_I8253=y +CONFIG_CLKEVT_I8253=y +CONFIG_CLKSRC_I8253=y +CONFIG_CLKSRC_MIPS_GIC=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100 +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +# CONFIG_CPU_HAS_SMARTMIPS is not set +CONFIG_CPU_HAS_SYNC=y +# CONFIG_CPU_MICROMIPS is not set +# CONFIG_CPU_MIPS32 is not set +# CONFIG_CPU_MIPS32_3_5_FEATURES is not set +# CONFIG_CPU_MIPS32_R1 is not set +# CONFIG_CPU_MIPS32_R2 is not set +# CONFIG_CPU_MIPS32_R5 is not set +# CONFIG_CPU_MIPS32_R5_FEATURES is not set +# CONFIG_CPU_MIPS32_R6 is not set +# CONFIG_CPU_MIPS64_R1 is not set +# CONFIG_CPU_MIPS64_R2 is not set +# CONFIG_CPU_MIPS64_R6 is not set +# CONFIG_CPU_MIPSR1 is not set +# CONFIG_CPU_MIPSR2 is not set +# CONFIG_CPU_MIPSR2_IRQ_EI is not set +# CONFIG_CPU_MIPSR2_IRQ_VI is not set +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +# CONFIG_CPU_NEVADA is not set +CONFIG_CPU_R4K_CACHE_TLB=y +# CONFIG_CPU_RM7000 is not set +CONFIG_CPU_RMAP=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRC16=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_RNG2=y +CONFIG_CSRC_R4K=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DNOTIFY=y +CONFIG_DTC=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_EXT4_FS=y +CONFIG_F2FS_FS=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HW_CONSOLE=y +CONFIG_I8253=y +CONFIG_I8253_LOCK=y +CONFIG_I8259=y +CONFIG_INPUT=y +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_ISA_DMA_API=y +CONFIG_JBD2=y +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_KALLSYMS=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_XZ is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MD=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_BONITO64=y +CONFIG_MIPS_CLOCK_VSYSCALL=y +CONFIG_MIPS_CM=y +CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER=y +CONFIG_MIPS_CPC=y +CONFIG_MIPS_CPU_SCACHE=y +CONFIG_MIPS_EBPF_JIT=y +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_GIC=y +CONFIG_MIPS_L1_CACHE_SHIFT=6 +CONFIG_MIPS_L1_CACHE_SHIFT_6=y +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +CONFIG_MIPS_MALTA=y +CONFIG_MIPS_MSC=y +CONFIG_MIPS_MT=y +CONFIG_MIPS_MT_FPAFF=y +CONFIG_MIPS_MT_SMP=y +CONFIG_MIPS_NO_APPENDED_DTB=y +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MTD_CFI_STAA=y +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NLS=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_PADATA=y +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_PATA_LEGACY=y +CONFIG_PATA_TIMINGS=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PCI_GT64XXX_PCI0=y +CONFIG_PCSPKR_PLATFORM=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_PIIX4_POWEROFF=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QFMT_V2=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_QUOTA=y +CONFIG_QUOTACTL=y +CONFIG_QUOTA_TREE=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELAY=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_MC146818_LIB=y +CONFIG_SATA_HOST=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SECCOMP=y +CONFIG_SECCOMP_FILTER=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SYNC_R4K=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_CPU_MIPS32_R3_5=y +CONFIG_SYS_HAS_CPU_MIPS32_R5=y +CONFIG_SYS_HAS_CPU_MIPS32_R6=y +CONFIG_SYS_HAS_CPU_MIPS64_R1=y +CONFIG_SYS_HAS_CPU_MIPS64_R2=y +CONFIG_SYS_HAS_CPU_MIPS64_R6=y +CONFIG_SYS_HAS_CPU_NEVADA=y +CONFIG_SYS_HAS_CPU_RM7000=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_64BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HIGHMEM=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_SYS_SUPPORTS_MICROMIPS=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_SYS_SUPPORTS_MIPS_CMP=y +CONFIG_SYS_SUPPORTS_MIPS_CPS=y +CONFIG_SYS_SUPPORTS_MULTITHREADING=y +CONFIG_SYS_SUPPORTS_RELOCATABLE=y +CONFIG_SYS_SUPPORTS_SCHED_SMT=y +CONFIG_SYS_SUPPORTS_SMARTMIPS=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_SYS_SUPPORTS_VPE_LOADER=y +CONFIG_SYS_SUPPORTS_ZBOOT=y +CONFIG_TARGET_ISA_REV=1 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_VXFS_FS=y +CONFIG_WAR_ICACHE_REFILLS=y +CONFIG_XPS=y +CONFIG_ZBOOT_LOAD_ADDRESS=0x0 diff --git a/target/linux/mediatek/dts/mt7981b-qihoo-360t7.dts b/target/linux/mediatek/dts/mt7981b-qihoo-360t7.dts new file mode 100644 index 00000000000..87076d255cf --- /dev/null +++ b/target/linux/mediatek/dts/mt7981b-qihoo-360t7.dts @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; +#include +#include + +#include "mt7981.dtsi" + +/ { + model = "Qihoo 360T7"; + compatible = "qihoo,360t7", "mediatek,mt7981"; + + aliases { + serial0 = &uart0; + led-boot = &led_status_red; + led-failsafe = &led_status_red; + led-running = &led_status_green; + led-upgrade = &led_status_green; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_LOW>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_status_red: red { + label = "red:status"; + gpios = <&pio 3 GPIO_ACTIVE_LOW>; + }; + + led_status_green: green { + label = "green:status"; + gpios = <&pio 7 GPIO_ACTIVE_LOW>; + }; + }; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; +}; + +&mdio_bus { + switch: switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 GPIO_ACTIVE_HIGH>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&pio>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand@0 { + compatible = "spi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bl2"; + reg = <0x0000000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x0180000 0x0200000>; + read-only; + }; + + partition@380000 { + label = "fip"; + reg = <0x0380000 0x0200000>; + read-only; + }; + + partition@580000 { + label = "ubi"; + reg = <0x0580000 0x6c00000>; + }; + + partition@7180000 { + label = "config"; + reg = <0x7180000 0x0100000>; + read-only; + }; + + partition@7280000 { + label = "factory"; + reg = <0x7280000 0x0080000>; + read-only; + }; + + partition@7300000 { + label = "log"; + reg = <0x7300000 0x0700000>; + read-only; + }; + }; + }; +}; + +&switch { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan3"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan1"; + }; + + port@3 { + reg = <3>; + label = "wan"; + }; + + port@6 { + reg = <6>; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; +}; + +&pio { + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&wifi { + status = "okay"; + + mediatek,mtd-eeprom = <&factory 0x0>; +}; diff --git a/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi b/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi index 95b548143b4..304566810a0 100644 --- a/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi +++ b/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi @@ -164,6 +164,13 @@ #size-cells = <2>; ranges; + /* 64 KiB reserved for ramoops/pstore */ + ramoops@42ff0000 { + 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>; @@ -317,8 +324,10 @@ interrupts = ; clock-div = <1>; clocks = <&infracfg CLK_INFRA_I2C0_CK>, - <&infracfg CLK_INFRA_AP_DMA_CK>; - clock-names = "main", "dma"; + <&infracfg CLK_INFRA_AP_DMA_CK>, + <&infracfg CLK_INFRA_I2C_MCK_CK>, + <&infracfg CLK_INFRA_I2C_PCK_CK>; + clock-names = "main", "dma", "arb", "pmic"; #address-cells = <1>; #size-cells = <0>; status = "disabled"; diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index 2fca00921b8..a327a46108a 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -29,6 +29,9 @@ mediatek_setup_interfaces() mediatek,mt7988a-dsa-10g-spim-snand) ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" "eth1 eth2" ;; + qihoo,360t7) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" wan + ;; tplink,tl-xdr4288|\ tplink,tl-xdr6088) ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 lan5" eth1 @@ -63,6 +66,11 @@ mediatek_setup_macs() bananapi,bpi-r3) wan_mac=$(macaddr_add $(cat /sys/class/net/eth0/address) 1) ;; + qihoo,360t7) + lan_mac=$(mtd_get_mac_ascii factory lanMac) + wan_mac=$(macaddr_add "$lan_mac" 1) + label_mac=$wan_mac + ;; xiaomi,redmi-router-ax6000-stock|\ xiaomi,redmi-router-ax6000-ubootmod) wan_mac=$(mtd_get_mac_ascii Bdata ethaddr_wan) diff --git a/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac index a56e5bc6893..a3db00f5e8f 100644 --- a/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac +++ b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac @@ -32,6 +32,11 @@ case "$board" in [ "$PHYNBR" = "0" ] && echo "$addr" > /sys${DEVPATH}/macaddress [ "$PHYNBR" = "1" ] && macaddr_setbit_la $(macaddr_add $addr 1) > /sys${DEVPATH}/macaddress ;; + qihoo,360t7) + addr=$(mtd_get_mac_ascii factory lanMac) + [ "$PHYNBR" = "0" ] && macaddr_add $addr 2 > /sys${DEVPATH}/macaddress + [ "$PHYNBR" = "1" ] && macaddr_add $addr 3 > /sys${DEVPATH}/macaddress + ;; tplink,tl-xdr4288|\ tplink,tl-xdr6086|\ tplink,tl-xdr6088) diff --git a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh index 9a7a5aaffb0..5b624bc5517 100755 --- a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh @@ -70,6 +70,7 @@ platform_do_upgrade() { cudy,wr3000-v1) default_do_upgrade "$1" ;; + qihoo,360t7|\ tplink,tl-xdr4288|\ tplink,tl-xdr6086|\ tplink,tl-xdr6088|\ diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index 72adb490666..582c1f70b8f 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -6,11 +6,19 @@ define Image/Prepare echo -ne '\xde\xad\xc0\xde' > $(KDIR)/ubi_mark endef -define Build/bl2 +define Build/mt7981-bl2 + cat $(STAGING_DIR_IMAGE)/mt7981-$1-bl2.img >> $@ +endef + +define Build/mt7981-bl31-uboot + cat $(STAGING_DIR_IMAGE)/mt7981_$1-u-boot.fip >> $@ +endef + +define Build/mt7986-bl2 cat $(STAGING_DIR_IMAGE)/mt7986-$1-bl2.img >> $@ endef -define Build/bl31-uboot +define Build/mt7986-bl31-uboot cat $(STAGING_DIR_IMAGE)/mt7986_$1-u-boot.fip >> $@ endef @@ -99,28 +107,29 @@ define Device/bananapi_bpi-r3 nor-preloader.bin nor-bl31-uboot.fip \ sdcard.img.gz \ snand-preloader.bin snand-bl31-uboot.fip - ARTIFACT/emmc-preloader.bin := bl2 emmc-ddr4 - ARTIFACT/emmc-bl31-uboot.fip := bl31-uboot bananapi_bpi-r3-emmc - ARTIFACT/nor-preloader.bin := bl2 nor-ddr4 - ARTIFACT/nor-bl31-uboot.fip := bl31-uboot bananapi_bpi-r3-nor - ARTIFACT/snand-preloader.bin := bl2 spim-nand-ddr4 - ARTIFACT/snand-bl31-uboot.fip := bl31-uboot bananapi_bpi-r3-snand + ARTIFACT/emmc-preloader.bin := mt7986-bl2 emmc-ddr4 + ARTIFACT/emmc-bl31-uboot.fip := mt7986-bl31-uboot bananapi_bpi-r3-emmc + ARTIFACT/nor-preloader.bin := mt7986-bl2 nor-ddr4 + ARTIFACT/nor-bl31-uboot.fip := mt7986-bl31-uboot bananapi_bpi-r3-nor + ARTIFACT/snand-preloader.bin := mt7986-bl2 spim-nand-ddr4 + ARTIFACT/snand-bl31-uboot.fip := mt7986-bl31-uboot bananapi_bpi-r3-snand ARTIFACT/sdcard.img.gz := mt7986-gpt sdmmc |\ - pad-to 17k | bl2 sdmmc-ddr4 |\ - pad-to 6656k | bl31-uboot bananapi_bpi-r3-sdmmc |\ + pad-to 17k | mt7986-bl2 sdmmc-ddr4 |\ + pad-to 6656k | mt7986-bl31-uboot bananapi_bpi-r3-sdmmc |\ $(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),\ pad-to 12M | append-image-stage initramfs-recovery.itb | check-size 44m |\ ) \ - pad-to 44M | bl2 spim-nand-ddr4 |\ - pad-to 45M | bl31-uboot bananapi_bpi-r3-snand |\ - pad-to 49M | bl2 nor-ddr4 |\ - pad-to 50M | bl31-uboot bananapi_bpi-r3-nor |\ - pad-to 51M | bl2 emmc-ddr4 |\ - pad-to 52M | bl31-uboot bananapi_bpi-r3-emmc |\ + pad-to 44M | mt7986-bl2 spim-nand-ddr4 |\ + pad-to 45M | mt7986-bl31-uboot bananapi_bpi-r3-snand |\ + pad-to 49M | mt7986-bl2 nor-ddr4 |\ + pad-to 50M | mt7986-bl31-uboot bananapi_bpi-r3-nor |\ + pad-to 51M | mt7986-bl2 emmc-ddr4 |\ + pad-to 52M | mt7986-bl31-uboot bananapi_bpi-r3-emmc |\ pad-to 56M | mt7986-gpt emmc |\ $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),\ - pad-to 64M | append-image squashfs-sysupgrade.itb | check-size | gzip \ - ) + pad-to 64M | append-image squashfs-sysupgrade.itb | check-size |\ + ) \ + gzip IMAGE_SIZE := $$(shell expr 64 + $$(CONFIG_TARGET_ROOTFS_PARTSIZE))m KERNEL := kernel-bin | gzip KERNEL_INITRAMFS := kernel-bin | lzma | \ @@ -224,6 +233,30 @@ define Device/mediatek_mt7988a-rfb-nand endef TARGET_DEVICES += mediatek_mt7988a-rfb-nand +define Device/qihoo_360t7 + DEVICE_VENDOR := Qihoo + DEVICE_MODEL := 360T7 + DEVICE_DTS := mt7981b-qihoo-360t7 + DEVICE_DTS_DIR := ../dts + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + KERNEL_IN_UBI := 1 + UBOOTENV_IN_UBI := 1 + IMAGES := sysupgrade.itb + KERNEL_INITRAMFS_SUFFIX := -recovery.itb + KERNEL := kernel-bin | gzip + KERNEL_INITRAMFS := kernel-bin | lzma | \ + fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 64k + IMAGE/sysupgrade.itb := append-kernel | \ + fit gzip $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb external-static-with-rootfs | append-metadata + DEVICE_PACKAGES := kmod-mt7981-firmware mt7981-wo-firmware + ARTIFACTS := preloader.bin bl31-uboot.fip + ARTIFACT/preloader.bin := mt7981-bl2 spim-nand-ddr3 + ARTIFACT/bl31-uboot.fip := mt7981-bl31-uboot qihoo_360t7 +endef +TARGET_DEVICES += qihoo_360t7 + define Device/tplink_tl-xdr-common DEVICE_VENDOR := TP-Link DEVICE_DTS_DIR := ../dts @@ -241,13 +274,13 @@ define Device/tplink_tl-xdr-common fit gzip $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb external-static-with-rootfs | append-metadata DEVICE_PACKAGES := kmod-usb3 kmod-mt7986-firmware mt7986-wo-firmware ARTIFACTS := preloader.bin bl31-uboot.fip - ARTIFACT/preloader.bin := bl2 spim-nand-ddr3 + ARTIFACT/preloader.bin := mt7986-bl2 spim-nand-ddr3 endef define Device/tplink_tl-xdr4288 DEVICE_MODEL := TL-XDR4288 DEVICE_DTS := mt7986a-tplink-tl-xdr4288 - ARTIFACT/bl31-uboot.fip := bl31-uboot tplink_tl-xdr4288 + ARTIFACT/bl31-uboot.fip := mt7986-bl31-uboot tplink_tl-xdr4288 $(call Device/tplink_tl-xdr-common) endef TARGET_DEVICES += tplink_tl-xdr4288 @@ -255,7 +288,7 @@ TARGET_DEVICES += tplink_tl-xdr4288 define Device/tplink_tl-xdr6086 DEVICE_MODEL := TL-XDR6086 DEVICE_DTS := mt7986a-tplink-tl-xdr6086 - ARTIFACT/bl31-uboot.fip := bl31-uboot tplink_tl-xdr6086 + ARTIFACT/bl31-uboot.fip := mt7986-bl31-uboot tplink_tl-xdr6086 $(call Device/tplink_tl-xdr-common) endef TARGET_DEVICES += tplink_tl-xdr6086 @@ -263,7 +296,7 @@ TARGET_DEVICES += tplink_tl-xdr6086 define Device/tplink_tl-xdr6088 DEVICE_MODEL := TL-XDR6088 DEVICE_DTS := mt7986a-tplink-tl-xdr6088 - ARTIFACT/bl31-uboot.fip := bl31-uboot tplink_tl-xdr6088 + ARTIFACT/bl31-uboot.fip := mt7986-bl31-uboot tplink_tl-xdr6088 $(call Device/tplink_tl-xdr-common) endef TARGET_DEVICES += tplink_tl-xdr6088 @@ -304,8 +337,8 @@ define Device/xiaomi_redmi-router-ax6000-ubootmod IMAGE/sysupgrade.itb := append-kernel | \ fit gzip $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb external-static-with-rootfs | append-metadata ARTIFACTS := preloader.bin bl31-uboot.fip - ARTIFACT/preloader.bin := bl2 spim-nand-ddr4 - ARTIFACT/bl31-uboot.fip := bl31-uboot xiaomi_redmi-router-ax6000 + ARTIFACT/preloader.bin := mt7986-bl2 spim-nand-ddr4 + ARTIFACT/bl31-uboot.fip := mt7986-bl31-uboot xiaomi_redmi-router-ax6000 ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) ARTIFACTS += initramfs-factory.ubi ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-recovery.itb | ubinize-kernel diff --git a/target/linux/mediatek/image/mt7622.mk b/target/linux/mediatek/image/mt7622.mk index 12e819ed545..92824ff24a6 100644 --- a/target/linux/mediatek/image/mt7622.mk +++ b/target/linux/mediatek/image/mt7622.mk @@ -88,9 +88,10 @@ define Device/bananapi_bpi-r64 pad-to 40960k | bl31-uboot bananapi_bpi-r64-emmc |\ pad-to 43008k | bl2 snand-2ddr |\ pad-to 43520k | bl31-uboot bananapi_bpi-r64-snand |\ - $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),\ - pad-to 46080k | append-image squashfs-sysupgrade.itb | check-size | gzip \ - ) + $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS), \ + pad-to 46080k | append-image squashfs-sysupgrade.itb | check-size |\ + ) \ + gzip IMAGE_SIZE := $$(shell expr 45 + $$(CONFIG_TARGET_ROOTFS_PARTSIZE))m KERNEL := kernel-bin | gzip KERNEL_INITRAMFS := kernel-bin | lzma | fit lzma $$(DTS_DIR)/$$(DEVICE_DTS).dtb with-initrd | pad-to 128k diff --git a/target/linux/mediatek/image/mt7623.mk b/target/linux/mediatek/image/mt7623.mk index 1645ecd5450..5828c4d763d 100644 --- a/target/linux/mediatek/image/mt7623.mk +++ b/target/linux/mediatek/image/mt7623.mk @@ -96,6 +96,7 @@ define Device/bananapi_bpi-r2 KERNEL := kernel-bin | gzip KERNEL_INITRAMFS_SUFFIX := -recovery.itb KERNEL_INITRAMFS := kernel-bin | gzip | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb with-initrd + IMAGE_SIZE := $$(shell expr 48 + $$(CONFIG_TARGET_ROOTFS_PARTSIZE))m IMAGE/sysupgrade.itb := append-kernel | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb external-static-with-rootfs | append-metadata ARTIFACT/preloader.bin := mt7623-mbr emmc |\ pad-to 2k | append-preloader $$(UBOOT_TARGET) @@ -105,10 +106,10 @@ define Device/bananapi_bpi-r2 pad-to $$(UBOOT_OFFSET) | append-bootloader $$(UBOOT_TARGET) |\ pad-to 4092k | mt7623-mbr emmc |\ $(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),\ - pad-to 4M | append-image-stage initramfs-recovery.itb |\ + pad-to 4M | append-image-stage initramfs-recovery.itb | check-size 48m |\ ) \ $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),\ - pad-to 48M | append-image squashfs-sysupgrade.itb |\ + pad-to 48M | append-image squashfs-sysupgrade.itb | check-size |\ ) \ gzip ARTIFACTS := u-boot.bin preloader.bin sdcard.img.gz @@ -129,6 +130,7 @@ define Device/unielec_u7623-02 UBOOT_TARGET := mt7623a_unielec_u7623 UBOOT_IMAGE := u-boot-mtk.bin UBOOT_PATH := $(STAGING_DIR_IMAGE)/$$(UBOOT_TARGET)-$$(UBOOT_IMAGE) + IMAGE_SIZE := $$(shell expr 48 + $$(CONFIG_TARGET_ROOTFS_PARTSIZE))m IMAGES := sysupgrade.itb KERNEL := kernel-bin | gzip KERNEL_INITRAMFS_SUFFIX := -recovery.itb @@ -140,10 +142,10 @@ define Device/unielec_u7623-02 ARTIFACT/emmc.img.gz := mt7623-mbr sdmmc |\ pad-to $$(UBOOT_OFFSET) | append-bootloader $$(UBOOT_TARGET) |\ $(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),\ - pad-to 4M | append-image-stage initramfs-recovery.itb |\ + pad-to 4M | append-image-stage initramfs-recovery.itb | check-size 48m |\ ) \ $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),\ - pad-to 48M | append-image squashfs-sysupgrade.itb |\ + pad-to 48M | append-image squashfs-sysupgrade.itb | check-size |\ ) \ gzip | append-metadata ARTIFACT/scatter.txt := scatterfile emmc.img.gz diff --git a/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-correct-voltages-for-MT7622-and-MT7.patch b/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-correct-voltages-for-MT7622-and-MT7.patch new file mode 100644 index 00000000000..6ab05b897c3 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-correct-voltages-for-MT7622-and-MT7.patch @@ -0,0 +1,53 @@ +From e7697814c142c99f470c3458d49e41b25a575f23 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 26 May 2023 10:31:40 +0100 +Subject: [PATCH] cpufreq: mediatek: correct voltages for MT7622 and MT7623 + +The MT6380 regulator typically used together with MT7622 does not +support the current maximum processor and SRAM voltage in the cpufreq +driver (1360000uV). +For MT7622 limit processor and SRAM supply voltages to 1350000uV to +avoid having the tracking algorithm request unsupported voltages from +the regulator. + +On MT7623 there is no separate SRAM supply and the maximum voltage used +is 1300000uV. Create dedicated platform data for MT7623 to cover that +case as well. + +Fixes: 0883426fd07e3 ("cpufreq: mediatek: Raise proc and sram max voltage for MT7622/7623") +Suggested-by: Jia-wei Chang +Signed-off-by: Daniel Golle +--- + drivers/cpufreq/mediatek-cpufreq.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/cpufreq/mediatek-cpufreq.c ++++ b/drivers/cpufreq/mediatek-cpufreq.c +@@ -696,9 +696,16 @@ static const struct mtk_cpufreq_platform + static const struct mtk_cpufreq_platform_data mt7622_platform_data = { + .min_volt_shift = 100000, + .max_volt_shift = 200000, +- .proc_max_volt = 1360000, ++ .proc_max_volt = 1350000, + .sram_min_volt = 0, +- .sram_max_volt = 1360000, ++ .sram_max_volt = 1350000, ++ .ccifreq_supported = false, ++}; ++ ++static const struct mtk_cpufreq_platform_data mt7623_platform_data = { ++ .min_volt_shift = 100000, ++ .max_volt_shift = 200000, ++ .proc_max_volt = 1300000, + .ccifreq_supported = false, + }; + +@@ -743,7 +750,7 @@ static const struct of_device_id mtk_cpu + { .compatible = "mediatek,mt2701", .data = &mt2701_platform_data }, + { .compatible = "mediatek,mt2712", .data = &mt2701_platform_data }, + { .compatible = "mediatek,mt7622", .data = &mt7622_platform_data }, +- { .compatible = "mediatek,mt7623", .data = &mt7622_platform_data }, ++ { .compatible = "mediatek,mt7623", .data = &mt7623_platform_data }, + { .compatible = "mediatek,mt7988", .data = &mt7988_platform_data }, + { .compatible = "mediatek,mt8167", .data = &mt8516_platform_data }, + { .compatible = "mediatek,mt817x", .data = &mt2701_platform_data }, diff --git a/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-don-t-request-unsupported-voltage.patch b/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-don-t-request-unsupported-voltage.patch deleted file mode 100644 index a7a4bd8ea2b..00000000000 --- a/target/linux/mediatek/patches-5.15/351-cpufreq-mediatek-don-t-request-unsupported-voltage.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 20aad28ba5d62f1618408c264384d0b2ad7417db Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Mon, 22 May 2023 23:25:48 +0100 -Subject: [PATCH] cpufreq: mediatek: don't request unsupported voltage - -PMICs on MT7622 and MT7623 boards only support up to 1350000uV despite -the SoC's processor and SRAM voltage can be up to 1360000uV. As a -work-around specify max. processor and SRAM voltage as 1350000uV to -avoid requesting an unsupported voltage from the regulator. - -Signed-off-by: Daniel Golle ---- - drivers/cpufreq/mediatek-cpufreq.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/cpufreq/mediatek-cpufreq.c -+++ b/drivers/cpufreq/mediatek-cpufreq.c -@@ -696,9 +696,9 @@ static const struct mtk_cpufreq_platform - static const struct mtk_cpufreq_platform_data mt7622_platform_data = { - .min_volt_shift = 100000, - .max_volt_shift = 200000, -- .proc_max_volt = 1360000, -+ .proc_max_volt = 1350000, - .sram_min_volt = 0, -- .sram_max_volt = 1360000, -+ .sram_max_volt = 1350000, - .ccifreq_supported = false, - }; - diff --git a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch index 04ca80c213d..84718d300b5 100644 --- a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch +++ b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch @@ -103,7 +103,7 @@ Signed-off-by: David S. Miller ret = mtk_mdio_busy_wait(eth); if (ret < 0) -@@ -1018,6 +1061,7 @@ static int mtk_mdio_init(struct mtk_eth +@@ -1013,6 +1056,7 @@ static int mtk_mdio_init(struct mtk_eth eth->mii_bus->name = "mdio"; eth->mii_bus->read = mtk_mdio_read; eth->mii_bus->write = mtk_mdio_write; diff --git a/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch b/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch deleted file mode 100644 index f9ad79c4430..00000000000 --- a/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch +++ /dev/null @@ -1,74 +0,0 @@ -From bd4f7dae6a1f2fd65bb2112783c92ffe0839bc77 Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Tue, 28 Feb 2023 23:53:56 +0000 -Subject: [PATCH] i2c: mediatek: add support for MT7981 SoC - -Signed-off-by: Daniel Golle ---- - drivers/i2c/busses/i2c-mt65xx.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - ---- a/drivers/i2c/busses/i2c-mt65xx.c -+++ b/drivers/i2c/busses/i2c-mt65xx.c -@@ -202,6 +202,34 @@ static const u16 mt_i2c_regs_v2[] = { - [OFFSET_DCM_EN] = 0xf88, - }; - -+static const u16 mt_i2c_regs_v3[] = { -+ [OFFSET_DATA_PORT] = 0x0, -+ [OFFSET_INTR_MASK] = 0x8, -+ [OFFSET_INTR_STAT] = 0xc, -+ [OFFSET_CONTROL] = 0x10, -+ [OFFSET_TRANSFER_LEN] = 0x14, -+ [OFFSET_TRANSAC_LEN] = 0x18, -+ [OFFSET_DELAY_LEN] = 0x1c, -+ [OFFSET_TIMING] = 0x20, -+ [OFFSET_START] = 0x24, -+ [OFFSET_EXT_CONF] = 0x28, -+ [OFFSET_LTIMING] = 0x2c, -+ [OFFSET_HS] = 0x30, -+ [OFFSET_IO_CONFIG] = 0x34, -+ [OFFSET_FIFO_ADDR_CLR] = 0x38, -+ [OFFSET_SDA_TIMING] = 0x3c, -+ [OFFSET_TRANSFER_LEN_AUX] = 0x44, -+ [OFFSET_CLOCK_DIV] = 0x48, -+ [OFFSET_SOFTRESET] = 0x50, -+ [OFFSET_SCL_MIS_COMP_POINT] = 0x90, -+ [OFFSET_SLAVE_ADDR] = 0x94, -+ [OFFSET_DEBUGSTAT] = 0xe4, -+ [OFFSET_DEBUGCTRL] = 0xe8, -+ [OFFSET_FIFO_STAT] = 0xf4, -+ [OFFSET_FIFO_THRESH] = 0xf8, -+ [OFFSET_DCM_EN] = 0xf88, -+}; -+ - struct mtk_i2c_compatible { - const struct i2c_adapter_quirks *quirks; - const u16 *regs; -@@ -365,6 +393,18 @@ static const struct mtk_i2c_compatible m - .max_dma_support = 32, - }; - -+static const struct mtk_i2c_compatible mt7981_compat = { -+ .regs = mt_i2c_regs_v3, -+ .pmic_i2c = 0, -+ .dcm = 0, -+ .auto_restart = 1, -+ .aux_len_reg = 1, -+ .timing_adjust = 1, -+ .dma_sync = 1, -+ .ltiming_adjust = 1, -+ .max_dma_support = 33 -+}; -+ - static const struct mtk_i2c_compatible mt7986_compat = { - .quirks = &mt7622_i2c_quirks, - .regs = mt_i2c_regs_v1, -@@ -424,6 +464,7 @@ static const struct of_device_id mtk_i2c - { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, - { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, - { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, -+ { .compatible = "mediatek,mt7981-i2c", .data = &mt7981_compat }, - { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, - { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, - { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, diff --git a/target/linux/mediatek/patches-5.15/840-v5.16-i2c-mediatek-Reset-the-handshake-signal-between-i2c-.patch b/target/linux/mediatek/patches-5.15/840-v5.16-i2c-mediatek-Reset-the-handshake-signal-between-i2c-.patch new file mode 100644 index 00000000000..8b011964098 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/840-v5.16-i2c-mediatek-Reset-the-handshake-signal-between-i2c-.patch @@ -0,0 +1,74 @@ +From 95e4dfbf33dc0a0843ba20db811f7ea271235e1e Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Sun, 10 Oct 2021 15:05:12 +0800 +Subject: [PATCH 01/16] i2c: mediatek: Reset the handshake signal between i2c + and dma + +Due to changes in the hardware design of the handshaking signal +between i2c and dma, it is necessary to reset the handshaking +signal before each transfer to ensure that the multi-msgs can +be transferred correctly. + +Signed-off-by: Kewei Xu +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -49,6 +50,8 @@ + #define I2C_RD_TRANAC_VALUE 0x0001 + #define I2C_SCL_MIS_COMP_VALUE 0x0000 + #define I2C_CHN_CLR_FLAG 0x0000 ++#define I2C_RELIABILITY 0x0010 ++#define I2C_DMAACK_ENABLE 0x0008 + + #define I2C_DMA_CON_TX 0x0000 + #define I2C_DMA_CON_RX 0x0001 +@@ -851,6 +854,7 @@ static int mtk_i2c_do_transfer(struct mt + u16 restart_flag = 0; + u16 dma_sync = 0; + u32 reg_4g_mode; ++ u32 reg_dma_reset; + u8 *dma_rd_buf = NULL; + u8 *dma_wr_buf = NULL; + dma_addr_t rpaddr = 0; +@@ -864,6 +868,28 @@ static int mtk_i2c_do_transfer(struct mt + + reinit_completion(&i2c->msg_complete); + ++ if (i2c->dev_comp->apdma_sync && ++ i2c->op != I2C_MASTER_WRRD && num > 1) { ++ mtk_i2c_writew(i2c, 0x00, OFFSET_DEBUGCTRL); ++ writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_WARM_RST, ++ i2c->pdmabase + OFFSET_RST); ++ ++ ret = readw_poll_timeout(i2c->pdmabase + OFFSET_RST, ++ reg_dma_reset, ++ !(reg_dma_reset & I2C_DMA_WARM_RST), ++ 0, 100); ++ if (ret) { ++ dev_err(i2c->dev, "DMA warm reset timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST); ++ mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST, OFFSET_SOFTRESET); ++ mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET); ++ mtk_i2c_writew(i2c, I2C_RELIABILITY | I2C_DMAACK_ENABLE, ++ OFFSET_DEBUGCTRL); ++ } ++ + control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) & + ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); + if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1)) diff --git a/target/linux/mediatek/patches-5.15/841-v5.16-i2c-mediatek-Dump-i2c-dma-register-when-a-timeout-oc.patch b/target/linux/mediatek/patches-5.15/841-v5.16-i2c-mediatek-Dump-i2c-dma-register-when-a-timeout-oc.patch new file mode 100644 index 00000000000..a2d2521c772 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/841-v5.16-i2c-mediatek-Dump-i2c-dma-register-when-a-timeout-oc.patch @@ -0,0 +1,102 @@ +From 5b8e29e566e086ef9b5b9ea0d054370a295e1d05 Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Sun, 10 Oct 2021 15:05:13 +0800 +Subject: [PATCH 02/16] i2c: mediatek: Dump i2c/dma register when a timeout + occurs + +When a timeout error occurs in i2c transter, it is usually related +to the i2c/dma IP hardware configuration. Therefore, the purpose of +this patch is to dump the key register values of i2c/dma when a +timeout occurs in i2c for debugging. + +Signed-off-by: Kewei Xu +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 54 +++++++++++++++++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -130,6 +130,7 @@ enum I2C_REGS_OFFSET { + OFFSET_HS, + OFFSET_SOFTRESET, + OFFSET_DCM_EN, ++ OFFSET_MULTI_DMA, + OFFSET_PATH_DIR, + OFFSET_DEBUGSTAT, + OFFSET_DEBUGCTRL, +@@ -197,6 +198,7 @@ static const u16 mt_i2c_regs_v2[] = { + [OFFSET_TRANSFER_LEN_AUX] = 0x44, + [OFFSET_CLOCK_DIV] = 0x48, + [OFFSET_SOFTRESET] = 0x50, ++ [OFFSET_MULTI_DMA] = 0x8c, + [OFFSET_SCL_MIS_COMP_POINT] = 0x90, + [OFFSET_DEBUGSTAT] = 0xe4, + [OFFSET_DEBUGCTRL] = 0xe8, +@@ -845,6 +847,57 @@ static int mtk_i2c_set_speed(struct mtk_ + return 0; + } + ++static void i2c_dump_register(struct mtk_i2c *i2c) ++{ ++ dev_dbg(i2c->dev, "SLAVE_ADDR: 0x%x, INTR_MASK: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_SLAVE_ADDR), ++ mtk_i2c_readw(i2c, OFFSET_INTR_MASK)); ++ dev_dbg(i2c->dev, "INTR_STAT: 0x%x, CONTROL: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_INTR_STAT), ++ mtk_i2c_readw(i2c, OFFSET_CONTROL)); ++ dev_dbg(i2c->dev, "TRANSFER_LEN: 0x%x, TRANSAC_LEN: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN), ++ mtk_i2c_readw(i2c, OFFSET_TRANSAC_LEN)); ++ dev_dbg(i2c->dev, "DELAY_LEN: 0x%x, HTIMING: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_DELAY_LEN), ++ mtk_i2c_readw(i2c, OFFSET_TIMING)); ++ dev_dbg(i2c->dev, "START: 0x%x, EXT_CONF: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_START), ++ mtk_i2c_readw(i2c, OFFSET_EXT_CONF)); ++ dev_dbg(i2c->dev, "HS: 0x%x, IO_CONFIG: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_HS), ++ mtk_i2c_readw(i2c, OFFSET_IO_CONFIG)); ++ dev_dbg(i2c->dev, "DCM_EN: 0x%x, TRANSFER_LEN_AUX: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_DCM_EN), ++ mtk_i2c_readw(i2c, OFFSET_TRANSFER_LEN_AUX)); ++ dev_dbg(i2c->dev, "CLOCK_DIV: 0x%x, FIFO_STAT: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_CLOCK_DIV), ++ mtk_i2c_readw(i2c, OFFSET_FIFO_STAT)); ++ dev_dbg(i2c->dev, "DEBUGCTRL : 0x%x, DEBUGSTAT: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_DEBUGCTRL), ++ mtk_i2c_readw(i2c, OFFSET_DEBUGSTAT)); ++ if (i2c->dev_comp->regs == mt_i2c_regs_v2) { ++ dev_dbg(i2c->dev, "LTIMING: 0x%x, MULTI_DMA: 0x%x\n", ++ mtk_i2c_readw(i2c, OFFSET_LTIMING), ++ mtk_i2c_readw(i2c, OFFSET_MULTI_DMA)); ++ } ++ dev_dbg(i2c->dev, "\nDMA_INT_FLAG: 0x%x, DMA_INT_EN: 0x%x\n", ++ readl(i2c->pdmabase + OFFSET_INT_FLAG), ++ readl(i2c->pdmabase + OFFSET_INT_EN)); ++ dev_dbg(i2c->dev, "DMA_EN: 0x%x, DMA_CON: 0x%x\n", ++ readl(i2c->pdmabase + OFFSET_EN), ++ readl(i2c->pdmabase + OFFSET_CON)); ++ dev_dbg(i2c->dev, "DMA_TX_MEM_ADDR: 0x%x, DMA_RX_MEM_ADDR: 0x%x\n", ++ readl(i2c->pdmabase + OFFSET_TX_MEM_ADDR), ++ readl(i2c->pdmabase + OFFSET_RX_MEM_ADDR)); ++ dev_dbg(i2c->dev, "DMA_TX_LEN: 0x%x, DMA_RX_LEN: 0x%x\n", ++ readl(i2c->pdmabase + OFFSET_TX_LEN), ++ readl(i2c->pdmabase + OFFSET_RX_LEN)); ++ dev_dbg(i2c->dev, "DMA_TX_4G_MODE: 0x%x, DMA_RX_4G_MODE: 0x%x", ++ readl(i2c->pdmabase + OFFSET_TX_4G_MODE), ++ readl(i2c->pdmabase + OFFSET_RX_4G_MODE)); ++} ++ + static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, + int num, int left_num) + { +@@ -1075,6 +1128,7 @@ static int mtk_i2c_do_transfer(struct mt + + if (ret == 0) { + dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr); ++ i2c_dump_register(i2c); + mtk_i2c_init_hw(i2c); + return -ETIMEDOUT; + } diff --git a/target/linux/mediatek/patches-5.15/842-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8186.patch b/target/linux/mediatek/patches-5.15/842-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8186.patch new file mode 100644 index 00000000000..184fe94ff80 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/842-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8186.patch @@ -0,0 +1,45 @@ +From 83630e3c6147bf7bb18a18f3d5a99462464f450b Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Tue, 25 Jan 2022 19:04:13 +0800 +Subject: [PATCH 03/16] i2c: mediatek: Add i2c compatible for Mediatek MT8186 + +Add i2c compatible for MT8186. Compare to MT8192 i2c controller, +MT8186 doesn't need handshake signal witch apdma. + +Signed-off-by: Kewei Xu +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -397,6 +397,19 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 33, + }; + ++static const struct mtk_i2c_compatible mt8186_compat = { ++ .regs = mt_i2c_regs_v2, ++ .pmic_i2c = 0, ++ .dcm = 0, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .timing_adjust = 1, ++ .dma_sync = 0, ++ .ltiming_adjust = 1, ++ .apdma_sync = 0, ++ .max_dma_support = 36, ++}; ++ + static const struct mtk_i2c_compatible mt8192_compat = { + .quirks = &mt8183_i2c_quirks, + .regs = mt_i2c_regs_v2, +@@ -418,6 +431,7 @@ static const struct of_device_id mtk_i2c + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, ++ { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat }, + { .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat }, + {} + }; diff --git a/target/linux/mediatek/patches-5.15/843-v5.18-i2c-mediatek-modify-bus-speed-calculation-formula.patch b/target/linux/mediatek/patches-5.15/843-v5.18-i2c-mediatek-modify-bus-speed-calculation-formula.patch new file mode 100644 index 00000000000..0ace4a6701c --- /dev/null +++ b/target/linux/mediatek/patches-5.15/843-v5.18-i2c-mediatek-modify-bus-speed-calculation-formula.patch @@ -0,0 +1,132 @@ +From f606aab3f1a49d723d66e14e545f6ca45005bda6 Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Thu, 17 Feb 2022 20:22:43 +0800 +Subject: [PATCH 04/16] i2c: mediatek: modify bus speed calculation formula + +When clock-div is 0 or greater than 1, the bus speed +calculated by the old speed calculation formula will be +larger than the target speed. So we update the formula. + +Signed-off-by: Kewei Xu +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 51 ++++++++++++++++++++++++++------- + 1 file changed, 41 insertions(+), 10 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -67,11 +67,12 @@ + + #define MAX_SAMPLE_CNT_DIV 8 + #define MAX_STEP_CNT_DIV 64 +-#define MAX_CLOCK_DIV 256 ++#define MAX_CLOCK_DIV_8BITS 256 ++#define MAX_CLOCK_DIV_5BITS 32 + #define MAX_HS_STEP_CNT_DIV 8 +-#define I2C_STANDARD_MODE_BUFFER (1000 / 2) +-#define I2C_FAST_MODE_BUFFER (300 / 2) +-#define I2C_FAST_MODE_PLUS_BUFFER (20 / 2) ++#define I2C_STANDARD_MODE_BUFFER (1000 / 3) ++#define I2C_FAST_MODE_BUFFER (300 / 3) ++#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3) + + #define I2C_CONTROL_RS (0x1 << 1) + #define I2C_CONTROL_DMA_EN (0x1 << 2) +@@ -604,6 +605,31 @@ static int mtk_i2c_max_step_cnt(unsigned + return MAX_STEP_CNT_DIV; + } + ++static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c, ++ unsigned int sample_cnt) ++{ ++ int clk_div_restri = 0; ++ ++ if (i2c->dev_comp->ltiming_adjust == 0) ++ return 0; ++ ++ if (sample_cnt == 1) { ++ if (i2c->ac_timing.inter_clk_div == 0) ++ clk_div_restri = 0; ++ else ++ clk_div_restri = 1; ++ } else { ++ if (i2c->ac_timing.inter_clk_div == 0) ++ clk_div_restri = -1; ++ else if (i2c->ac_timing.inter_clk_div == 1) ++ clk_div_restri = 0; ++ else ++ clk_div_restri = 1; ++ } ++ ++ return clk_div_restri; ++} ++ + /* + * Check and Calculate i2c ac-timing + * +@@ -732,6 +758,7 @@ static int mtk_i2c_calculate_speed(struc + unsigned int best_mul; + unsigned int cnt_mul; + int ret = -EINVAL; ++ int clk_div_restri = 0; + + if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ) + target_speed = I2C_MAX_HIGH_SPEED_MODE_FREQ; +@@ -749,7 +776,8 @@ static int mtk_i2c_calculate_speed(struc + * optimizing for sample_cnt * step_cnt being minimal + */ + for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) { +- step_cnt = DIV_ROUND_UP(opt_div, sample_cnt); ++ clk_div_restri = mtk_i2c_get_clk_div_restri(i2c, sample_cnt); ++ step_cnt = DIV_ROUND_UP(opt_div + clk_div_restri, sample_cnt); + cnt_mul = step_cnt * sample_cnt; + if (step_cnt > max_step_cnt) + continue; +@@ -763,7 +791,7 @@ static int mtk_i2c_calculate_speed(struc + best_mul = cnt_mul; + base_sample_cnt = sample_cnt; + base_step_cnt = step_cnt; +- if (best_mul == opt_div) ++ if (best_mul == (opt_div + clk_div_restri)) + break; + } + } +@@ -774,7 +802,8 @@ static int mtk_i2c_calculate_speed(struc + sample_cnt = base_sample_cnt; + step_cnt = base_step_cnt; + +- if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) { ++ if ((clk_src / (2 * (sample_cnt * step_cnt - clk_div_restri))) > ++ target_speed) { + /* In this case, hardware can't support such + * low i2c_bus_freq + */ +@@ -803,13 +832,16 @@ static int mtk_i2c_set_speed(struct mtk_ + target_speed = i2c->speed_hz; + parent_clk /= i2c->clk_src_div; + +- if (i2c->dev_comp->timing_adjust) +- max_clk_div = MAX_CLOCK_DIV; ++ if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust) ++ max_clk_div = MAX_CLOCK_DIV_5BITS; ++ else if (i2c->dev_comp->timing_adjust) ++ max_clk_div = MAX_CLOCK_DIV_8BITS; + else + max_clk_div = 1; + + for (clk_div = 1; clk_div <= max_clk_div; clk_div++) { + clk_src = parent_clk / clk_div; ++ i2c->ac_timing.inter_clk_div = clk_div - 1; + + if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) { + /* Set master code speed register */ +@@ -856,7 +888,6 @@ static int mtk_i2c_set_speed(struct mtk_ + break; + } + +- i2c->ac_timing.inter_clk_div = clk_div - 1; + + return 0; + } diff --git a/target/linux/mediatek/patches-5.15/844-v5.18-i2c-mediatek-remove-redundant-null-check.patch b/target/linux/mediatek/patches-5.15/844-v5.18-i2c-mediatek-remove-redundant-null-check.patch new file mode 100644 index 00000000000..8f3c965a8d4 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/844-v5.18-i2c-mediatek-remove-redundant-null-check.patch @@ -0,0 +1,41 @@ +From eb4a2ae019815946f574cd9f8209e12bdcd8fd34 Mon Sep 17 00:00:00 2001 +From: Xu Wang +Date: Wed, 30 Sep 2020 08:42:33 +0000 +Subject: [PATCH 05/16] i2c: mediatek: remove redundant null check + +Because clk_disable_unprepare already checked NULL clock parameter, +so the additional checks are unnecessary, just remove it + +Signed-off-by: Xu Wang +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -476,8 +476,7 @@ static int mtk_i2c_clock_enable(struct m + return 0; + + err_arb: +- if (i2c->have_pmic) +- clk_disable_unprepare(i2c->clk_pmic); ++ clk_disable_unprepare(i2c->clk_pmic); + err_pmic: + clk_disable_unprepare(i2c->clk_main); + err_main: +@@ -488,11 +487,9 @@ err_main: + + static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) + { +- if (i2c->clk_arb) +- clk_disable_unprepare(i2c->clk_arb); ++ clk_disable_unprepare(i2c->clk_arb); + +- if (i2c->have_pmic) +- clk_disable_unprepare(i2c->clk_pmic); ++ clk_disable_unprepare(i2c->clk_pmic); + + clk_disable_unprepare(i2c->clk_main); + clk_disable_unprepare(i2c->clk_dma); diff --git a/target/linux/mediatek/patches-5.15/845-v5.18-i2c-mt65xx-Simplify-with-clk-bulk.patch b/target/linux/mediatek/patches-5.15/845-v5.18-i2c-mt65xx-Simplify-with-clk-bulk.patch new file mode 100644 index 00000000000..71d083f31a9 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/845-v5.18-i2c-mt65xx-Simplify-with-clk-bulk.patch @@ -0,0 +1,234 @@ +From cc6faa5e0772296d815fd298c231277d47308a6a Mon Sep 17 00:00:00 2001 +From: AngeloGioacchino Del Regno +Date: Thu, 3 Mar 2022 10:15:47 +0100 +Subject: [PATCH 06/16] i2c: mt65xx: Simplify with clk-bulk + +Since depending on the SoC or specific bus functionality some clocks +may be optional, we cannot get the benefit of using devm_clk_bulk_get() +but, by migrating to clk-bulk, we are able to remove the custom functions +mtk_i2c_clock_enable() and mtk_i2c_clock_disable(), increasing common +APIs usage, hence (lightly) decreasing kernel footprint. + +Signed-off-by: AngeloGioacchino Del Regno +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 124 +++++++++++++------------------- + 1 file changed, 51 insertions(+), 73 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -86,6 +86,27 @@ + + #define I2C_DRV_NAME "i2c-mt65xx" + ++/** ++ * enum i2c_mt65xx_clks - Clocks enumeration for MT65XX I2C ++ * ++ * @I2C_MT65XX_CLK_MAIN: main clock for i2c bus ++ * @I2C_MT65XX_CLK_DMA: DMA clock for i2c via DMA ++ * @I2C_MT65XX_CLK_PMIC: PMIC clock for i2c from PMIC ++ * @I2C_MT65XX_CLK_ARB: Arbitrator clock for i2c ++ * @I2C_MT65XX_CLK_MAX: Number of supported clocks ++ */ ++enum i2c_mt65xx_clks { ++ I2C_MT65XX_CLK_MAIN = 0, ++ I2C_MT65XX_CLK_DMA, ++ I2C_MT65XX_CLK_PMIC, ++ I2C_MT65XX_CLK_ARB, ++ I2C_MT65XX_CLK_MAX ++}; ++ ++static const char * const i2c_mt65xx_clk_ids[I2C_MT65XX_CLK_MAX] = { ++ "main", "dma", "pmic", "arb" ++}; ++ + enum DMA_REGS_OFFSET { + OFFSET_INT_FLAG = 0x0, + OFFSET_INT_EN = 0x04, +@@ -244,10 +265,7 @@ struct mtk_i2c { + /* set in i2c probe */ + void __iomem *base; /* i2c base addr */ + void __iomem *pdmabase; /* dma base address*/ +- struct clk *clk_main; /* main clock for i2c bus */ +- struct clk *clk_dma; /* DMA clock for i2c via DMA */ +- struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */ +- struct clk *clk_arb; /* Arbitrator clock for i2c */ ++ struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */ + bool have_pmic; /* can use i2c pins from PMIC */ + bool use_push_pull; /* IO config push-pull mode */ + +@@ -449,52 +467,6 @@ static void mtk_i2c_writew(struct mtk_i2 + writew(val, i2c->base + i2c->dev_comp->regs[reg]); + } + +-static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) +-{ +- int ret; +- +- ret = clk_prepare_enable(i2c->clk_dma); +- if (ret) +- return ret; +- +- ret = clk_prepare_enable(i2c->clk_main); +- if (ret) +- goto err_main; +- +- if (i2c->have_pmic) { +- ret = clk_prepare_enable(i2c->clk_pmic); +- if (ret) +- goto err_pmic; +- } +- +- if (i2c->clk_arb) { +- ret = clk_prepare_enable(i2c->clk_arb); +- if (ret) +- goto err_arb; +- } +- +- return 0; +- +-err_arb: +- clk_disable_unprepare(i2c->clk_pmic); +-err_pmic: +- clk_disable_unprepare(i2c->clk_main); +-err_main: +- clk_disable_unprepare(i2c->clk_dma); +- +- return ret; +-} +- +-static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) +-{ +- clk_disable_unprepare(i2c->clk_arb); +- +- clk_disable_unprepare(i2c->clk_pmic); +- +- clk_disable_unprepare(i2c->clk_main); +- clk_disable_unprepare(i2c->clk_dma); +-} +- + static void mtk_i2c_init_hw(struct mtk_i2c *i2c) + { + u16 control_reg; +@@ -1191,7 +1163,7 @@ static int mtk_i2c_transfer(struct i2c_a + int left_num = num; + struct mtk_i2c *i2c = i2c_get_adapdata(adap); + +- ret = mtk_i2c_clock_enable(i2c); ++ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) + return ret; + +@@ -1245,7 +1217,7 @@ static int mtk_i2c_transfer(struct i2c_a + ret = num; + + err_exit: +- mtk_i2c_clock_disable(i2c); ++ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); + return ret; + } + +@@ -1323,9 +1295,8 @@ static int mtk_i2c_probe(struct platform + { + int ret = 0; + struct mtk_i2c *i2c; +- struct clk *clk; + struct resource *res; +- int irq; ++ int i, irq, speed_clk; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) +@@ -1371,35 +1342,42 @@ static int mtk_i2c_probe(struct platform + if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c) + return -EINVAL; + +- i2c->clk_main = devm_clk_get(&pdev->dev, "main"); +- if (IS_ERR(i2c->clk_main)) { ++ /* Fill in clk-bulk IDs */ ++ for (i = 0; i < I2C_MT65XX_CLK_MAX; i++) ++ i2c->clocks[i].id = i2c_mt65xx_clk_ids[i]; ++ ++ /* Get clocks one by one, some may be optional */ ++ i2c->clocks[I2C_MT65XX_CLK_MAIN].clk = devm_clk_get(&pdev->dev, "main"); ++ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk)) { + dev_err(&pdev->dev, "cannot get main clock\n"); +- return PTR_ERR(i2c->clk_main); ++ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk); + } + +- i2c->clk_dma = devm_clk_get(&pdev->dev, "dma"); +- if (IS_ERR(i2c->clk_dma)) { ++ i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma"); ++ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) { + dev_err(&pdev->dev, "cannot get dma clock\n"); +- return PTR_ERR(i2c->clk_dma); ++ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk); + } + +- i2c->clk_arb = devm_clk_get(&pdev->dev, "arb"); +- if (IS_ERR(i2c->clk_arb)) +- i2c->clk_arb = NULL; ++ i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb"); ++ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk)) ++ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk); + +- clk = i2c->clk_main; + if (i2c->have_pmic) { +- i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic"); +- if (IS_ERR(i2c->clk_pmic)) { ++ i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get(&pdev->dev, "pmic"); ++ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) { + dev_err(&pdev->dev, "cannot get pmic clock\n"); +- return PTR_ERR(i2c->clk_pmic); ++ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk); + } +- clk = i2c->clk_pmic; ++ speed_clk = I2C_MT65XX_CLK_PMIC; ++ } else { ++ i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = NULL; ++ speed_clk = I2C_MT65XX_CLK_MAIN; + } + + strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); + +- ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk)); ++ ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); + if (ret) { + dev_err(&pdev->dev, "Failed to set the speed.\n"); + return -EINVAL; +@@ -1414,13 +1392,13 @@ static int mtk_i2c_probe(struct platform + } + } + +- ret = mtk_i2c_clock_enable(i2c); ++ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) { + dev_err(&pdev->dev, "clock enable failed!\n"); + return ret; + } + mtk_i2c_init_hw(i2c); +- mtk_i2c_clock_disable(i2c); ++ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); + + ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, + IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, +@@ -1465,7 +1443,7 @@ static int mtk_i2c_resume_noirq(struct d + int ret; + struct mtk_i2c *i2c = dev_get_drvdata(dev); + +- ret = mtk_i2c_clock_enable(i2c); ++ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) { + dev_err(dev, "clock enable failed!\n"); + return ret; +@@ -1473,7 +1451,7 @@ static int mtk_i2c_resume_noirq(struct d + + mtk_i2c_init_hw(i2c); + +- mtk_i2c_clock_disable(i2c); ++ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); + + i2c_mark_adapter_resumed(&i2c->adap); + diff --git a/target/linux/mediatek/patches-5.15/846-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8168.patch b/target/linux/mediatek/patches-5.15/846-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8168.patch new file mode 100644 index 00000000000..fe5be94fca4 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/846-v5.18-i2c-mediatek-Add-i2c-compatible-for-Mediatek-MT8168.patch @@ -0,0 +1,46 @@ +From de054c03f90b3ea22bc346fbf78ac716df192b2d Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Mon, 7 Mar 2022 11:36:49 +0800 +Subject: [PATCH 07/16] i2c: mediatek: Add i2c compatible for Mediatek MT8168 + +Add i2c compatible for MT8168. Compare to MT2712 i2c controller, +MT8168 need to synchronize signal with dma. + +Signed-off-by: Kewei Xu +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -389,6 +389,19 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 32, + }; + ++static const struct mtk_i2c_compatible mt8168_compat = { ++ .regs = mt_i2c_regs_v1, ++ .pmic_i2c = 0, ++ .dcm = 1, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .timing_adjust = 1, ++ .dma_sync = 1, ++ .ltiming_adjust = 0, ++ .apdma_sync = 0, ++ .max_dma_support = 33, ++}; ++ + static const struct mtk_i2c_compatible mt8173_compat = { + .regs = mt_i2c_regs_v1, + .pmic_i2c = 0, +@@ -448,6 +461,7 @@ static const struct of_device_id mtk_i2c + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, ++ { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, + { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat }, diff --git a/target/linux/mediatek/patches-5.15/847-v5.19-i2c-mediatek-Optimize-master_xfer-and-avoid-circular.patch b/target/linux/mediatek/patches-5.15/847-v5.19-i2c-mediatek-Optimize-master_xfer-and-avoid-circular.patch new file mode 100644 index 00000000000..5c4ce40765c --- /dev/null +++ b/target/linux/mediatek/patches-5.15/847-v5.19-i2c-mediatek-Optimize-master_xfer-and-avoid-circular.patch @@ -0,0 +1,101 @@ +From 2831f9a53ec3a16012d2d23590e3ebad6084b763 Mon Sep 17 00:00:00 2001 +From: AngeloGioacchino Del Regno +Date: Mon, 11 Apr 2022 15:21:07 +0200 +Subject: [PATCH 08/16] i2c: mediatek: Optimize master_xfer() and avoid + circular locking +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Especially (but not only) during probe, it may happen that multiple +devices are communicating via i2c (or multiple i2c busses) and +sometimes while others are probing asynchronously. +For example, a Cr50 TPM may be filling entropy (or userspace may be +reading random data) while the rt5682 (i2c) codec driver reads/sets +some registers, like while getting/setting a clock's rate, which +happens both during probe and during system operation. + +In this driver, the mtk_i2c_transfer() function (which is the i2c +.master_xfer() callback) was granularly managing the clocks by +performing a clk_bulk_prepare_enable() to start them and its inverse. +This is not only creating possible circular locking dependencies in +the some cases (like former explanation), but it's also suboptimal, +as clk_core prepare/unprepare operations are using mutex locking, +which creates a bit of unwanted overhead (for example, i2c trackpads +will call master_xfer() every few milliseconds!). + +With this commit, we avoid both the circular locking and additional +overhead by changing how we handle the clocks in this driver: +- Prepare the clocks during probe (and PM resume) +- Enable/disable clocks in mtk_i2c_transfer() +- Unprepare the clocks only for driver removal (and PM suspend) + +For the sake of providing a full explanation: during probe, the +clocks are not only prepared but also enabled, as this is needed +for some hardware initialization but, after that, we are disabling +but not unpreparing them, leaving an expected state for the +aforementioned clock handling strategy. + +Signed-off-by: AngeloGioacchino Del Regno +Tested-by: Nícolas F. R. A. Prado +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -1177,7 +1177,7 @@ static int mtk_i2c_transfer(struct i2c_a + int left_num = num; + struct mtk_i2c *i2c = i2c_get_adapdata(adap); + +- ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret) + return ret; + +@@ -1231,7 +1231,7 @@ static int mtk_i2c_transfer(struct i2c_a + ret = num; + + err_exit: +- clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + return ret; + } + +@@ -1412,7 +1412,7 @@ static int mtk_i2c_probe(struct platform + return ret; + } + mtk_i2c_init_hw(i2c); +- clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + + ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, + IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, +@@ -1439,6 +1439,8 @@ static int mtk_i2c_remove(struct platfor + + i2c_del_adapter(&i2c->adap); + ++ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ + return 0; + } + +@@ -1448,6 +1450,7 @@ static int mtk_i2c_suspend_noirq(struct + struct mtk_i2c *i2c = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&i2c->adap); ++ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); + + return 0; + } +@@ -1465,7 +1468,7 @@ static int mtk_i2c_resume_noirq(struct d + + mtk_i2c_init_hw(i2c); + +- clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + + i2c_mark_adapter_resumed(&i2c->adap); + diff --git a/target/linux/mediatek/patches-5.15/848-v5.19-i2c-mediatek-Fix-an-error-handling-path-in-mtk_i2c_p.patch b/target/linux/mediatek/patches-5.15/848-v5.19-i2c-mediatek-Fix-an-error-handling-path-in-mtk_i2c_p.patch new file mode 100644 index 00000000000..354f12e2146 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/848-v5.19-i2c-mediatek-Fix-an-error-handling-path-in-mtk_i2c_p.patch @@ -0,0 +1,48 @@ +From 6f3a5814c7aaea4176e0ac8b1ec6dc0a65aa2808 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sun, 22 May 2022 14:22:07 +0200 +Subject: [PATCH 09/16] i2c: mediatek: Fix an error handling path in + mtk_i2c_probe() + +The clsk are prepared, enabled, then disabled. So if an error occurs after +the disable step, they are still prepared. + +Add an error handling path to unprepare the clks in such a case, as already +done in the .remove function. + +Fixes: 8b4fc246c3ff ("i2c: mediatek: Optimize master_xfer() and avoid circular locking") +Signed-off-by: Christophe JAILLET +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -1420,17 +1420,22 @@ static int mtk_i2c_probe(struct platform + if (ret < 0) { + dev_err(&pdev->dev, + "Request I2C IRQ %d fail\n", irq); +- return ret; ++ goto err_bulk_unprepare; + } + + i2c_set_adapdata(&i2c->adap, i2c); + ret = i2c_add_adapter(&i2c->adap); + if (ret) +- return ret; ++ goto err_bulk_unprepare; + + platform_set_drvdata(pdev, i2c); + + return 0; ++ ++err_bulk_unprepare: ++ clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ++ ++ return ret; + } + + static int mtk_i2c_remove(struct platform_device *pdev) diff --git a/target/linux/mediatek/patches-5.15/849-v6.0-i2c-mediatek-add-i2c-compatible-for-MT8188.patch b/target/linux/mediatek/patches-5.15/849-v6.0-i2c-mediatek-add-i2c-compatible-for-MT8188.patch new file mode 100644 index 00000000000..744aa96ed0c --- /dev/null +++ b/target/linux/mediatek/patches-5.15/849-v6.0-i2c-mediatek-add-i2c-compatible-for-MT8188.patch @@ -0,0 +1,82 @@ +From 94c7f8af2c0a399c8aa66f2522b60c5784b5be6c Mon Sep 17 00:00:00 2001 +From: Kewei Xu +Date: Sat, 6 Aug 2022 18:02:49 +0800 +Subject: [PATCH 10/16] i2c: mediatek: add i2c compatible for MT8188 + +Add i2c compatible for MT8188 and added mt_i2c_regs_v3[], since +MT8188 i2c OFFSET_SLAVE_ADDR register changed from 0x04 to 0x94. + +Signed-off-by: Kewei Xu +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Qii Wang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 43 +++++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -229,6 +229,35 @@ static const u16 mt_i2c_regs_v2[] = { + [OFFSET_DCM_EN] = 0xf88, + }; + ++static const u16 mt_i2c_regs_v3[] = { ++ [OFFSET_DATA_PORT] = 0x0, ++ [OFFSET_INTR_MASK] = 0x8, ++ [OFFSET_INTR_STAT] = 0xc, ++ [OFFSET_CONTROL] = 0x10, ++ [OFFSET_TRANSFER_LEN] = 0x14, ++ [OFFSET_TRANSAC_LEN] = 0x18, ++ [OFFSET_DELAY_LEN] = 0x1c, ++ [OFFSET_TIMING] = 0x20, ++ [OFFSET_START] = 0x24, ++ [OFFSET_EXT_CONF] = 0x28, ++ [OFFSET_LTIMING] = 0x2c, ++ [OFFSET_HS] = 0x30, ++ [OFFSET_IO_CONFIG] = 0x34, ++ [OFFSET_FIFO_ADDR_CLR] = 0x38, ++ [OFFSET_SDA_TIMING] = 0x3c, ++ [OFFSET_TRANSFER_LEN_AUX] = 0x44, ++ [OFFSET_CLOCK_DIV] = 0x48, ++ [OFFSET_SOFTRESET] = 0x50, ++ [OFFSET_MULTI_DMA] = 0x8c, ++ [OFFSET_SCL_MIS_COMP_POINT] = 0x90, ++ [OFFSET_SLAVE_ADDR] = 0x94, ++ [OFFSET_DEBUGSTAT] = 0xe4, ++ [OFFSET_DEBUGCTRL] = 0xe8, ++ [OFFSET_FIFO_STAT] = 0xf4, ++ [OFFSET_FIFO_THRESH] = 0xf8, ++ [OFFSET_DCM_EN] = 0xf88, ++}; ++ + struct mtk_i2c_compatible { + const struct i2c_adapter_quirks *quirks; + const u16 *regs; +@@ -442,6 +471,19 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 36, + }; + ++static const struct mtk_i2c_compatible mt8188_compat = { ++ .regs = mt_i2c_regs_v3, ++ .pmic_i2c = 0, ++ .dcm = 0, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .timing_adjust = 1, ++ .dma_sync = 0, ++ .ltiming_adjust = 1, ++ .apdma_sync = 1, ++ .max_dma_support = 36, ++}; ++ + static const struct mtk_i2c_compatible mt8192_compat = { + .quirks = &mt8183_i2c_quirks, + .regs = mt_i2c_regs_v2, +@@ -465,6 +507,7 @@ static const struct of_device_id mtk_i2c + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, + { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat }, ++ { .compatible = "mediatek,mt8188-i2c", .data = &mt8188_compat }, + { .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat }, + {} + }; diff --git a/target/linux/mediatek/patches-5.15/850-v6.0-i2c-move-drivers-from-strlcpy-to-strscpy.patch b/target/linux/mediatek/patches-5.15/850-v6.0-i2c-move-drivers-from-strlcpy-to-strscpy.patch new file mode 100644 index 00000000000..506689965d3 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/850-v6.0-i2c-move-drivers-from-strlcpy-to-strscpy.patch @@ -0,0 +1,579 @@ +From 2f4ca256a98cc19787b7c861109dd1150a21b0bf Mon Sep 17 00:00:00 2001 +From: Wolfram Sang +Date: Thu, 11 Aug 2022 09:10:30 +0200 +Subject: [PATCH 11/16] i2c: move drivers from strlcpy to strscpy + +Follow the advice of the below link and prefer 'strscpy'. Conversion is +easy because no driver used the return value and has been done with a +simple sed invocation. + +Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ +Signed-off-by: Wolfram Sang +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-altera.c | 2 +- + drivers/i2c/busses/i2c-aspeed.c | 2 +- + drivers/i2c/busses/i2c-au1550.c | 2 +- + drivers/i2c/busses/i2c-axxia.c | 2 +- + drivers/i2c/busses/i2c-bcm-kona.c | 2 +- + drivers/i2c/busses/i2c-cbus-gpio.c | 2 +- + drivers/i2c/busses/i2c-cht-wc.c | 2 +- + drivers/i2c/busses/i2c-cros-ec-tunnel.c | 2 +- + drivers/i2c/busses/i2c-davinci.c | 2 +- + drivers/i2c/busses/i2c-digicolor.c | 2 +- + drivers/i2c/busses/i2c-eg20t.c | 2 +- + drivers/i2c/busses/i2c-emev2.c | 2 +- + drivers/i2c/busses/i2c-exynos5.c | 2 +- + drivers/i2c/busses/i2c-gpio.c | 2 +- + drivers/i2c/busses/i2c-highlander.c | 2 +- + drivers/i2c/busses/i2c-hix5hd2.c | 2 +- + drivers/i2c/busses/i2c-i801.c | 4 ++-- + drivers/i2c/busses/i2c-ibm_iic.c | 2 +- + drivers/i2c/busses/i2c-icy.c | 2 +- + drivers/i2c/busses/i2c-imx-lpi2c.c | 2 +- + drivers/i2c/busses/i2c-lpc2k.c | 2 +- + drivers/i2c/busses/i2c-meson.c | 2 +- + drivers/i2c/busses/i2c-mt65xx.c | 2 +- + drivers/i2c/busses/i2c-mt7621.c | 2 +- + drivers/i2c/busses/i2c-mv64xxx.c | 2 +- + drivers/i2c/busses/i2c-mxs.c | 2 +- + drivers/i2c/busses/i2c-nvidia-gpu.c | 2 +- + drivers/i2c/busses/i2c-omap.c | 2 +- + drivers/i2c/busses/i2c-opal.c | 4 ++-- + drivers/i2c/busses/i2c-parport.c | 2 +- + drivers/i2c/busses/i2c-pxa.c | 2 +- + drivers/i2c/busses/i2c-qcom-geni.c | 2 +- + drivers/i2c/busses/i2c-qup.c | 2 +- + drivers/i2c/busses/i2c-rcar.c | 2 +- + drivers/i2c/busses/i2c-riic.c | 2 +- + drivers/i2c/busses/i2c-rk3x.c | 2 +- + drivers/i2c/busses/i2c-s3c2410.c | 2 +- + drivers/i2c/busses/i2c-sh_mobile.c | 2 +- + drivers/i2c/busses/i2c-simtec.c | 2 +- + drivers/i2c/busses/i2c-taos-evm.c | 2 +- + drivers/i2c/busses/i2c-tegra-bpmp.c | 2 +- + drivers/i2c/busses/i2c-tegra.c | 2 +- + drivers/i2c/busses/i2c-uniphier-f.c | 2 +- + drivers/i2c/busses/i2c-uniphier.c | 2 +- + drivers/i2c/busses/i2c-versatile.c | 2 +- + drivers/i2c/busses/i2c-wmt.c | 2 +- + 46 files changed, 48 insertions(+), 48 deletions(-) + +--- a/drivers/i2c/busses/i2c-altera.c ++++ b/drivers/i2c/busses/i2c-altera.c +@@ -447,7 +447,7 @@ static int altr_i2c_probe(struct platfor + mutex_unlock(&idev->isr_mutex); + + i2c_set_adapdata(&idev->adapter, idev); +- strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); ++ strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); + idev->adapter.owner = THIS_MODULE; + idev->adapter.algo = &altr_i2c_algo; + idev->adapter.dev.parent = &pdev->dev; +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -1024,7 +1024,7 @@ static int aspeed_i2c_probe_bus(struct p + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; +- strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); ++ strscpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); + i2c_set_adapdata(&bus->adap, bus); + + bus->dev = &pdev->dev; +--- a/drivers/i2c/busses/i2c-au1550.c ++++ b/drivers/i2c/busses/i2c-au1550.c +@@ -321,7 +321,7 @@ i2c_au1550_probe(struct platform_device + priv->adap.algo = &au1550_algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; +- strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); ++ strscpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); + + /* Now, set up the PSC for SMBus PIO mode. */ + i2c_au1550_setup(priv); +--- a/drivers/i2c/busses/i2c-axxia.c ++++ b/drivers/i2c/busses/i2c-axxia.c +@@ -783,7 +783,7 @@ static int axxia_i2c_probe(struct platfo + } + + i2c_set_adapdata(&idev->adapter, idev); +- strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); ++ strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); + idev->adapter.owner = THIS_MODULE; + idev->adapter.algo = &axxia_i2c_algo; + idev->adapter.bus_recovery_info = &axxia_i2c_recovery_info; +--- a/drivers/i2c/busses/i2c-bcm-kona.c ++++ b/drivers/i2c/busses/i2c-bcm-kona.c +@@ -849,7 +849,7 @@ static int bcm_kona_i2c_probe(struct pla + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; +- strlcpy(adap->name, "Broadcom I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "Broadcom I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; +--- a/drivers/i2c/busses/i2c-cbus-gpio.c ++++ b/drivers/i2c/busses/i2c-cbus-gpio.c +@@ -245,7 +245,7 @@ static int cbus_i2c_probe(struct platfor + adapter->nr = pdev->id; + adapter->timeout = HZ; + adapter->algo = &cbus_i2c_algo; +- strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); ++ strscpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); + + spin_lock_init(&chost->lock); + chost->dev = &pdev->dev; +--- a/drivers/i2c/busses/i2c-cht-wc.c ++++ b/drivers/i2c/busses/i2c-cht-wc.c +@@ -334,7 +334,7 @@ static int cht_wc_i2c_adap_i2c_probe(str + adap->adapter.class = I2C_CLASS_HWMON; + adap->adapter.algo = &cht_wc_i2c_adap_algo; + adap->adapter.lock_ops = &cht_wc_i2c_adap_lock_ops; +- strlcpy(adap->adapter.name, "PMIC I2C Adapter", ++ strscpy(adap->adapter.name, "PMIC I2C Adapter", + sizeof(adap->adapter.name)); + adap->adapter.dev.parent = &pdev->dev; + +--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c ++++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c +@@ -267,7 +267,7 @@ static int ec_i2c_probe(struct platform_ + bus->dev = dev; + + bus->adap.owner = THIS_MODULE; +- strlcpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name)); ++ strscpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name)); + bus->adap.algo = &ec_i2c_algorithm; + bus->adap.algo_data = bus; + bus->adap.dev.parent = &pdev->dev; +--- a/drivers/i2c/busses/i2c-davinci.c ++++ b/drivers/i2c/busses/i2c-davinci.c +@@ -847,7 +847,7 @@ static int davinci_i2c_probe(struct plat + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; +- strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name)); + adap->algo = &i2c_davinci_algo; + adap->dev.parent = &pdev->dev; + adap->timeout = DAVINCI_I2C_TIMEOUT; +--- a/drivers/i2c/busses/i2c-digicolor.c ++++ b/drivers/i2c/busses/i2c-digicolor.c +@@ -322,7 +322,7 @@ static int dc_i2c_probe(struct platform_ + if (ret < 0) + return ret; + +- strlcpy(i2c->adap.name, "Conexant Digicolor I2C adapter", ++ strscpy(i2c->adap.name, "Conexant Digicolor I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &dc_i2c_algorithm; +--- a/drivers/i2c/busses/i2c-eg20t.c ++++ b/drivers/i2c/busses/i2c-eg20t.c +@@ -773,7 +773,7 @@ static int pch_i2c_probe(struct pci_dev + + pch_adap->owner = THIS_MODULE; + pch_adap->class = I2C_CLASS_HWMON; +- strlcpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name)); ++ strscpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name)); + pch_adap->algo = &pch_algorithm; + pch_adap->algo_data = &adap_info->pch_data[i]; + +--- a/drivers/i2c/busses/i2c-emev2.c ++++ b/drivers/i2c/busses/i2c-emev2.c +@@ -371,7 +371,7 @@ static int em_i2c_probe(struct platform_ + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + +- strlcpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name)); ++ strscpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name)); + + priv->sclk = devm_clk_get(&pdev->dev, "sclk"); + if (IS_ERR(priv->sclk)) +--- a/drivers/i2c/busses/i2c-exynos5.c ++++ b/drivers/i2c/busses/i2c-exynos5.c +@@ -751,7 +751,7 @@ static int exynos5_i2c_probe(struct plat + if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock)) + i2c->op_clock = I2C_MAX_STANDARD_MODE_FREQ; + +- strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &exynos5_i2c_algorithm; + i2c->adap.retries = 3; +--- a/drivers/i2c/busses/i2c-gpio.c ++++ b/drivers/i2c/busses/i2c-gpio.c +@@ -436,7 +436,7 @@ static int i2c_gpio_probe(struct platfor + + adap->owner = THIS_MODULE; + if (np) +- strlcpy(adap->name, dev_name(dev), sizeof(adap->name)); ++ strscpy(adap->name, dev_name(dev), sizeof(adap->name)); + else + snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); + +--- a/drivers/i2c/busses/i2c-highlander.c ++++ b/drivers/i2c/busses/i2c-highlander.c +@@ -402,7 +402,7 @@ static int highlander_i2c_probe(struct p + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; +- strlcpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name)); + adap->algo = &highlander_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; +--- a/drivers/i2c/busses/i2c-hix5hd2.c ++++ b/drivers/i2c/busses/i2c-hix5hd2.c +@@ -423,7 +423,7 @@ static int hix5hd2_i2c_probe(struct plat + } + clk_prepare_enable(priv->clk); + +- strlcpy(priv->adap.name, "hix5hd2-i2c", sizeof(priv->adap.name)); ++ strscpy(priv->adap.name, "hix5hd2-i2c", sizeof(priv->adap.name)); + priv->dev = &pdev->dev; + priv->adap.owner = THIS_MODULE; + priv->adap.algo = &hix5hd2_i2c_algorithm; +--- a/drivers/i2c/busses/i2c-i801.c ++++ b/drivers/i2c/busses/i2c-i801.c +@@ -1111,7 +1111,7 @@ static void dmi_check_onboard_device(u8 + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = dmi_devices[i].i2c_addr; +- strlcpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE); ++ strscpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE); + i2c_new_client_device(adap, &info); + break; + } +@@ -1267,7 +1267,7 @@ static void register_dell_lis3lv02d_i2c_ + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = dell_lis3lv02d_devices[i].i2c_addr; +- strlcpy(info.type, "lis3lv02d", I2C_NAME_SIZE); ++ strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE); + i2c_new_client_device(&priv->adapter, &info); + } + +--- a/drivers/i2c/busses/i2c-ibm_iic.c ++++ b/drivers/i2c/busses/i2c-ibm_iic.c +@@ -738,7 +738,7 @@ static int iic_probe(struct platform_dev + adap = &dev->adap; + adap->dev.parent = &ofdev->dev; + adap->dev.of_node = of_node_get(np); +- strlcpy(adap->name, "IBM IIC", sizeof(adap->name)); ++ strscpy(adap->name, "IBM IIC", sizeof(adap->name)); + i2c_set_adapdata(adap, dev); + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->algo = &iic_algo; +--- a/drivers/i2c/busses/i2c-icy.c ++++ b/drivers/i2c/busses/i2c-icy.c +@@ -141,7 +141,7 @@ static int icy_probe(struct zorro_dev *z + i2c->adapter.owner = THIS_MODULE; + /* i2c->adapter.algo assigned by i2c_pcf_add_bus() */ + i2c->adapter.algo_data = algo_data; +- strlcpy(i2c->adapter.name, "ICY I2C Zorro adapter", ++ strscpy(i2c->adapter.name, "ICY I2C Zorro adapter", + sizeof(i2c->adapter.name)); + + if (!devm_request_mem_region(&z->dev, +--- a/drivers/i2c/busses/i2c-imx-lpi2c.c ++++ b/drivers/i2c/busses/i2c-imx-lpi2c.c +@@ -564,7 +564,7 @@ static int lpi2c_imx_probe(struct platfo + lpi2c_imx->adapter.algo = &lpi2c_imx_algo; + lpi2c_imx->adapter.dev.parent = &pdev->dev; + lpi2c_imx->adapter.dev.of_node = pdev->dev.of_node; +- strlcpy(lpi2c_imx->adapter.name, pdev->name, ++ strscpy(lpi2c_imx->adapter.name, pdev->name, + sizeof(lpi2c_imx->adapter.name)); + + lpi2c_imx->clk = devm_clk_get(&pdev->dev, NULL); +--- a/drivers/i2c/busses/i2c-lpc2k.c ++++ b/drivers/i2c/busses/i2c-lpc2k.c +@@ -417,7 +417,7 @@ static int i2c_lpc2k_probe(struct platfo + + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.owner = THIS_MODULE; +- strlcpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name)); + i2c->adap.algo = &i2c_lpc2k_algorithm; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; +--- a/drivers/i2c/busses/i2c-meson.c ++++ b/drivers/i2c/busses/i2c-meson.c +@@ -451,7 +451,7 @@ static int meson_i2c_probe(struct platfo + return ret; + } + +- strlcpy(i2c->adap.name, "Meson I2C adapter", ++ strscpy(i2c->adap.name, "Meson I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &meson_i2c_algorithm; +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -1432,7 +1432,7 @@ static int mtk_i2c_probe(struct platform + speed_clk = I2C_MT65XX_CLK_MAIN; + } + +- strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); + + ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); + if (ret) { +--- a/drivers/i2c/busses/i2c-mt7621.c ++++ b/drivers/i2c/busses/i2c-mt7621.c +@@ -315,7 +315,7 @@ static int mtk_i2c_probe(struct platform + adap->dev.parent = &pdev->dev; + i2c_set_adapdata(adap, i2c); + adap->dev.of_node = pdev->dev.of_node; +- strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ strscpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); + + platform_set_drvdata(pdev, i2c); + +--- a/drivers/i2c/busses/i2c-mv64xxx.c ++++ b/drivers/i2c/busses/i2c-mv64xxx.c +@@ -989,7 +989,7 @@ mv64xxx_i2c_probe(struct platform_device + if (IS_ERR(drv_data->reg_base)) + return PTR_ERR(drv_data->reg_base); + +- strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", ++ strscpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", + sizeof(drv_data->adapter.name)); + + init_waitqueue_head(&drv_data->waitq); +--- a/drivers/i2c/busses/i2c-mxs.c ++++ b/drivers/i2c/busses/i2c-mxs.c +@@ -838,7 +838,7 @@ static int mxs_i2c_probe(struct platform + return err; + + adap = &i2c->adapter; +- strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &mxs_i2c_algo; + adap->quirks = &mxs_i2c_quirks; +--- a/drivers/i2c/busses/i2c-nvidia-gpu.c ++++ b/drivers/i2c/busses/i2c-nvidia-gpu.c +@@ -319,7 +319,7 @@ static int gpu_i2c_probe(struct pci_dev + + i2c_set_adapdata(&i2cd->adapter, i2cd); + i2cd->adapter.owner = THIS_MODULE; +- strlcpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter", ++ strscpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter", + sizeof(i2cd->adapter.name)); + i2cd->adapter.algo = &gpu_i2c_algorithm; + i2cd->adapter.quirks = &gpu_i2c_quirks; +--- a/drivers/i2c/busses/i2c-omap.c ++++ b/drivers/i2c/busses/i2c-omap.c +@@ -1488,7 +1488,7 @@ omap_i2c_probe(struct platform_device *p + i2c_set_adapdata(adap, omap); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; +- strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + adap->algo = &omap_i2c_algo; + adap->quirks = &omap_i2c_quirks; + adap->dev.parent = &pdev->dev; +--- a/drivers/i2c/busses/i2c-opal.c ++++ b/drivers/i2c/busses/i2c-opal.c +@@ -220,9 +220,9 @@ static int i2c_opal_probe(struct platfor + adapter->dev.of_node = of_node_get(pdev->dev.of_node); + pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL); + if (pname) +- strlcpy(adapter->name, pname, sizeof(adapter->name)); ++ strscpy(adapter->name, pname, sizeof(adapter->name)); + else +- strlcpy(adapter->name, "opal", sizeof(adapter->name)); ++ strscpy(adapter->name, "opal", sizeof(adapter->name)); + + platform_set_drvdata(pdev, adapter); + rc = i2c_add_adapter(adapter); +--- a/drivers/i2c/busses/i2c-parport.c ++++ b/drivers/i2c/busses/i2c-parport.c +@@ -308,7 +308,7 @@ static void i2c_parport_attach(struct pa + /* Fill the rest of the structure */ + adapter->adapter.owner = THIS_MODULE; + adapter->adapter.class = I2C_CLASS_HWMON; +- strlcpy(adapter->adapter.name, "Parallel port adapter", ++ strscpy(adapter->adapter.name, "Parallel port adapter", + sizeof(adapter->adapter.name)); + adapter->algo_data = parport_algo_data; + /* Slow down if we can't sense SCL */ +--- a/drivers/i2c/busses/i2c-pxa.c ++++ b/drivers/i2c/busses/i2c-pxa.c +@@ -1403,7 +1403,7 @@ static int i2c_pxa_probe(struct platform + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + +- strlcpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name)); + + i2c->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(i2c->clk)) { +--- a/drivers/i2c/busses/i2c-qcom-geni.c ++++ b/drivers/i2c/busses/i2c-qcom-geni.c +@@ -577,7 +577,7 @@ static int geni_i2c_probe(struct platfor + i2c_set_adapdata(&gi2c->adap, gi2c); + gi2c->adap.dev.parent = dev; + gi2c->adap.dev.of_node = dev->of_node; +- strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name)); ++ strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name)); + + ret = geni_icc_get(&gi2c->se, "qup-memory"); + if (ret) +--- a/drivers/i2c/busses/i2c-qup.c ++++ b/drivers/i2c/busses/i2c-qup.c +@@ -1878,7 +1878,7 @@ nodma: + qup->adap.dev.of_node = pdev->dev.of_node; + qup->is_last = true; + +- strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name)); ++ strscpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name)); + + pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(qup->dev); +--- a/drivers/i2c/busses/i2c-rcar.c ++++ b/drivers/i2c/busses/i2c-rcar.c +@@ -1059,7 +1059,7 @@ static int rcar_i2c_probe(struct platfor + adap->bus_recovery_info = &rcar_i2c_bri; + adap->quirks = &rcar_i2c_quirks; + i2c_set_adapdata(adap, priv); +- strlcpy(adap->name, pdev->name, sizeof(adap->name)); ++ strscpy(adap->name, pdev->name, sizeof(adap->name)); + + /* Init DMA */ + sg_init_table(&priv->sg, 1); +--- a/drivers/i2c/busses/i2c-riic.c ++++ b/drivers/i2c/busses/i2c-riic.c +@@ -447,7 +447,7 @@ static int riic_i2c_probe(struct platfor + + adap = &riic->adapter; + i2c_set_adapdata(adap, riic); +- strlcpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name)); ++ strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &riic_algo; + adap->dev.parent = &pdev->dev; +--- a/drivers/i2c/busses/i2c-rk3x.c ++++ b/drivers/i2c/busses/i2c-rk3x.c +@@ -1240,7 +1240,7 @@ static int rk3x_i2c_probe(struct platfor + /* use common interface to get I2C timing properties */ + i2c_parse_fw_timings(&pdev->dev, &i2c->t, true); + +- strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &rk3x_i2c_algorithm; + i2c->adap.retries = 3; +--- a/drivers/i2c/busses/i2c-s3c2410.c ++++ b/drivers/i2c/busses/i2c-s3c2410.c +@@ -1076,7 +1076,7 @@ static int s3c24xx_i2c_probe(struct plat + else + s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c); + +- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &s3c24xx_i2c_algorithm; + i2c->adap.retries = 2; +--- a/drivers/i2c/busses/i2c-sh_mobile.c ++++ b/drivers/i2c/busses/i2c-sh_mobile.c +@@ -930,7 +930,7 @@ static int sh_mobile_i2c_probe(struct pl + adap->nr = dev->id; + adap->dev.of_node = dev->dev.of_node; + +- strlcpy(adap->name, dev->name, sizeof(adap->name)); ++ strscpy(adap->name, dev->name, sizeof(adap->name)); + + spin_lock_init(&pd->lock); + init_waitqueue_head(&pd->wait); +--- a/drivers/i2c/busses/i2c-simtec.c ++++ b/drivers/i2c/busses/i2c-simtec.c +@@ -99,7 +99,7 @@ static int simtec_i2c_probe(struct platf + pd->adap.algo_data = &pd->bit; + pd->adap.dev.parent = &dev->dev; + +- strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); ++ strscpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); + + pd->bit.data = pd; + pd->bit.setsda = simtec_i2c_setsda; +--- a/drivers/i2c/busses/i2c-taos-evm.c ++++ b/drivers/i2c/busses/i2c-taos-evm.c +@@ -239,7 +239,7 @@ static int taos_connect(struct serio *se + dev_err(&serio->dev, "TAOS EVM identification failed\n"); + goto exit_close; + } +- strlcpy(adapter->name, name, sizeof(adapter->name)); ++ strscpy(adapter->name, name, sizeof(adapter->name)); + + /* Turn echo off for better performance */ + taos->state = TAOS_STATE_EOFF; +--- a/drivers/i2c/busses/i2c-tegra-bpmp.c ++++ b/drivers/i2c/busses/i2c-tegra-bpmp.c +@@ -305,7 +305,7 @@ static int tegra_bpmp_i2c_probe(struct p + + i2c_set_adapdata(&i2c->adapter, i2c); + i2c->adapter.owner = THIS_MODULE; +- strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter", ++ strscpy(i2c->adapter.name, "Tegra BPMP I2C adapter", + sizeof(i2c->adapter.name)); + i2c->adapter.algo = &tegra_bpmp_i2c_algo; + i2c->adapter.dev.parent = &pdev->dev; +--- a/drivers/i2c/busses/i2c-tegra.c ++++ b/drivers/i2c/busses/i2c-tegra.c +@@ -1799,7 +1799,7 @@ static int tegra_i2c_probe(struct platfo + if (i2c_dev->hw->supports_bus_clear) + i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + +- strlcpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev), ++ strscpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev), + sizeof(i2c_dev->adapter.name)); + + err = i2c_add_numbered_adapter(&i2c_dev->adapter); +--- a/drivers/i2c/busses/i2c-uniphier-f.c ++++ b/drivers/i2c/busses/i2c-uniphier-f.c +@@ -564,7 +564,7 @@ static int uniphier_fi2c_probe(struct pl + priv->adap.algo = &uniphier_fi2c_algo; + priv->adap.dev.parent = dev; + priv->adap.dev.of_node = dev->of_node; +- strlcpy(priv->adap.name, "UniPhier FI2C", sizeof(priv->adap.name)); ++ strscpy(priv->adap.name, "UniPhier FI2C", sizeof(priv->adap.name)); + priv->adap.bus_recovery_info = &uniphier_fi2c_bus_recovery_info; + i2c_set_adapdata(&priv->adap, priv); + platform_set_drvdata(pdev, priv); +--- a/drivers/i2c/busses/i2c-uniphier.c ++++ b/drivers/i2c/busses/i2c-uniphier.c +@@ -358,7 +358,7 @@ static int uniphier_i2c_probe(struct pla + priv->adap.algo = &uniphier_i2c_algo; + priv->adap.dev.parent = dev; + priv->adap.dev.of_node = dev->of_node; +- strlcpy(priv->adap.name, "UniPhier I2C", sizeof(priv->adap.name)); ++ strscpy(priv->adap.name, "UniPhier I2C", sizeof(priv->adap.name)); + priv->adap.bus_recovery_info = &uniphier_i2c_bus_recovery_info; + i2c_set_adapdata(&priv->adap, priv); + platform_set_drvdata(pdev, priv); +--- a/drivers/i2c/busses/i2c-versatile.c ++++ b/drivers/i2c/busses/i2c-versatile.c +@@ -79,7 +79,7 @@ static int i2c_versatile_probe(struct pl + writel(SCL | SDA, i2c->base + I2C_CONTROLS); + + i2c->adap.owner = THIS_MODULE; +- strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); ++ strscpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); + i2c->adap.algo_data = &i2c->algo; + i2c->adap.dev.parent = &dev->dev; + i2c->adap.dev.of_node = dev->dev.of_node; +--- a/drivers/i2c/busses/i2c-wmt.c ++++ b/drivers/i2c/busses/i2c-wmt.c +@@ -413,7 +413,7 @@ static int wmt_i2c_probe(struct platform + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); +- strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name)); ++ strscpy(adap->name, "WMT I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &wmt_i2c_algo; + adap->dev.parent = &pdev->dev; diff --git a/target/linux/mediatek/patches-5.15/803-i2c-busses-add-mt7986-support.patch b/target/linux/mediatek/patches-5.15/851-v6.2-i2c-mediatek-add-mt7986-support.patch similarity index 54% rename from target/linux/mediatek/patches-5.15/803-i2c-busses-add-mt7986-support.patch rename to target/linux/mediatek/patches-5.15/851-v6.2-i2c-mediatek-add-mt7986-support.patch index 1860a6da017..4c398c59f93 100644 --- a/target/linux/mediatek/patches-5.15/803-i2c-busses-add-mt7986-support.patch +++ b/target/linux/mediatek/patches-5.15/851-v6.2-i2c-mediatek-add-mt7986-support.patch @@ -1,7 +1,21 @@ +From 11f9a0f4e51887ad7b4a2898a368fcd0c2984e89 Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Sun, 9 Oct 2022 12:16:31 +0200 +Subject: [PATCH 12/16] i2c: mediatek: add mt7986 support + +Add i2c support for MT7986 SoC. + +Signed-off-by: Frank Wunderlich +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c -@@ -365,6 +365,19 @@ static const struct mtk_i2c_compatible m - .max_dma_support = 32, +@@ -431,6 +431,19 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 33, }; +static const struct mtk_i2c_compatible mt7986_compat = { @@ -11,20 +25,20 @@ + .dcm = 1, + .auto_restart = 1, + .aux_len_reg = 1, -+ .max_dma_support = 32, + .timing_adjust = 0, + .dma_sync = 1, + .ltiming_adjust = 0, ++ .max_dma_support = 32, +}; + static const struct mtk_i2c_compatible mt8173_compat = { .regs = mt_i2c_regs_v1, .pmic_i2c = 0, -@@ -411,6 +424,7 @@ static const struct of_device_id mtk_i2c +@@ -503,6 +516,7 @@ static const struct of_device_id mtk_i2c { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, + { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, + { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, - { .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat }, diff --git a/target/linux/mediatek/patches-5.15/852-v6.3-i2c-mt65xx-Use-devm_platform_get_and_ioremap_resourc.patch b/target/linux/mediatek/patches-5.15/852-v6.3-i2c-mt65xx-Use-devm_platform_get_and_ioremap_resourc.patch new file mode 100644 index 00000000000..18c66cdac52 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/852-v6.3-i2c-mt65xx-Use-devm_platform_get_and_ioremap_resourc.patch @@ -0,0 +1,42 @@ +From 98204ccafd45a8a6109ff2d60e2c179b95d92578 Mon Sep 17 00:00:00 2001 +From: ye xingchen +Date: Thu, 19 Jan 2023 17:19:58 +0800 +Subject: [PATCH 13/16] i2c: mt65xx: Use + devm_platform_get_and_ioremap_resource() + +Convert platform_get_resource(), devm_ioremap_resource() to a single +call to devm_platform_get_and_ioremap_resource(), as this is exactly +what this function does. + +Signed-off-by: ye xingchen +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -1366,20 +1366,17 @@ static int mtk_i2c_probe(struct platform + { + int ret = 0; + struct mtk_i2c *i2c; +- struct resource *res; + int i, irq, speed_clk; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- i2c->base = devm_ioremap_resource(&pdev->dev, res); ++ i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res); ++ i2c->pdmabase = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); + if (IS_ERR(i2c->pdmabase)) + return PTR_ERR(i2c->pdmabase); + diff --git a/target/linux/mediatek/patches-5.15/853-v6.3-i2c-mt65xx-drop-of_match_ptr-for-ID-table.patch b/target/linux/mediatek/patches-5.15/853-v6.3-i2c-mt65xx-drop-of_match_ptr-for-ID-table.patch new file mode 100644 index 00000000000..d000d535226 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/853-v6.3-i2c-mt65xx-drop-of_match_ptr-for-ID-table.patch @@ -0,0 +1,33 @@ +From 8106fa2e0ae6082833fe1df97829c46c0183eaea Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Sat, 11 Mar 2023 12:16:54 +0100 +Subject: [PATCH 14/16] i2c: mt65xx: drop of_match_ptr for ID table +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The driver can match only via the DT table so the table should be always +used and the of_match_ptr does not have any sense (this also allows ACPI +matching via PRP0001, even though it might not be relevant here). + + drivers/i2c/busses/i2c-mt65xx.c:514:34: error: ‘mtk_i2c_of_match’ defined but not used [-Werror=unused-const-variable=] + +Signed-off-by: Krzysztof Kozlowski +Reviewed-by: Guenter Roeck +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -1546,7 +1546,7 @@ static struct platform_driver mtk_i2c_dr + .driver = { + .name = I2C_DRV_NAME, + .pm = &mtk_i2c_pm, +- .of_match_table = of_match_ptr(mtk_i2c_of_match), ++ .of_match_table = mtk_i2c_of_match, + }, + }; + diff --git a/target/linux/mediatek/patches-5.15/854-v6.4-i2c-mediatek-add-support-for-MT7981-SoC.patch b/target/linux/mediatek/patches-5.15/854-v6.4-i2c-mediatek-add-support-for-MT7981-SoC.patch new file mode 100644 index 00000000000..e0973741e29 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/854-v6.4-i2c-mediatek-add-support-for-MT7981-SoC.patch @@ -0,0 +1,47 @@ +From f69f3d662ba3bf999c36d9ac1e684540c4487bc3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 10 Apr 2023 17:19:38 +0100 +Subject: [PATCH 15/16] i2c: mediatek: add support for MT7981 SoC + +Add support for the I2C units found in the MediaTek MT7981 and MT7988 +SoCs. Just like other recent MediaTek I2C units that also uses v3 +register offsets (which differ from v2 only by OFFSET_SLAVE_ADDR being +0x94 instead of 0x4). + +Signed-off-by: Daniel Golle +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Alexandre Mergnat +Signed-off-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-mt65xx.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -431,6 +431,18 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 33, + }; + ++static const struct mtk_i2c_compatible mt7981_compat = { ++ .regs = mt_i2c_regs_v3, ++ .pmic_i2c = 0, ++ .dcm = 0, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .timing_adjust = 1, ++ .dma_sync = 1, ++ .ltiming_adjust = 1, ++ .max_dma_support = 33 ++}; ++ + static const struct mtk_i2c_compatible mt7986_compat = { + .quirks = &mt7622_i2c_quirks, + .regs = mt_i2c_regs_v1, +@@ -516,6 +528,7 @@ static const struct of_device_id mtk_i2c + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, ++ { .compatible = "mediatek,mt7981-i2c", .data = &mt7981_compat }, + { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, + { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, diff --git a/target/linux/mvebu/cortexa9/base-files/etc/uci-defaults/35_turris-omnia_uboot-env b/target/linux/mvebu/cortexa9/base-files/etc/uci-defaults/35_turris-omnia_uboot-env deleted file mode 100644 index da025a200f1..00000000000 --- a/target/linux/mvebu/cortexa9/base-files/etc/uci-defaults/35_turris-omnia_uboot-env +++ /dev/null @@ -1,44 +0,0 @@ -# This must be sourced after 30_uboot-envtools - -. /lib/functions.sh - -board=$(board_name) - -case "$board" in -cznic,turris-omnia) - # Do nothing if this is not the old U-Boot - grep -q 'U-Boot 2015.10-rc2' /dev/mtd0 || exit 0 - # Do nothing if we already have distro_bootcmd - fw_printenv distro_bootcmd >/dev/null 2>/dev/null && exit 0 - # Set the complete environment, since U-Boot does not merge the default environment from its own image! - fw_setenv -s - <<-"EOF" - baudrate 115200 - bootdelay 3 - ethact neta2 - fdt_high 0x10000000 - initrd_high 0x10000000 - bootargs earlyprintk console=ttyS0,115200 rootfstype=btrfs rootdelay=2 root=b301 rootflags=subvol=@,commit=5 rw - bootcmd i2c dev 1; i2c read 0x2a 0x9 1 0x00FFFFF0; setexpr.b rescue *0x00FFFFF0; if test $rescue -ge 1; then echo BOOT RESCUE; run rescueboot; else echo BOOT eMMC FS; run mmcboot; setenv bootargs; run distro_bootcmd; fi - rescueboot i2c mw 0x2a.1 0x3 0x1c 1; i2c mw 0x2a.1 0x4 0x1c 1; mw.l 0x01000000 0x00ff000c; i2c write 0x01000000 0x2a.1 0x5 4 -s; setenv bootargs "$bootargs omniarescue=$rescue"; sf probe; sf read 0x1000000 0x100000 0x700000; bootz 0x1000000 - mmcboot btrload mmc 0 ${kernel_addr_r} boot/zImage @ && btrload mmc 0 ${fdt_addr_r} boot/dtb @ && setenv bootargs ${bootargs} cfg80211.freg=${regdomain} && bootz ${kernel_addr_r} - ${fdt_addr_r} - kernel_addr_r 0x1000000 - scriptaddr 0x1800000 - fdt_addr_r 0x2000000 - boot_targets mmc0 scsi0 - boot_prefixes / /boot/ - boot_scripts boot.scr - distro_bootcmd scsi_need_init=true; for target in ${boot_targets}; do run bootcmd_${target}; done - bootcmd_mmc0 devnum=0; run mmc_boot - mmc_boot if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi - bootcmd_scsi0 devnum=0; run scsi_boot - scsi_boot run scsi_init; if scsi dev ${devnum}; then devtype=scsi; run scan_dev_for_boot_part; fi - scsi_init if ${scsi_need_init}; then scsi_need_init=false; scsi scan; fi - scan_dev_for_boot_part for distro_bootpart in 1; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done - scan_dev_for_boot echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_scripts; done - scan_dev_for_scripts for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done - boot_a_script load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr} - EOF - ;; -esac - -exit 0 diff --git a/target/linux/mvebu/image/Makefile b/target/linux/mvebu/image/Makefile index 57129d2dcb8..9d6f207b3f2 100644 --- a/target/linux/mvebu/image/Makefile +++ b/target/linux/mvebu/image/Makefile @@ -136,17 +136,6 @@ define Build/sdcard-img-ext4 83 $(CONFIG_TARGET_ROOTFS_PARTSIZE) $(IMAGE_ROOTFS) endef -define Build/omnia-medkit-initramfs - $(TAR) -c -T /dev/null -f $@ - rm -rf $(dir $(IMAGE_KERNEL))boot - mkdir -p $(dir $(IMAGE_KERNEL))boot/boot/ - cp $(KDIR)/zImage-initramfs $(dir $(IMAGE_KERNEL))boot/boot/zImage - cp $(KDIR)/image-$(DEVICE_DTS).dtb $(dir $(IMAGE_KERNEL))boot/boot/dtb - $(TAR) -rp --numeric-owner --owner=0 --group=0 --sort=name \ - $(if $(SOURCE_DATE_EPOCH),--mtime="@$(SOURCE_DATE_EPOCH)") \ - --file=$@ -C $(dir $(IMAGE_KERNEL))boot/ . -endef - define Build/uDPU-firmware (rm -fR $@-fw; mkdir -p $@-fw) $(CP) $(BIN_DIR)/$(DEVICE_IMG_PREFIX)-initramfs.itb $@-fw/recovery.itb diff --git a/target/linux/mvebu/image/cortexa9.mk b/target/linux/mvebu/image/cortexa9.mk index 248f1cd6fdf..ba6213a89ef 100644 --- a/target/linux/mvebu/image/cortexa9.mk +++ b/target/linux/mvebu/image/cortexa9.mk @@ -88,10 +88,8 @@ define Device/cznic_turris-omnia mkf2fs e2fsprogs kmod-fs-vfat kmod-nls-cp437 kmod-nls-iso8859-1 \ wpad-basic-mbedtls kmod-ath9k kmod-ath10k-ct ath10k-firmware-qca988x-ct \ partx-utils kmod-i2c-mux-pca954x kmod-leds-turris-omnia - IMAGES := $$(DEVICE_IMG_PREFIX)-sysupgrade.img.gz omnia-medkit-$$(DEVICE_IMG_PREFIX)-initramfs.tar.gz - IMAGE/$$(DEVICE_IMG_PREFIX)-sysupgrade.img.gz := boot-scr | boot-img | sdcard-img | gzip | append-metadata - IMAGE/omnia-medkit-$$(DEVICE_IMG_PREFIX)-initramfs.tar.gz := omnia-medkit-initramfs | gzip - DEVICE_IMG_NAME = $$(2) + IMAGES := sysupgrade.img.gz + IMAGE/sysupgrade.img.gz := boot-scr | boot-img | sdcard-img | gzip | append-metadata SUPPORTED_DEVICES += armada-385-turris-omnia BOOT_SCRIPT := turris-omnia endef diff --git a/target/linux/octeon/config-5.15 b/target/linux/octeon/config-5.15 index 450b84be44c..5882c7dd757 100644 --- a/target/linux/octeon/config-5.15 +++ b/target/linux/octeon/config-5.15 @@ -70,7 +70,6 @@ CONFIG_EXT4_FS=y CONFIG_F2FS_FS=y CONFIG_FAT_FS=y CONFIG_FIXED_PHY=y -CONFIG_FRAME_WARN=2048 CONFIG_FS_IOMAP=y CONFIG_FS_MBCACHE=y CONFIG_FWNODE_MDIO=y diff --git a/target/linux/ramips/patches-5.15/100-PCI-mt7621-Add-MediaTek-MT7621-PCIe-host-controller-.patch b/target/linux/ramips/patches-5.15/100-v5.16-PCI-mt7621-Add-MediaTek-MT7621-PCIe-host-controller-.patch similarity index 100% rename from target/linux/ramips/patches-5.15/100-PCI-mt7621-Add-MediaTek-MT7621-PCIe-host-controller-.patch rename to target/linux/ramips/patches-5.15/100-v5.16-PCI-mt7621-Add-MediaTek-MT7621-PCIe-host-controller-.patch diff --git a/target/linux/ramips/patches-5.15/101-PCI-mt7621-Rename-mt7621_pci_-to-mt7621_pcie_.patch b/target/linux/ramips/patches-5.15/101-v5.17-PCI-mt7621-Rename-mt7621_pci_-to-mt7621_pcie_.patch similarity index 100% rename from target/linux/ramips/patches-5.15/101-PCI-mt7621-Rename-mt7621_pci_-to-mt7621_pcie_.patch rename to target/linux/ramips/patches-5.15/101-v5.17-PCI-mt7621-Rename-mt7621_pci_-to-mt7621_pcie_.patch diff --git a/target/linux/ramips/patches-5.15/102-PCI-mt7621-Declare-mt7621_pci_ops-static.patch b/target/linux/ramips/patches-5.15/102-v5.17-PCI-mt7621-Declare-mt7621_pci_ops-static.patch similarity index 100% rename from target/linux/ramips/patches-5.15/102-PCI-mt7621-Declare-mt7621_pci_ops-static.patch rename to target/linux/ramips/patches-5.15/102-v5.17-PCI-mt7621-Declare-mt7621_pci_ops-static.patch diff --git a/target/linux/ramips/patches-5.15/103-PCI-mt7621-Move-MIPS-setup-to-pcibios_root_bridge_pr.patch b/target/linux/ramips/patches-5.15/103-v5.17-PCI-mt7621-Move-MIPS-setup-to-pcibios_root_bridge_pr.patch similarity index 100% rename from target/linux/ramips/patches-5.15/103-PCI-mt7621-Move-MIPS-setup-to-pcibios_root_bridge_pr.patch rename to target/linux/ramips/patches-5.15/103-v5.17-PCI-mt7621-Move-MIPS-setup-to-pcibios_root_bridge_pr.patch diff --git a/target/linux/ramips/patches-5.15/104-PCI-mt7621-Drop-of_match_ptr-to-avoid-unused-variabl.patch b/target/linux/ramips/patches-5.15/104-v5.17-PCI-mt7621-Drop-of_match_ptr-to-avoid-unused-variabl.patch similarity index 100% rename from target/linux/ramips/patches-5.15/104-PCI-mt7621-Drop-of_match_ptr-to-avoid-unused-variabl.patch rename to target/linux/ramips/patches-5.15/104-v5.17-PCI-mt7621-Drop-of_match_ptr-to-avoid-unused-variabl.patch diff --git a/target/linux/ramips/patches-5.15/105-PCI-mt7621-Remove-unused-function-pcie_rmw.patch b/target/linux/ramips/patches-5.15/105-v5.17-PCI-mt7621-Remove-unused-function-pcie_rmw.patch similarity index 100% rename from target/linux/ramips/patches-5.15/105-PCI-mt7621-Remove-unused-function-pcie_rmw.patch rename to target/linux/ramips/patches-5.15/105-v5.17-PCI-mt7621-Remove-unused-function-pcie_rmw.patch diff --git a/target/linux/ramips/patches-5.15/106-PCI-Let-pcibios_root_bridge_prepare-access-bridge-wi.patch b/target/linux/ramips/patches-5.15/106-v5.17-PCI-Let-pcibios_root_bridge_prepare-access-bridge-wi.patch similarity index 100% rename from target/linux/ramips/patches-5.15/106-PCI-Let-pcibios_root_bridge_prepare-access-bridge-wi.patch rename to target/linux/ramips/patches-5.15/106-v5.17-PCI-Let-pcibios_root_bridge_prepare-access-bridge-wi.patch diff --git a/target/linux/ramips/patches-5.15/107-PCI-mt7621-Add-sentinel-to-quirks-table.patch b/target/linux/ramips/patches-5.15/107-v6.2-PCI-mt7621-Add-sentinel-to-quirks-table.patch similarity index 100% rename from target/linux/ramips/patches-5.15/107-PCI-mt7621-Add-sentinel-to-quirks-table.patch rename to target/linux/ramips/patches-5.15/107-v6.2-PCI-mt7621-Add-sentinel-to-quirks-table.patch diff --git a/target/linux/ramips/patches-5.15/108-PCI-mt7621-Delay-phy-ports-initialization.patch b/target/linux/ramips/patches-5.15/108-v6.3-PCI-mt7621-Delay-phy-ports-initialization.patch similarity index 100% rename from target/linux/ramips/patches-5.15/108-PCI-mt7621-Delay-phy-ports-initialization.patch rename to target/linux/ramips/patches-5.15/108-v6.3-PCI-mt7621-Delay-phy-ports-initialization.patch diff --git a/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch b/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch index a613803ee6d..e57e6fa83b2 100644 --- a/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch +++ b/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch @@ -14,7 +14,7 @@ Signed-off-by: René van Dorst --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4638,6 +4638,7 @@ static const struct net_device_ops mtk_n +@@ -4633,6 +4633,7 @@ static const struct net_device_ops mtk_n static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) { @@ -22,7 +22,7 @@ Signed-off-by: René van Dorst const __be32 *_id = of_get_property(np, "reg", NULL); phy_interface_t phy_mode; struct phylink *phylink; -@@ -4796,6 +4797,9 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4791,6 +4792,9 @@ static int mtk_add_mac(struct mtk_eth *e register_netdevice_notifier(&mac->device_notifier); } diff --git a/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch index 8f2f4c0c4aa..63a5050ebb5 100644 --- a/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch +++ b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch @@ -83,7 +83,7 @@ Signed-off-by: Sander Vanheule obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h -@@ -177,6 +177,7 @@ enum cpuhp_state { +@@ -176,6 +176,7 @@ enum cpuhp_state { CPUHP_AP_MARCO_TIMER_STARTING, CPUHP_AP_MIPS_GIC_TIMER_STARTING, CPUHP_AP_ARC_TIMER_STARTING, diff --git a/target/linux/rockchip/Makefile b/target/linux/rockchip/Makefile index 7b04bfdb218..f17f0bdf490 100644 --- a/target/linux/rockchip/Makefile +++ b/target/linux/rockchip/Makefile @@ -8,6 +8,7 @@ FEATURES:=ext4 audio usb usbgadget display gpio fpu pci pcie rootfs-part boot-pa SUBTARGETS:=armv8 KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 define Target/Description Build firmware image for Rockchip SoC devices. diff --git a/target/linux/rockchip/armv8/base-files/etc/board.d/01_leds b/target/linux/rockchip/armv8/base-files/etc/board.d/01_leds index 2c9206b03ed..d82e47cf538 100644 --- a/target/linux/rockchip/armv8/base-files/etc/board.d/01_leds +++ b/target/linux/rockchip/armv8/base-files/etc/board.d/01_leds @@ -10,7 +10,9 @@ board_config_update case $board in friendlyarm,nanopi-r2c|\ friendlyarm,nanopi-r2s|\ -friendlyarm,nanopi-r4s) +friendlyarm,nanopi-r4s|\ +xunlong,orangepi-r1-plus|\ +xunlong,orangepi-r1-plus-lts) ucidef_set_led_netdev "wan" "WAN" "green:wan" "eth0" ucidef_set_led_netdev "lan" "LAN" "green:lan" "eth1" ;; diff --git a/target/linux/rockchip/armv8/base-files/etc/board.d/02_network b/target/linux/rockchip/armv8/base-files/etc/board.d/02_network index 16ba01b7e31..c10e0cbcc9d 100644 --- a/target/linux/rockchip/armv8/base-files/etc/board.d/02_network +++ b/target/linux/rockchip/armv8/base-files/etc/board.d/02_network @@ -8,7 +8,9 @@ rockchip_setup_interfaces() case "$board" in friendlyarm,nanopi-r2s|\ - friendlyarm,nanopi-r4s) + friendlyarm,nanopi-r4s|\ + xunlong,orangepi-r1-plus|\ + xunlong,orangepi-r1-plus-lts) ucidef_set_interfaces_lan_wan 'eth1' 'eth0' ;; *) @@ -55,6 +57,10 @@ rockchip_setup_macs() wan_mac=$(nanopi_r4s_get_mac wan) lan_mac=$(nanopi_r4s_get_mac lan) ;; + xunlong,orangepi-r1-plus|\ + xunlong,orangepi-r1-plus-lts) + wan_mac=$(macaddr_add "$(cat /sys/class/net/eth1/address)" -1) + ;; esac [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac diff --git a/target/linux/rockchip/armv8/base-files/etc/hotplug.d/net/40-net-smp-affinity b/target/linux/rockchip/armv8/base-files/etc/hotplug.d/net/40-net-smp-affinity index 3d1d99128ec..bb119b9185e 100644 --- a/target/linux/rockchip/armv8/base-files/etc/hotplug.d/net/40-net-smp-affinity +++ b/target/linux/rockchip/armv8/base-files/etc/hotplug.d/net/40-net-smp-affinity @@ -30,7 +30,9 @@ set_interface_core() { case "$(board_name)" in friendlyarm,nanopi-r2c|\ -friendlyarm,nanopi-r2s) +friendlyarm,nanopi-r2s|\ +xunlong,orangepi-r1-plus|\ +xunlong,orangepi-r1-plus-lts) set_interface_core 2 "eth0" set_interface_core 4 "eth1" "xhci-hcd:usb3" ;; diff --git a/target/linux/rockchip/armv8/config-5.15 b/target/linux/rockchip/armv8/config-5.15 index 0ed009004f1..08ce65d8eaf 100644 --- a/target/linux/rockchip/armv8/config-5.15 +++ b/target/linux/rockchip/armv8/config-5.15 @@ -201,7 +201,6 @@ CONFIG_FIXED_PHY=y CONFIG_FIX_EARLYCON_MEM=y # CONFIG_FORTIFY_SOURCE is not set CONFIG_FRAME_POINTER=y -CONFIG_FRAME_WARN=2048 CONFIG_FS_IOMAP=y CONFIG_FS_MBCACHE=y CONFIG_FS_POSIX_ACL=y diff --git a/target/linux/rockchip/armv8/config-6.1 b/target/linux/rockchip/armv8/config-6.1 new file mode 100644 index 00000000000..4d747537e23 --- /dev/null +++ b/target/linux/rockchip/armv8/config-6.1 @@ -0,0 +1,690 @@ +CONFIG_64BIT=y +CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y +CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=33 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_ARC_EMAC_CORE=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_EPAN=y +CONFIG_ARM64_ERRATUM_2051678=y +CONFIG_ARM64_ERRATUM_2054223=y +CONFIG_ARM64_ERRATUM_2067961=y +CONFIG_ARM64_ERRATUM_2077057=y +CONFIG_ARM64_ERRATUM_2658417=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_ERRATUM_858921=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_RAS_EXTN=y +CONFIG_ARM64_SME=y +CONFIG_ARM64_SVE=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=48 +# CONFIG_ARM64_VA_BITS_39 is not set +CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_MHU=y +CONFIG_ARM_MHU_V2=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_RK3399_DMC_DEVFREQ is not set +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ARM_SCPI_POWER_DOMAIN=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y +# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set +CONFIG_ARM_SMMU_V3=y +# CONFIG_ARM_SMMU_V3_SVA is not set +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_BSG_COMMON=y +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_INTEGRITY_T10=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_BLK_DEV_PCIESSD_MTIP32XX=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +# CONFIG_BOSCH_BNO055_SERIAL is not set +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CHARGER_GPIO=y +# CONFIG_CHARGER_RK817 is not set +CONFIG_CLKSRC_MMIO=y +CONFIG_CLK_PX30=y +CONFIG_CLK_RK3308=y +CONFIG_CLK_RK3328=y +CONFIG_CLK_RK3368=y +CONFIG_CLK_RK3399=y +CONFIG_CLK_RK3568=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_MBYTES=5 +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SYSFS is not set +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_ROCKCHIP=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONFIGFS_FS=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CONTIG_ALLOC=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_ISOLATION=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRASH_CORE=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRC64=y +CONFIG_CRC64_ROCKSOFT=y +CONFIG_CRC_T10DIF=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_AES_ARM64=y +CONFIG_CRYPTO_AES_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_CRC64_ROCKSOFT=y +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=y +CONFIG_CRYPTO_CRYPTD=y +# CONFIG_CRYPTO_DEV_ROCKCHIP is not set +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_POLYVAL=y +CONFIG_CRYPTO_POLYVAL_ARM64_CE=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SM3=y +CONFIG_CRYPTO_SM3_NEON=y +CONFIG_CRYPTO_SM4=y +CONFIG_CRYPTO_SM4_ARM64_CE_BLK=y +CONFIG_CRYPTO_SM4_ARM64_NEON_BLK=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEVFREQ_GOV_PASSIVE is not set +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +# CONFIG_DEVFREQ_THERMAL is not set +CONFIG_DEVMEM=y +# CONFIG_DEVPORT is not set +CONFIG_DMADEVICES=y +CONFIG_DMA_CMA=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_OPS=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DNOTIFY=y +CONFIG_DTC=y +CONFIG_DT_IDLE_GENPD=y +CONFIG_DT_IDLE_STATES=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_DWMAC_DWC_QOS_ETH=y +CONFIG_DWMAC_GENERIC=y +CONFIG_DWMAC_ROCKCHIP=y +CONFIG_DW_WATCHDOG=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EEPROM_AT24=y +CONFIG_EMAC_ROCKCHIP=y +CONFIG_ENERGY_MODEL=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXTCON=y +CONFIG_F2FS_FS=y +CONFIG_FANOTIFY=y +CONFIG_FHANDLE=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FORTIFY_SOURCE is not set +CONFIG_FRAME_POINTER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=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_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_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_ROCKCHIP=y +CONFIG_GPIO_SYSCON=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_HWMON=y +CONFIG_HWSPINLOCK=y +CONFIG_HW_CONSOLE=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_RK3X=y +CONFIG_IIO=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INDIRECT_PIO=y +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_LEDS=y +CONFIG_INPUT_MATRIXKMAP=y +# CONFIG_INPUT_RK805_PWRKEY is not set +CONFIG_IOMMU_API=y +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set +# CONFIG_IOMMU_DEFAULT_DMA_STRICT is not set +CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y +CONFIG_IOMMU_DMA=y +CONFIG_IOMMU_IOVA=y +CONFIG_IOMMU_IO_PGTABLE=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_DART is not set +CONFIG_IOMMU_IO_PGTABLE_LPAE=y +# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set +CONFIG_IOMMU_SUPPORT=y +# CONFIG_IO_STRICT_DEVMEM is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_IOMMU=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JUMP_LABEL=y +CONFIG_KALLSYMS=y +CONFIG_KCMP=y +CONFIG_KEXEC_CORE=y +CONFIG_KEXEC_FILE=y +CONFIG_KSM=y +# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +# CONFIG_LEDS_PWM_MULTICOLOR is not set +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LIBCRC32C=y +CONFIG_LIBFDT=y +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LOG_BUF_SHIFT=19 +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_MAILBOX=y +# CONFIG_MAILBOX_TEST is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_GPIO=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MFD_CORE=y +# CONFIG_MFD_KHADAS_MCU is not set +CONFIG_MFD_RK808=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_CQHCI=y +CONFIG_MMC_DW=y +# CONFIG_MMC_DW_BLUEFIELD is not set +# CONFIG_MMC_DW_EXYNOS is not set +# CONFIG_MMC_DW_HI3798CV200 is not set +# CONFIG_MMC_DW_K3 is not set +# CONFIG_MMC_DW_PCI is not set +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MOTORCOMM_PHY=y +CONFIG_MQ_IOSCHED_DEADLINE=y +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SELFTESTS=y +CONFIG_NLS=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=256 +CONFIG_NVMEM=y +CONFIG_NVMEM_ROCKCHIP_EFUSE=y +# CONFIG_NVMEM_ROCKCHIP_OTP is not set +CONFIG_NVMEM_SYSFS=y +CONFIG_NVME_CORE=y +# CONFIG_NVME_HWMON is not set +# CONFIG_NVME_MULTIPATH is not set +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DYNAMIC=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IOMMU=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_RESOLVE=y +# CONFIG_OVERLAY_FS_XINO_AUTO is not set +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_PME=y +CONFIG_PCIE_ROCKCHIP=y +CONFIG_PCIE_ROCKCHIP_DW_HOST=y +CONFIG_PCIE_ROCKCHIP_HOST=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCI_STUB=y +CONFIG_PCS_XPCS=y +CONFIG_PGTABLE_LEVELS=4 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_ROCKCHIP_DP=y +# CONFIG_PHY_ROCKCHIP_DPHY_RX0 is not set +CONFIG_PHY_ROCKCHIP_EMMC=y +# CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY is not set +# CONFIG_PHY_ROCKCHIP_INNO_HDMI is not set +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +# CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY is not set +CONFIG_PHY_ROCKCHIP_PCIE=y +# CONFIG_PHY_ROCKCHIP_SNPS_PCIE3 is not set +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_RK805=y +CONFIG_PINCTRL_ROCKCHIP=y +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PL330_DMA=y +CONFIG_PLATFORM_MHU=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_DEVFREQ=y +# CONFIG_PM_DEVFREQ_EVENT is not set +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y +CONFIG_POWER_SUPPLY_HWMON=y +CONFIG_PPS=y +CONFIG_PREEMPT=y +CONFIG_PREEMPTION=y +CONFIG_PREEMPT_BUILD=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y +CONFIG_PRINTK_TIME=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_VMCORE=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_PWM=y +# CONFIG_PWM_CLK is not set +CONFIG_PWM_ROCKCHIP=y +CONFIG_PWM_SYSFS=y +# CONFIG_PWM_XILINX is not set +# CONFIG_QFMT_V2 is not set +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_QUOTA=y +CONFIG_QUOTACTL=y +CONFIG_RAID_ATTRS=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_RANDOMIZE_MODULE_REGION_FULL=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_RCU_TRACE=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_IRQ=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_RK808=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_ROCKCHIP_GRF=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_ROCKCHIP_MBOX=y +CONFIG_ROCKCHIP_PHY=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +# CONFIG_ROCKCHIP_SARADC is not set +CONFIG_ROCKCHIP_THERMAL=y +CONFIG_ROCKCHIP_TIMER=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RSEQ=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_RK808=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RTC_NVMEM=y +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCHED_MC=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_PROC_FS is not set +CONFIG_SCSI_SAS_ATTRS=y +CONFIG_SCSI_SAS_HOST_SMP=y +CONFIG_SCSI_SAS_LIBSAS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_DWLIB=y +CONFIG_SERIAL_8250_EXAR=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SG_POOL=y +CONFIG_SLUB_DEBUG=y +CONFIG_SMP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOFTIRQ_ON_OWN_STACK=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_DYNAMIC=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_ROCKCHIP=y +# CONFIG_SPI_ROCKCHIP_SFC is not set +CONFIG_SPI_SPIDEV=y +# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set +CONFIG_SQUASHFS_DECOMP_SINGLE=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +CONFIG_SRAM=y +CONFIG_SRCU=y +CONFIG_STACKDEPOT=y +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_PER_TASK=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_STACKTRACE=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +CONFIG_STRICT_DEVMEM=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_SWAP is not set +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFS_SYSCALL=y +# CONFIG_TEXTSEARCH is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_EMULATION=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TRACE_CLOCK=y +CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y +# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set +CONFIG_TRANS_TABLE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_TYPEC=y +# CONFIG_TYPEC_ANX7411 is not set +CONFIG_TYPEC_FUSB302=y +# CONFIG_TYPEC_HD3SS3220 is not set +# CONFIG_TYPEC_MUX_FSA4480 is not set +# CONFIG_TYPEC_MUX_PI3USB30532 is not set +# CONFIG_TYPEC_RT1719 is not set +# CONFIG_TYPEC_STUSB160X is not set +# CONFIG_TYPEC_TCPCI is not set +CONFIG_TYPEC_TCPM=y +# CONFIG_TYPEC_TPS6598X is not set +# CONFIG_TYPEC_WUSB3801 is not set +# CONFIG_UACCE is not set +# CONFIG_UCLAMP_TASK is not set +# CONFIG_UEVENT_HELPER is not set +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_HOST=y +CONFIG_USB_DWC3_OF_SIMPLE=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_HID=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_PHY=y +CONFIG_USB_ROLE_SWITCH=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_ULPI=y +CONFIG_USB_ULPI_BUS=y +CONFIG_USB_ULPI_VIEWPORT=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +# CONFIG_VIRTIO_MENU is not set +CONFIG_VMAP_STACK=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_WATCHDOG_CORE=y +CONFIG_XARRAY_MULTI=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/rockchip/image/armv8.mk b/target/linux/rockchip/image/armv8.mk index 601ddcbdd6b..063dc6fd644 100644 --- a/target/linux/rockchip/image/armv8.mk +++ b/target/linux/rockchip/image/armv8.mk @@ -60,3 +60,21 @@ define Device/radxa_rock-pi-4a IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata endef TARGET_DEVICES += radxa_rock-pi-4a + +define Device/xunlong_orangepi-r1-plus + DEVICE_VENDOR := Xunlong + DEVICE_MODEL := Orange Pi R1 Plus + SOC := rk3328 + IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata + DEVICE_PACKAGES := kmod-usb-net-rtl8152 +endef +TARGET_DEVICES += xunlong_orangepi-r1-plus + +define Device/xunlong_orangepi-r1-plus-lts + DEVICE_VENDOR := Xunlong + DEVICE_MODEL := Orange Pi R1 Plus LTS + SOC := rk3328 + IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata + DEVICE_PACKAGES := kmod-usb-net-rtl8152 +endef +TARGET_DEVICES += xunlong_orangepi-r1-plus-lts diff --git a/target/linux/rockchip/patches-5.15/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch b/target/linux/rockchip/patches-5.15/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch new file mode 100644 index 00000000000..053a4d0d8e2 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch @@ -0,0 +1,407 @@ +From 51712e1d014aaaa4c6e1e7e84932d58b5c0f59ed Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Sat, 3 Dec 2022 15:41:49 +0800 +Subject: [PATCH] arm64: dts: rockchip: rk3328: Add Orange Pi R1 Plus + +Orange Pi R1 Plus is a Rockchip RK3328 based SBC by Xunlong. + +This device is similar to the NanoPi R2S, and has a 16MB +SPI NOR (mx25l12805d). The reset button is changed to +directly reset the power supply, another detail is that +both network ports have independent MAC addresses. + +Signed-off-by: Chukun Pan +Link: https://lore.kernel.org/r/20221203074149.11543-3-amadeus@jmu.edu.cn +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/Makefile | 1 + + .../dts/rockchip/rk3328-orangepi-r1-plus.dts | 373 ++++++++++++++++++ + 2 files changed, 374 insertions(+) + create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts + +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -11,6 +11,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-a1 + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-evb.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2c.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2s.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock-pi-e.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -0,0 +1,373 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Based on rk3328-nanopi-r2s.dts, which is: ++ * Copyright (c) 2020 David Bauer ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include "rk3328.dtsi" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus"; ++ compatible = "xunlong,orangepi-r1-plus", "rockchip,rk3328"; ++ ++ aliases { ++ ethernet1 = &rtl8153; ++ mmc0 = &sdmmc; ++ }; ++ ++ chosen { ++ stdout-path = "serial2:1500000n8"; ++ }; ++ ++ gmac_clk: gmac-clock { ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_clkin"; ++ #clock-cells = <0>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-0 = <&lan_led_pin>, <&sys_led_pin>, <&wan_led_pin>; ++ pinctrl-names = "default"; ++ ++ led-0 { ++ function = LED_FUNCTION_LAN; ++ color = ; ++ gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-1 { ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led-2 { ++ function = LED_FUNCTION_WAN; ++ color = ; ++ gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ ++ vcc_sd: sdmmc-regulator { ++ compatible = "regulator-fixed"; ++ gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&sdmmc0m1_pin>; ++ pinctrl-names = "default"; ++ regulator-name = "vcc_sd"; ++ regulator-boot-on; ++ vin-supply = <&vcc_io>; ++ }; ++ ++ vcc_sys: vcc-sys-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ vdd_5v_lan: vdd-5v-lan-regulator { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&lan_vdd_pin>; ++ pinctrl-names = "default"; ++ regulator-name = "vdd_5v_lan"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc_sys>; ++ }; ++}; ++ ++&cpu0 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu1 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu2 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu3 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&display_subsystem { ++ status = "disabled"; ++}; ++ ++&gmac2io { ++ assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; ++ assigned-clock-parents = <&gmac_clk>, <&gmac_clk>; ++ clock_in_out = "input"; ++ phy-handle = <&rtl8211e>; ++ phy-mode = "rgmii"; ++ phy-supply = <&vcc_io>; ++ pinctrl-0 = <&rgmiim1_pins>; ++ pinctrl-names = "default"; ++ snps,aal; ++ rx_delay = <0x18>; ++ tx_delay = <0x24>; ++ status = "okay"; ++ ++ mdio { ++ compatible = "snps,dwmac-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rtl8211e: ethernet-phy@1 { ++ reg = <1>; ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; ++ ++&i2c1 { ++ status = "okay"; ++ ++ rk805: pmic@18 { ++ compatible = "rockchip,rk805"; ++ reg = <0x18>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <24 IRQ_TYPE_LEVEL_LOW>; ++ #clock-cells = <1>; ++ clock-output-names = "xin32k", "rk805-clkout2"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ pinctrl-0 = <&pmic_int_l>; ++ pinctrl-names = "default"; ++ rockchip,system-power-controller; ++ wakeup-source; ++ ++ vcc1-supply = <&vcc_sys>; ++ vcc2-supply = <&vcc_sys>; ++ vcc3-supply = <&vcc_sys>; ++ vcc4-supply = <&vcc_sys>; ++ vcc5-supply = <&vcc_io>; ++ vcc6-supply = <&vcc_sys>; ++ ++ regulators { ++ vdd_log: DCDC_REG1 { ++ regulator-name = "vdd_log"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ ++ vdd_arm: DCDC_REG2 { ++ regulator-name = "vdd_arm"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <950000>; ++ }; ++ }; ++ ++ vcc_ddr: DCDC_REG3 { ++ regulator-name = "vcc_ddr"; ++ regulator-always-on; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ vcc_io: DCDC_REG4 { ++ regulator-name = "vcc_io"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3300000>; ++ }; ++ }; ++ ++ vcc_18: LDO_REG1 { ++ regulator-name = "vcc_18"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vcc18_emmc: LDO_REG2 { ++ regulator-name = "vcc18_emmc"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vdd_10: LDO_REG3 { ++ regulator-name = "vdd_10"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&io_domains { ++ pmuio-supply = <&vcc_io>; ++ vccio1-supply = <&vcc_io>; ++ vccio2-supply = <&vcc18_emmc>; ++ vccio3-supply = <&vcc_io>; ++ vccio4-supply = <&vcc_io>; ++ vccio5-supply = <&vcc_io>; ++ vccio6-supply = <&vcc_io>; ++ status = "okay"; ++}; ++ ++&pinctrl { ++ gmac2io { ++ eth_phy_reset_pin: eth-phy-reset-pin { ++ rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ leds { ++ lan_led_pin: lan-led-pin { ++ rockchip,pins = <2 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ sys_led_pin: sys-led-pin { ++ rockchip,pins = <3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ wan_led_pin: wan-led-pin { ++ rockchip,pins = <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ lan { ++ lan_vdd_pin: lan-vdd-pin { ++ rockchip,pins = <2 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ pmic { ++ pmic_int_l: pmic-int-l { ++ rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++}; ++ ++&pwm2 { ++ status = "okay"; ++}; ++ ++&sdmmc { ++ bus-width = <4>; ++ cap-sd-highspeed; ++ disable-wp; ++ pinctrl-0 = <&sdmmc0_clk>, <&sdmmc0_cmd>, <&sdmmc0_dectn>, <&sdmmc0_bus4>; ++ pinctrl-names = "default"; ++ vmmc-supply = <&vcc_sd>; ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++}; ++ ++&tsadc { ++ rockchip,hw-tshut-mode = <0>; ++ rockchip,hw-tshut-polarity = <0>; ++ status = "okay"; ++}; ++ ++&u2phy { ++ status = "okay"; ++}; ++ ++&u2phy_host { ++ status = "okay"; ++}; ++ ++&u2phy_otg { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&usb20_otg { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&usbdrd3 { ++ dr_mode = "host"; ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* Second port is for USB 3.0 */ ++ rtl8153: device@2 { ++ compatible = "usbbda,8153"; ++ reg = <2>; ++ }; ++}; ++ ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; diff --git a/target/linux/rockchip/patches-5.15/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch b/target/linux/rockchip/patches-5.15/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch new file mode 100644 index 00000000000..cedf28dc799 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch @@ -0,0 +1,71 @@ +From 387b3bbac5ea6a0a105d685237f033ffe0f184f1 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Sat, 25 Mar 2023 15:40:22 +0800 +Subject: [PATCH] arm64: dts: rockchip: Add Xunlong OrangePi R1 Plus LTS + +The OrangePi R1 Plus LTS is a minor variant of OrangePi R1 Plus with +the on-board NIC chip changed from rtl8211e to yt8531c, and otherwise +identical to OrangePi R1 Plus. + +Signed-off-by: Tianling Shen +Link: https://lore.kernel.org/r/20230325074022.9818-5-cnsztl@gmail.com +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/Makefile | 1 + + .../rockchip/rk3328-orangepi-r1-plus-lts.dts | 40 +++++++++++++++++++ + 2 files changed, 41 insertions(+) + create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts + +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-ev + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2c.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2s.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus-lts.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock-pi-e.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later OR MIT ++/* ++ * Copyright (c) 2016 Xunlong Software. Co., Ltd. ++ * (http://www.orangepi.org) ++ * ++ * Copyright (c) 2021-2023 Tianling Shen ++ */ ++ ++/dts-v1/; ++#include "rk3328-orangepi-r1-plus.dts" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus LTS"; ++ compatible = "xunlong,orangepi-r1-plus-lts", "rockchip,rk3328"; ++}; ++ ++&gmac2io { ++ phy-handle = <&yt8531c>; ++ tx_delay = <0x19>; ++ rx_delay = <0x05>; ++ ++ mdio { ++ /delete-node/ ethernet-phy@1; ++ ++ yt8531c: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ ++ motorcomm,clk-out-frequency-hz = <125000000>; ++ motorcomm,keep-pll-enabled; ++ motorcomm,auto-sleep-disabled; ++ ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <15000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; diff --git a/target/linux/rockchip/patches-5.15/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch b/target/linux/rockchip/patches-5.15/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch new file mode 100644 index 00000000000..b1e26d4e3e2 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch @@ -0,0 +1,56 @@ +From d2166e3b3680bd2b206aebf1e1ce4c0d346f3c50 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Fri, 19 May 2023 12:10:52 +0800 +Subject: [PATCH] arm64: dts: rockchip: Update LED properties for Orange Pi R1 + Plus + +Add OpenWrt's LED aliases for showing system status. +Also replace function/color with legacy label as OpenWrt relys on it +to update LED settings. + +Signed-off-by: Tianling Shen +--- + .../dts/rockchip/rk3328-orangepi-r1-plus.dts | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -17,6 +17,11 @@ + aliases { + ethernet1 = &rtl8153; + mmc0 = &sdmmc; ++ ++ led-boot = &status_led; ++ led-failsafe = &status_led; ++ led-running = &status_led; ++ led-upgrade = &status_led; + }; + + chosen { +@@ -36,22 +41,18 @@ + pinctrl-names = "default"; + + led-0 { +- function = LED_FUNCTION_LAN; +- color = ; + gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; ++ label = "green:lan"; + }; + +- led-1 { +- function = LED_FUNCTION_STATUS; +- color = ; ++ status_led: led-1 { + gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; +- linux,default-trigger = "heartbeat"; ++ label = "red:status"; + }; + + led-2 { +- function = LED_FUNCTION_WAN; +- color = ; + gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; ++ label = "green:wan"; + }; + }; + diff --git a/target/linux/rockchip/patches-5.15/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch b/target/linux/rockchip/patches-5.15/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch new file mode 100644 index 00000000000..37b59925fc1 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch @@ -0,0 +1,24 @@ +From b46a530d12ada422b9d5b2b97059e0d3ed950b40 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Fri, 19 May 2023 12:38:04 +0800 +Subject: [PATCH] arm64: dts: rockchip: add LED configuration to Orange Pi R1 + Plus + +Add the correct value for the RTL8153 LED configuration register to +match the blink behavior of the other port on the device. + +Signed-off-by: Tianling Shen +--- + arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -362,6 +362,7 @@ + rtl8153: device@2 { + compatible = "usbbda,8153"; + reg = <2>; ++ realtek,led-data = <0x87>; + }; + }; + diff --git a/target/linux/rockchip/patches-6.1/005-arm64-dts-rockchip-add-EEPROM-node-for-NanoPi-R4S.patch b/target/linux/rockchip/patches-6.1/005-arm64-dts-rockchip-add-EEPROM-node-for-NanoPi-R4S.patch new file mode 100644 index 00000000000..792028b2929 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/005-arm64-dts-rockchip-add-EEPROM-node-for-NanoPi-R4S.patch @@ -0,0 +1,31 @@ +From af20b3384e8723077cc6484160b0cf4e9be321de Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Mon, 7 Jun 2021 15:45:37 +0800 +Subject: [PATCH] arm64: dts: rockchip: add EEPROM node for NanoPi R4S + +NanoPi R4S has a EEPROM attached to the 2nd I2C bus (U92), which +stores the MAC address. + +Signed-off-by: Tianling Shen +--- + arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts +@@ -68,6 +68,15 @@ + status = "disabled"; + }; + ++&i2c2 { ++ eeprom@51 { ++ compatible = "microchip,24c02", "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ read-only; /* This holds our MAC */ ++ }; ++}; ++ + &i2c4 { + status = "disabled"; + }; diff --git a/target/linux/rockchip/patches-6.1/006-v6.4-arm64-dts-rockchip-Add-FriendlyARM-NanoPi-R2C.patch b/target/linux/rockchip/patches-6.1/006-v6.4-arm64-dts-rockchip-Add-FriendlyARM-NanoPi-R2C.patch new file mode 100644 index 00000000000..049c8ad1af8 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/006-v6.4-arm64-dts-rockchip-Add-FriendlyARM-NanoPi-R2C.patch @@ -0,0 +1,70 @@ +From 004589ff9df5b75672a78b6c3c4cba93202b14c9 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Sat, 25 Mar 2023 15:40:20 +0800 +Subject: [PATCH] arm64: dts: rockchip: Add FriendlyARM NanoPi R2C + +The NanoPi R2C is a minor variant of NanoPi R2S with the on-board NIC +chip changed from rtl8211e to yt8521s, and otherwise identical to R2S. + +Signed-off-by: Tianling Shen +Link: https://lore.kernel.org/r/20230325074022.9818-3-cnsztl@gmail.com +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/Makefile | 1 + + .../boot/dts/rockchip/rk3328-nanopi-r2c.dts | 40 +++++++++++++++++++ + 2 files changed, 41 insertions(+) + create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts + +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -10,6 +10,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3318-a9 + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3326-odroid-go2.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-a1.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-evb.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2c.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2s.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock-pi-e.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later OR MIT ++/* ++ * Copyright (c) 2021 FriendlyElec Computer Tech. Co., Ltd. ++ * (http://www.friendlyarm.com) ++ * ++ * Copyright (c) 2021-2023 Tianling Shen ++ */ ++ ++/dts-v1/; ++#include "rk3328-nanopi-r2s.dts" ++ ++/ { ++ model = "FriendlyElec NanoPi R2C"; ++ compatible = "friendlyarm,nanopi-r2c", "rockchip,rk3328"; ++}; ++ ++&gmac2io { ++ phy-handle = <&yt8521s>; ++ tx_delay = <0x22>; ++ rx_delay = <0x12>; ++ ++ mdio { ++ /delete-node/ ethernet-phy@1; ++ ++ yt8521s: ethernet-phy@3 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <3>; ++ ++ motorcomm,clk-out-frequency-hz = <125000000>; ++ motorcomm,keep-pll-enabled; ++ motorcomm,auto-sleep-disabled; ++ ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; diff --git a/target/linux/rockchip/patches-6.1/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch b/target/linux/rockchip/patches-6.1/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch new file mode 100644 index 00000000000..4e48218b894 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/007-v6.3-arm64-dts-rockchip-rk3328-Add-Orange-Pi-R1-Plus.patch @@ -0,0 +1,407 @@ +From 51712e1d014aaaa4c6e1e7e84932d58b5c0f59ed Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Sat, 3 Dec 2022 15:41:49 +0800 +Subject: [PATCH] arm64: dts: rockchip: rk3328: Add Orange Pi R1 Plus + +Orange Pi R1 Plus is a Rockchip RK3328 based SBC by Xunlong. + +This device is similar to the NanoPi R2S, and has a 16MB +SPI NOR (mx25l12805d). The reset button is changed to +directly reset the power supply, another detail is that +both network ports have independent MAC addresses. + +Signed-off-by: Chukun Pan +Link: https://lore.kernel.org/r/20221203074149.11543-3-amadeus@jmu.edu.cn +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/Makefile | 1 + + .../dts/rockchip/rk3328-orangepi-r1-plus.dts | 373 ++++++++++++++++++ + 2 files changed, 374 insertions(+) + create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts + +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-a1 + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-evb.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2c.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2s.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock-pi-e.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -0,0 +1,373 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Based on rk3328-nanopi-r2s.dts, which is: ++ * Copyright (c) 2020 David Bauer ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include "rk3328.dtsi" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus"; ++ compatible = "xunlong,orangepi-r1-plus", "rockchip,rk3328"; ++ ++ aliases { ++ ethernet1 = &rtl8153; ++ mmc0 = &sdmmc; ++ }; ++ ++ chosen { ++ stdout-path = "serial2:1500000n8"; ++ }; ++ ++ gmac_clk: gmac-clock { ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_clkin"; ++ #clock-cells = <0>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-0 = <&lan_led_pin>, <&sys_led_pin>, <&wan_led_pin>; ++ pinctrl-names = "default"; ++ ++ led-0 { ++ function = LED_FUNCTION_LAN; ++ color = ; ++ gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-1 { ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led-2 { ++ function = LED_FUNCTION_WAN; ++ color = ; ++ gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ ++ vcc_sd: sdmmc-regulator { ++ compatible = "regulator-fixed"; ++ gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&sdmmc0m1_pin>; ++ pinctrl-names = "default"; ++ regulator-name = "vcc_sd"; ++ regulator-boot-on; ++ vin-supply = <&vcc_io>; ++ }; ++ ++ vcc_sys: vcc-sys-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ vdd_5v_lan: vdd-5v-lan-regulator { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&lan_vdd_pin>; ++ pinctrl-names = "default"; ++ regulator-name = "vdd_5v_lan"; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vcc_sys>; ++ }; ++}; ++ ++&cpu0 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu1 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu2 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&cpu3 { ++ cpu-supply = <&vdd_arm>; ++}; ++ ++&display_subsystem { ++ status = "disabled"; ++}; ++ ++&gmac2io { ++ assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; ++ assigned-clock-parents = <&gmac_clk>, <&gmac_clk>; ++ clock_in_out = "input"; ++ phy-handle = <&rtl8211e>; ++ phy-mode = "rgmii"; ++ phy-supply = <&vcc_io>; ++ pinctrl-0 = <&rgmiim1_pins>; ++ pinctrl-names = "default"; ++ snps,aal; ++ rx_delay = <0x18>; ++ tx_delay = <0x24>; ++ status = "okay"; ++ ++ mdio { ++ compatible = "snps,dwmac-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rtl8211e: ethernet-phy@1 { ++ reg = <1>; ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; ++ ++&i2c1 { ++ status = "okay"; ++ ++ rk805: pmic@18 { ++ compatible = "rockchip,rk805"; ++ reg = <0x18>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <24 IRQ_TYPE_LEVEL_LOW>; ++ #clock-cells = <1>; ++ clock-output-names = "xin32k", "rk805-clkout2"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ pinctrl-0 = <&pmic_int_l>; ++ pinctrl-names = "default"; ++ rockchip,system-power-controller; ++ wakeup-source; ++ ++ vcc1-supply = <&vcc_sys>; ++ vcc2-supply = <&vcc_sys>; ++ vcc3-supply = <&vcc_sys>; ++ vcc4-supply = <&vcc_sys>; ++ vcc5-supply = <&vcc_io>; ++ vcc6-supply = <&vcc_sys>; ++ ++ regulators { ++ vdd_log: DCDC_REG1 { ++ regulator-name = "vdd_log"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ ++ vdd_arm: DCDC_REG2 { ++ regulator-name = "vdd_arm"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1450000>; ++ regulator-ramp-delay = <12500>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <950000>; ++ }; ++ }; ++ ++ vcc_ddr: DCDC_REG3 { ++ regulator-name = "vcc_ddr"; ++ regulator-always-on; ++ regulator-boot-on; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ vcc_io: DCDC_REG4 { ++ regulator-name = "vcc_io"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3300000>; ++ }; ++ }; ++ ++ vcc_18: LDO_REG1 { ++ regulator-name = "vcc_18"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vcc18_emmc: LDO_REG2 { ++ regulator-name = "vcc18_emmc"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vdd_10: LDO_REG3 { ++ regulator-name = "vdd_10"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1000000>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&io_domains { ++ pmuio-supply = <&vcc_io>; ++ vccio1-supply = <&vcc_io>; ++ vccio2-supply = <&vcc18_emmc>; ++ vccio3-supply = <&vcc_io>; ++ vccio4-supply = <&vcc_io>; ++ vccio5-supply = <&vcc_io>; ++ vccio6-supply = <&vcc_io>; ++ status = "okay"; ++}; ++ ++&pinctrl { ++ gmac2io { ++ eth_phy_reset_pin: eth-phy-reset-pin { ++ rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ leds { ++ lan_led_pin: lan-led-pin { ++ rockchip,pins = <2 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ sys_led_pin: sys-led-pin { ++ rockchip,pins = <3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ wan_led_pin: wan-led-pin { ++ rockchip,pins = <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ lan { ++ lan_vdd_pin: lan-vdd-pin { ++ rockchip,pins = <2 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ pmic { ++ pmic_int_l: pmic-int-l { ++ rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++}; ++ ++&pwm2 { ++ status = "okay"; ++}; ++ ++&sdmmc { ++ bus-width = <4>; ++ cap-sd-highspeed; ++ disable-wp; ++ pinctrl-0 = <&sdmmc0_clk>, <&sdmmc0_cmd>, <&sdmmc0_dectn>, <&sdmmc0_bus4>; ++ pinctrl-names = "default"; ++ vmmc-supply = <&vcc_sd>; ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++}; ++ ++&tsadc { ++ rockchip,hw-tshut-mode = <0>; ++ rockchip,hw-tshut-polarity = <0>; ++ status = "okay"; ++}; ++ ++&u2phy { ++ status = "okay"; ++}; ++ ++&u2phy_host { ++ status = "okay"; ++}; ++ ++&u2phy_otg { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&usb20_otg { ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&usbdrd3 { ++ dr_mode = "host"; ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* Second port is for USB 3.0 */ ++ rtl8153: device@2 { ++ compatible = "usbbda,8153"; ++ reg = <2>; ++ }; ++}; ++ ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; diff --git a/target/linux/rockchip/patches-6.1/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch b/target/linux/rockchip/patches-6.1/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch new file mode 100644 index 00000000000..78d3d51a17a --- /dev/null +++ b/target/linux/rockchip/patches-6.1/008-v6.4-arm64-dts-rockchip-Add-Xunlong-OrangePi-R1-Plus-LTS.patch @@ -0,0 +1,71 @@ +From 387b3bbac5ea6a0a105d685237f033ffe0f184f1 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Sat, 25 Mar 2023 15:40:22 +0800 +Subject: [PATCH] arm64: dts: rockchip: Add Xunlong OrangePi R1 Plus LTS + +The OrangePi R1 Plus LTS is a minor variant of OrangePi R1 Plus with +the on-board NIC chip changed from rtl8211e to yt8531c, and otherwise +identical to OrangePi R1 Plus. + +Signed-off-by: Tianling Shen +Link: https://lore.kernel.org/r/20230325074022.9818-5-cnsztl@gmail.com +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/Makefile | 1 + + .../rockchip/rk3328-orangepi-r1-plus-lts.dts | 40 +++++++++++++++++++ + 2 files changed, 41 insertions(+) + create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts + +--- a/arch/arm64/boot/dts/rockchip/Makefile ++++ b/arch/arm64/boot/dts/rockchip/Makefile +@@ -13,6 +13,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-ev + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2c.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-nanopi-r2s.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus.dtb ++dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-orangepi-r1-plus-lts.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock-pi-e.dtb + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later OR MIT ++/* ++ * Copyright (c) 2016 Xunlong Software. Co., Ltd. ++ * (http://www.orangepi.org) ++ * ++ * Copyright (c) 2021-2023 Tianling Shen ++ */ ++ ++/dts-v1/; ++#include "rk3328-orangepi-r1-plus.dts" ++ ++/ { ++ model = "Xunlong Orange Pi R1 Plus LTS"; ++ compatible = "xunlong,orangepi-r1-plus-lts", "rockchip,rk3328"; ++}; ++ ++&gmac2io { ++ phy-handle = <&yt8531c>; ++ tx_delay = <0x19>; ++ rx_delay = <0x05>; ++ ++ mdio { ++ /delete-node/ ethernet-phy@1; ++ ++ yt8531c: ethernet-phy@0 { ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ reg = <0>; ++ ++ motorcomm,clk-out-frequency-hz = <125000000>; ++ motorcomm,keep-pll-enabled; ++ motorcomm,auto-sleep-disabled; ++ ++ pinctrl-0 = <ð_phy_reset_pin>; ++ pinctrl-names = "default"; ++ reset-assert-us = <15000>; ++ reset-deassert-us = <50000>; ++ reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++}; diff --git a/target/linux/rockchip/patches-6.1/100-rockchip-use-system-LED-for-OpenWrt.patch b/target/linux/rockchip/patches-6.1/100-rockchip-use-system-LED-for-OpenWrt.patch new file mode 100644 index 00000000000..01009c5eb84 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/100-rockchip-use-system-LED-for-OpenWrt.patch @@ -0,0 +1,65 @@ +From 6731d2c9039fbe1ecf21915eab3acee0a999508a Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 10 Jul 2020 21:38:20 +0200 +Subject: [PATCH] rockchip: use system LED for OpenWrt + +Use the SYS LED on the casing for showing system status. + +This patch is kept separate from the NanoPi R2S support patch, as i plan +on submitting the device support upstream. + +Signed-off-by: David Bauer +--- + arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts +@@ -16,6 +16,11 @@ + aliases { + ethernet1 = &rtl8153; + mmc0 = &sdmmc; ++ ++ led-boot = &sys_led; ++ led-failsafe = &sys_led; ++ led-running = &sys_led; ++ led-upgrade = &sys_led; + }; + + chosen { +@@ -49,18 +54,18 @@ + + lan_led: led-0 { + gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; +- label = "nanopi-r2s:green:lan"; ++ label = "green:lan"; + }; + + sys_led: led-1 { + gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; +- label = "nanopi-r2s:red:sys"; ++ label = "red:sys"; + default-state = "on"; + }; + + wan_led: led-2 { + gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; +- label = "nanopi-r2s:green:wan"; ++ label = "green:wan"; + }; + }; + +--- a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts +@@ -13,6 +13,11 @@ + aliases { + mmc0 = &sdmmc; + mmc1 = &emmc; ++ ++ led-boot = &power_led; ++ led-failsafe = &power_led; ++ led-running = &power_led; ++ led-upgrade = &power_led; + }; + + chosen { diff --git a/target/linux/rockchip/patches-6.1/103-arm64-rockchip-add-OF-node-for-USB-eth-on-NanoPi-R2S.patch b/target/linux/rockchip/patches-6.1/103-arm64-rockchip-add-OF-node-for-USB-eth-on-NanoPi-R2S.patch new file mode 100644 index 00000000000..2221077c97c --- /dev/null +++ b/target/linux/rockchip/patches-6.1/103-arm64-rockchip-add-OF-node-for-USB-eth-on-NanoPi-R2S.patch @@ -0,0 +1,24 @@ +From 2795c8b31a686bdb8338f9404d18ef7a154f0d75 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sun, 26 Jul 2020 13:32:59 +0200 +Subject: [PATCH] arm64: rockchip: add OF node for USB eth on NanoPi R2S + +This adds the OF node for the USB3 ethernet adapter on the FriendlyARM +NanoPi R2S. Add the correct value for the RTL8153 LED configuration +register to match the blink behavior of the other port on the device. + +Signed-off-by: David Bauer +--- + arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts | 7 +++++++ + 1 file changed, 1 insertions(+) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s.dts +@@ -402,6 +402,7 @@ + rtl8153: device@2 { + compatible = "usbbda,8153"; + reg = <2>; ++ realtek,led-data = <0x87>; + }; + }; + diff --git a/target/linux/rockchip/patches-6.1/105-nanopi-r4s-sd-signalling.patch b/target/linux/rockchip/patches-6.1/105-nanopi-r4s-sd-signalling.patch new file mode 100644 index 00000000000..856970738aa --- /dev/null +++ b/target/linux/rockchip/patches-6.1/105-nanopi-r4s-sd-signalling.patch @@ -0,0 +1,36 @@ +From: David Bauer +Subject: arm64: dts: rockchip: disable UHS modes for NanoPi R4S + +The NanoPi R4S leaves the SD card in 1.8V signalling when rebooting +while U-Boot requires the card to be in 3.3V mode. + +Remove UHS support from the SD controller so the card remains in 3.3V +mode. This reduces transfer speeds but ensures a reboot whether from +userspace or following a kernel panic is always working. + +Signed-off-by: David Bauer + +--- a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts +@@ -335,7 +335,6 @@ + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; +- sd-uhs-sdr104; + vmmc-supply = <&vcc_sd>; + vqmmc-supply = <&vcc_sdio>; + status = "okay"; +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts +@@ -121,6 +121,11 @@ + status = "disabled"; + }; + ++&sdmmc { ++ /delete-property/ sd-uhs-sdr104; ++ cap-sd-highspeed; ++}; ++ + &u2phy0_host { + phy-supply = <&vdd_5v>; + }; diff --git a/target/linux/rockchip/patches-6.1/106-r4s-openwrt-leds.patch b/target/linux/rockchip/patches-6.1/106-r4s-openwrt-leds.patch new file mode 100644 index 00000000000..d7579d61e90 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/106-r4s-openwrt-leds.patch @@ -0,0 +1,16 @@ +--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dts +@@ -19,6 +19,13 @@ + model = "FriendlyElec NanoPi R4S"; + compatible = "friendlyarm,nanopi-r4s", "rockchip,rk3399"; + ++ aliases { ++ led-boot = &sys_led; ++ led-failsafe = &sys_led; ++ led-running = &sys_led; ++ led-upgrade = &sys_led; ++ }; ++ + /delete-node/ display-subsystem; + + gpio-leds { diff --git a/target/linux/rockchip/patches-6.1/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch b/target/linux/rockchip/patches-6.1/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch new file mode 100644 index 00000000000..b1e26d4e3e2 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/107-arm64-dts-rockchip-Update-LED-properties-for-Orange-.patch @@ -0,0 +1,56 @@ +From d2166e3b3680bd2b206aebf1e1ce4c0d346f3c50 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Fri, 19 May 2023 12:10:52 +0800 +Subject: [PATCH] arm64: dts: rockchip: Update LED properties for Orange Pi R1 + Plus + +Add OpenWrt's LED aliases for showing system status. +Also replace function/color with legacy label as OpenWrt relys on it +to update LED settings. + +Signed-off-by: Tianling Shen +--- + .../dts/rockchip/rk3328-orangepi-r1-plus.dts | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -17,6 +17,11 @@ + aliases { + ethernet1 = &rtl8153; + mmc0 = &sdmmc; ++ ++ led-boot = &status_led; ++ led-failsafe = &status_led; ++ led-running = &status_led; ++ led-upgrade = &status_led; + }; + + chosen { +@@ -36,22 +41,18 @@ + pinctrl-names = "default"; + + led-0 { +- function = LED_FUNCTION_LAN; +- color = ; + gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; ++ label = "green:lan"; + }; + +- led-1 { +- function = LED_FUNCTION_STATUS; +- color = ; ++ status_led: led-1 { + gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>; +- linux,default-trigger = "heartbeat"; ++ label = "red:status"; + }; + + led-2 { +- function = LED_FUNCTION_WAN; +- color = ; + gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>; ++ label = "green:wan"; + }; + }; + diff --git a/target/linux/rockchip/patches-6.1/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch b/target/linux/rockchip/patches-6.1/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch new file mode 100644 index 00000000000..37b59925fc1 --- /dev/null +++ b/target/linux/rockchip/patches-6.1/108-arm64-dts-rockchip-add-LED-configuration-to-Orange-P.patch @@ -0,0 +1,24 @@ +From b46a530d12ada422b9d5b2b97059e0d3ed950b40 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Fri, 19 May 2023 12:38:04 +0800 +Subject: [PATCH] arm64: dts: rockchip: add LED configuration to Orange Pi R1 + Plus + +Add the correct value for the RTL8153 LED configuration register to +match the blink behavior of the other port on the device. + +Signed-off-by: Tianling Shen +--- + arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus.dts +@@ -362,6 +362,7 @@ + rtl8153: device@2 { + compatible = "usbbda,8153"; + reg = <2>; ++ realtek,led-data = <0x87>; + }; + }; + diff --git a/target/linux/sifiveu/Makefile b/target/linux/sifiveu/Makefile new file mode 100644 index 00000000000..f88164e7af3 --- /dev/null +++ b/target/linux/sifiveu/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 Toco Technologies +# +include $(TOPDIR)/rules.mk + +ARCH:=riscv64 +BOARD:=sifiveu +BOARDNAME:=SiFive U-based RISC-V boards +FEATURES:=ext4 +KERNELNAME:=Image dtbs +SUBTARGETS:=generic + +KERNEL_PATCHVER:=5.15 + +include $(INCLUDE_DIR)/target.mk + +define Target/Description + Build firmware images for the SiFive U-based boards + (HiFive boards) +endef + +$(eval $(call BuildTarget)) diff --git a/target/linux/sifiveu/base-files/etc/board.d/01_leds b/target/linux/sifiveu/base-files/etc/board.d/01_leds new file mode 100644 index 00000000000..5610a791ecd --- /dev/null +++ b/target/linux/sifiveu/base-files/etc/board.d/01_leds @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in +sifive,hifive-unleashed-a00) + ucidef_set_led_netdev "lan" "LAN" "green:d3" "eth0" + ;; +sifive,hifive-unmatched-a00) + ucidef_set_led_netdev "lan" "LAN" "green:d2" "eth0" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/sifiveu/base-files/etc/board.d/02_network b/target/linux/sifiveu/base-files/etc/board.d/02_network new file mode 100644 index 00000000000..ca9f7f20080 --- /dev/null +++ b/target/linux/sifiveu/base-files/etc/board.d/02_network @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in +*) + ucidef_set_interface_lan 'eth0' + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/sifiveu/base-files/etc/inittab b/target/linux/sifiveu/base-files/etc/inittab new file mode 100644 index 00000000000..69f97c47c81 --- /dev/null +++ b/target/linux/sifiveu/base-files/etc/inittab @@ -0,0 +1,4 @@ +::sysinit:/etc/init.d/rcS S boot +::shutdown:/etc/init.d/rcS K shutdown +ttySIF0::askfirst:/usr/libexec/login.sh +tty1::askfirst:/usr/libexec/login.sh diff --git a/target/linux/sifiveu/base-files/lib/preinit/79_move_config b/target/linux/sifiveu/base-files/lib/preinit/79_move_config new file mode 100644 index 00000000000..2796c4d01b6 --- /dev/null +++ b/target/linux/sifiveu/base-files/lib/preinit/79_move_config @@ -0,0 +1,19 @@ +# Copyright (C) 2012-2015 OpenWrt.org + +move_config() { + local partdev + + . /lib/upgrade/common.sh + + if export_bootdevice && export_partdevice partdev 3; then + if mount -t vfat -o rw,noatime "/dev/$partdev" /mnt; then + if [ -f "/mnt/$BACKUP_FILE" ]; then + mv -f "/mnt/$BACKUP_FILE" / + fi + umount /mnt + fi + fi +} + +boot_hook_add preinit_mount_root move_config + diff --git a/target/linux/sifiveu/base-files/lib/upgrade/platform.sh b/target/linux/sifiveu/base-files/lib/upgrade/platform.sh new file mode 100644 index 00000000000..b5f6bada419 --- /dev/null +++ b/target/linux/sifiveu/base-files/lib/upgrade/platform.sh @@ -0,0 +1,86 @@ +platform_check_image() { + local diskdev partdev diff + + export_bootdevice && export_partdevice diskdev 0 || { + echo "Unable to determine upgrade device" + return 1 + } + + get_partitions "/dev/$diskdev" bootdisk + + #extract the boot sector from the image + get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b 2>/dev/null + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + + rm -f /tmp/image.bs /tmp/partmap.bootdisk /tmp/partmap.image + + if [ -n "$diff" ]; then + echo "Partition layout has changed. Full image will be written." + ask_bool 0 "Abort" && exit 1 + return 0 + fi +} + +platform_copy_config() { + local partdev + + if export_partdevice partdev 3; then + mount -t vfat -o rw,noatime "/dev/$partdev" /mnt + cp -af "$UPGRADE_BACKUP" "/mnt/$BACKUP_FILE" + umount /mnt + fi +} + +platform_do_upgrade() { + local diskdev partdev diff + + export_bootdevice && export_partdevice diskdev 0 || { + echo "Unable to determine upgrade device" + return 1 + } + + sync + + if [ "$UPGRADE_OPT_SAVE_PARTITIONS" = "1" ]; then + get_partitions "/dev/$diskdev" bootdisk + + #extract the boot sector from the image + get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + else + diff=1 + fi + + if [ -n "$diff" ]; then + get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync + + # Separate removal and addtion is necessary; otherwise, partition 1 + # will be missing if it overlaps with the old partition 2 + partx -d - "/dev/$diskdev" + partx -a - "/dev/$diskdev" + + return 0 + fi + + #iterate over each partition from the image and write it to the boot disk + while read part start size; do + if export_partdevice partdev $part; then + echo "Writing image to /dev/$partdev..." + get_image "$@" | dd of="/dev/$partdev" ibs="512" obs=1M skip="$start" count="$size" conv=fsync + else + echo "Unable to find partition $part device, skipped." + fi + done < /tmp/partmap.image + + #copy partition uuid + echo "Writing new UUID to /dev/$diskdev..." + get_image "$@" | dd of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync +} diff --git a/target/linux/sifiveu/config-5.15 b/target/linux/sifiveu/config-5.15 new file mode 100644 index 00000000000..de3fe5b46f7 --- /dev/null +++ b/target/linux/sifiveu/config-5.15 @@ -0,0 +1,362 @@ +CONFIG_64BIT=y +CONFIG_ARCH_CLOCKSOURCE_INIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +# CONFIG_ARCH_RV32I is not set +CONFIG_ARCH_RV64I=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_ATA=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_CAVIUM_PTP=y +CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y +CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC=y +CONFIG_CLK_SIFIVE=y +CONFIG_CLK_SIFIVE_PRCI=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CLZ_TAB=y +CONFIG_CMODEL_MEDANY=y +# CONFIG_CMODEL_MEDLOW is not set +CONFIG_COMMON_CLK=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_COMPAT_BRK=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_COREDUMP=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_CPU_ISOLATION=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRC7=y +CONFIG_CRC_ITU_T=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DNOTIFY=y +CONFIG_DTC=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_EDAC=y +# CONFIG_EDAC_DEBUG is not set +CONFIG_EDAC_LEGACY_SYSFS=y +CONFIG_EDAC_SIFIVE=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EFI=y +CONFIG_EFIVAR_FS=m +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_CAPSULE_LOADER is not set +# CONFIG_EFI_DISABLE_PCI_DMA is not set +CONFIG_EFI_EARLYCON=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_GENERIC_STUB=y +CONFIG_EFI_PARAMS_FROM_FDT=y +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_TEST is not set +CONFIG_ELF_CORE=y +CONFIG_ERRATA_SIFIVE=y +CONFIG_ERRATA_SIFIVE_CIP_1200=y +CONFIG_ERRATA_SIFIVE_CIP_453=y +CONFIG_EXT4_FS=y +CONFIG_FAILOVER=y +CONFIG_FHANDLE=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FONT_8x16=y +CONFIG_FONT_AUTOSELECT=y +CONFIG_FONT_SUPPORT=y +CONFIG_FPU=y +CONFIG_FRAME_POINTER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_INJECTION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_CDEV_V1=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_SIFIVE=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_HOTPLUG_PCI_SHPC=y +CONFIG_HVC_DRIVER=y +CONFIG_HVC_RISCV_SBI=y +CONFIG_HW_CONSOLE=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_OCORES=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT=y +# CONFIG_IOMMU_DEBUGFS is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IO_URING=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=y +CONFIG_KALLSYMS=y +CONFIG_KEYS=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_MACB=y +# CONFIG_MACB_PCI is not set +CONFIG_MACB_USE_HWSTAMP=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_CORE=y +CONFIG_MFD_SYSCON=y +CONFIG_MICROSEMI_PHY=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_CADENCE=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SPI=y +CONFIG_MMIOWB=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MODULE_SECTIONS=y +CONFIG_MPILIB=y +CONFIG_MQ_IOSCHED_DEADLINE=y +CONFIG_MQ_IOSCHED_KYBER=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_FAILOVER=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SELFTESTS=y +CONFIG_NLS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NR_CPUS=8 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OID_REGISTRY=y +CONFIG_PADATA=y +CONFIG_PAGE_OFFSET=0xffffffe000000000 +CONFIG_PAGE_REPORTING=y +CONFIG_PA_BITS=56 +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEAER_INJECT=m +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DPC=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_ECRC=y +CONFIG_PCIE_FU740=y +CONFIG_PCIE_PTM=y +CONFIG_PCIE_XILINX=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_ECAM=y +CONFIG_PCI_HOST_COMMON=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCI_SW_SWITCHTEC=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PHYS_RAM_BASE_FIXED is not set +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_PPS=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_PWM=y +CONFIG_PWM_SIFIVE=y +CONFIG_PWM_SYSFS=y +CONFIG_RATIONAL=y +CONFIG_RCU_TRACE=y +CONFIG_RD_GZIP=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_IRQ=y +CONFIG_REGMAP_MMIO=y +# CONFIG_RESET_ATTACK_MITIGATION is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y +CONFIG_RFS_ACCEL=y +CONFIG_RISCV=y +CONFIG_RISCV_ERRATA_ALTERNATIVE=y +CONFIG_RISCV_INTC=y +CONFIG_RISCV_ISA_C=y +CONFIG_RISCV_SBI=y +CONFIG_RISCV_SBI_V01=y +CONFIG_RISCV_TIMER=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_EFI is not set +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCHED_DEBUG=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_EXAR=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_SIFIVE=y +CONFIG_SERIAL_SIFIVE_CONSOLE=y +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SG_POOL=y +CONFIG_SIFIVE_L2=y +CONFIG_SIFIVE_PLIC=y +CONFIG_SLUB_DEBUG=y +CONFIG_SMP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +# CONFIG_SOC_MICROCHIP_POLARFIRE is not set +CONFIG_SOC_SIFIVE=y +# CONFIG_SOC_VIRT is not set +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_SIFIVE=y +CONFIG_SRCU=y +CONFIG_STACKTRACE=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFB=y +# CONFIG_SYSFB_SIMPLEFB is not set +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TRACE_CLOCK=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_TUNE_GENERIC=y +CONFIG_UCS2_STRING=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +CONFIG_USB_EHCI_PCI=y +CONFIG_USB_HID=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_PCI=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +# CONFIG_USB_UHCI_HCD is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PCI=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_VA_BITS=39 +CONFIG_VFAT_FS=y +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +CONFIG_VMAP_STACK=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_WATCHDOG_CORE=y +CONFIG_XPS=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/sifiveu/generic/target.mk b/target/linux/sifiveu/generic/target.mk new file mode 100644 index 00000000000..f5cb1fb19b9 --- /dev/null +++ b/target/linux/sifiveu/generic/target.mk @@ -0,0 +1 @@ +BOARDNAME:=Generic diff --git a/target/linux/sifiveu/image/Config.in b/target/linux/sifiveu/image/Config.in new file mode 100644 index 00000000000..640869b92c8 --- /dev/null +++ b/target/linux/sifiveu/image/Config.in @@ -0,0 +1,5 @@ +config SIFIVEU_SD_BOOT_PARTSIZE + int "Boot (SD Card) filesystem partition size (in MB)" + depends on TARGET_sifiveu + default 32 + diff --git a/target/linux/sifiveu/image/Makefile b/target/linux/sifiveu/image/Makefile new file mode 100644 index 00000000000..ef3b13eeec4 --- /dev/null +++ b/target/linux/sifiveu/image/Makefile @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 Toco Technologies +# +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +FAT32_BLOCK_SIZE=1024 +FAT32_BLOCKS=$(shell echo $$(($(CONFIG_SIFIVEU_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE)))) + +KERNEL_LOADADDR:=0x80200000 + +define Build/riscv-sdcard + rm -f $@.boot #$(KDIR_TMP)/$(IMG_PREFIX)-$(PROFILE)-boot.img + mkfs.fat $@.boot -C $(FAT32_BLOCKS) + + mcopy -i $@.boot $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-boot.scr ::boot.scr + mcopy -i $@.boot $(DTS_DIR)/$(DEVICE_DTS).dtb ::dtb + mcopy -i $@.boot $(IMAGE_KERNEL) ::Image + + ./gen_sifiveu_sdcard_img.sh \ + $@ \ + $@.boot \ + $(IMAGE_ROOTFS) \ + $(CONFIG_SIFIVEU_SD_BOOT_PARTSIZE) \ + $(CONFIG_TARGET_ROOTFS_PARTSIZE) \ + $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-u-boot.itb \ + $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-u-boot.itb-spl +endef + +define Device/Default + PROFILES := Default + KERNEL_NAME := Image + KERNEL := kernel-bin | libdeflate-gzip + IMAGES := sdcard.img.gz + IMAGE/sdcard.img.gz := riscv-sdcard | append-metadata | gzip +endef + +define Device/sifive_unleashed + DEVICE_VENDOR := SiFive + DEVICE_MODEL := Unleashed (FU540) + DEVICE_DTS := sifive/hifive-unleashed-a00 + UBOOT := sifive_unleashed +endef +TARGET_DEVICES += sifive_unleashed + +define Device/sifive_unmatched + DEVICE_VENDOR := SiFive + DEVICE_MODEL := Unmatched (FU740) + DEVICE_DTS := sifive/hifive-unmatched-a00 + DEVICE_PACKAGES += kmod-eeprom-at24 kmod-hwmon-lm90 + UBOOT := sifive_unmatched +endef +TARGET_DEVICES += sifive_unmatched + +$(eval $(call BuildImage)) diff --git a/target/linux/sifiveu/image/gen_sifiveu_sdcard_img.sh b/target/linux/sifiveu/image/gen_sifiveu_sdcard_img.sh new file mode 100755 index 00000000000..172f62485d8 --- /dev/null +++ b/target/linux/sifiveu/image/gen_sifiveu_sdcard_img.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 OpenWrt.org +# + +set -ex +[ $# -eq 7 ] || { + echo "SYNTAX: $0 " + exit 1 +} + +OUTPUT="$1" +BOOTFS="$2" +ROOTFS="$3" +BOOTFSSIZE="$4" +ROOTFSSIZE="$5" +UBOOT="$6" +UBOOT_SPL="$7" + +set $(ptgen -o $OUTPUT -v -g -T sifiveu_spl -N loader1 -p 1024 -T sifiveu_uboot -N loader2 -p 4096 -t ef -N boot -p ${BOOTFSSIZE}M -N rootfs -p ${ROOTFSSIZE}M) + +ROOTFSOFFSET=$(($7 / 512)) + +dd bs=512 if="$UBOOT_SPL" of="$OUTPUT" seek=34 conv=notrunc +dd bs=512 if="$UBOOT" of="$OUTPUT" seek=2082 conv=notrunc +dd bs=512 if="$BOOTFS" of="$OUTPUT" seek=10274 conv=notrunc +dd bs=512 if="$ROOTFS" of="$OUTPUT" seek=${ROOTFSOFFSET} conv=notrunc diff --git a/target/linux/sifiveu/patches-5.15/0001-riscv-sifive-fu740-cpu-1-2-3-4-set-compatible-to-sif.patch b/target/linux/sifiveu/patches-5.15/0001-riscv-sifive-fu740-cpu-1-2-3-4-set-compatible-to-sif.patch new file mode 100644 index 00000000000..9a1c9681390 --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0001-riscv-sifive-fu740-cpu-1-2-3-4-set-compatible-to-sif.patch @@ -0,0 +1,49 @@ +From ab5c8f5492cce16ff2104393e2f1fa64a3ff6e88 Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Wed, 17 Feb 2021 06:06:14 -0800 +Subject: [PATCH 1/7] riscv: sifive: fu740: cpu{1,2,3,4} set compatible to + sifive,u74-mc + +Signed-off-by: David Abdurachmanov +--- + arch/riscv/boot/dts/sifive/fu740-c000.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi ++++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi +@@ -39,7 +39,7 @@ + }; + }; + cpu1: cpu@1 { +- compatible = "sifive,bullet0", "riscv"; ++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; +@@ -63,7 +63,7 @@ + }; + }; + cpu2: cpu@2 { +- compatible = "sifive,bullet0", "riscv"; ++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; +@@ -87,7 +87,7 @@ + }; + }; + cpu3: cpu@3 { +- compatible = "sifive,bullet0", "riscv"; ++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; +@@ -111,7 +111,7 @@ + }; + }; + cpu4: cpu@4 { +- compatible = "sifive,bullet0", "riscv"; ++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; diff --git a/target/linux/sifiveu/patches-5.15/0002-riscv-sifive-unmatched-update-regulators-values.patch b/target/linux/sifiveu/patches-5.15/0002-riscv-sifive-unmatched-update-regulators-values.patch new file mode 100644 index 00000000000..ac316e9d9b9 --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0002-riscv-sifive-unmatched-update-regulators-values.patch @@ -0,0 +1,104 @@ +From 657819ff477dd73cd71075609698aa57ba098d8c Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Wed, 15 Sep 2021 07:10:02 -0700 +Subject: [PATCH 2/7] riscv: sifive: unmatched: update regulators values + +These are the regulators values from the schematics for Rev3{A,B} boards. + +Note this is not fully correct as bcore1/bcore2 and bmem/bio are merged, but +it's only supported in v5.15 kernel. See: + +541ee8f640327f951e7039278057827322231ab0 ("regulator: da9063: Add support for +full-current mode.") + +This will be changed for v5.15 kernel based on the patch above. + +Signed-off-by: David Abdurachmanov +--- + .../riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 32 +++++++++++----------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts ++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +@@ -73,16 +73,16 @@ + + regulators { + vdd_bcore1: bcore1 { +- regulator-min-microvolt = <900000>; +- regulator-max-microvolt = <900000>; ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1050000>; + regulator-min-microamp = <5000000>; + regulator-max-microamp = <5000000>; + regulator-always-on; + }; + + vdd_bcore2: bcore2 { +- regulator-min-microvolt = <900000>; +- regulator-max-microvolt = <900000>; ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1050000>; + regulator-min-microamp = <5000000>; + regulator-max-microamp = <5000000>; + regulator-always-on; +@@ -137,48 +137,48 @@ + }; + + vdd_ldo3: ldo3 { +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-always-on; + }; + + vdd_ldo4: ldo4 { +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-always-on; + }; + + vdd_ldo5: ldo5 { +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; + regulator-min-microamp = <100000>; + regulator-max-microamp = <100000>; + regulator-always-on; + }; + + vdd_ldo6: ldo6 { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-always-on; + }; + + vdd_ldo7: ldo7 { +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-always-on; + }; + + vdd_ldo8: ldo8 { +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-always-on; diff --git a/target/linux/sifiveu/patches-5.15/0003-riscv-sifive-unmatched-define-PWM-LEDs.patch b/target/linux/sifiveu/patches-5.15/0003-riscv-sifive-unmatched-define-PWM-LEDs.patch new file mode 100644 index 00000000000..661e15905f9 --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0003-riscv-sifive-unmatched-define-PWM-LEDs.patch @@ -0,0 +1,69 @@ +From 2c2d8ac8c124a2938c9326c14b2dffd46d76b4a8 Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Mon, 13 Sep 2021 02:15:37 -0700 +Subject: [PATCH 3/7] riscv: sifive: unmatched: define PWM LEDs + +Add D2 (RGB) and D12 (green) LEDs for SiFive Unmatched board. + +Signed-off-by: David Abdurachmanov +--- + .../riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 41 ++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts ++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +@@ -4,6 +4,8 @@ + #include "fu740-c000.dtsi" + #include + #include ++#include ++#include + + /* Clock frequency (in Hz) of the PCB crystal for rtcclk */ + #define RTCCLK_FREQ 1000000 +@@ -31,6 +33,45 @@ + soc { + }; + ++ pwmleds { ++ compatible = "pwm-leds"; ++ green-d12 { ++ label = "green:d12"; ++ color = ; ++ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; ++ active-low = <1>; ++ max-brightness = <255>; ++ linux,default-trigger = "none"; ++ }; ++ ++ green-d2 { ++ label = "green:d2"; ++ color = ; ++ pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; ++ active-low = <1>; ++ max-brightness = <255>; ++ linux,default-trigger = "none"; ++ }; ++ ++ red-d2 { ++ label = "red:d2"; ++ color = ; ++ pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; ++ active-low = <1>; ++ max-brightness = <255>; ++ linux,default-trigger = "none"; ++ }; ++ ++ blue-d2 { ++ label = "blue:d2"; ++ color = ; ++ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; ++ active-low = <1>; ++ max-brightness = <255>; ++ linux,default-trigger = "none"; ++ }; ++ }; ++ + hfclk: hfclk { + #clock-cells = <0>; + compatible = "fixed-clock"; diff --git a/target/linux/sifiveu/patches-5.15/0004-riscv-sifive-unmatched-add-gpio-poweroff-node.patch b/target/linux/sifiveu/patches-5.15/0004-riscv-sifive-unmatched-add-gpio-poweroff-node.patch new file mode 100644 index 00000000000..6d09628cb3b --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0004-riscv-sifive-unmatched-add-gpio-poweroff-node.patch @@ -0,0 +1,26 @@ +From 14ede57943bc4209755d08daf93ac7be967d7fbe Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Mon, 13 Sep 2021 02:18:30 -0700 +Subject: [PATCH 4/7] riscv: sifive: unmatched: add gpio-poweroff node + +Add gpio-poweroff node to allow powering off the system. + +Signed-off-by: David Abdurachmanov +--- + arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts ++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +@@ -85,6 +85,11 @@ + clock-frequency = ; + clock-output-names = "rtcclk"; + }; ++ ++ gpio-poweroff { ++ compatible = "gpio-poweroff"; ++ gpios = <&gpio 2 GPIO_ACTIVE_LOW>; ++ }; + }; + + &uart0 { diff --git a/target/linux/sifiveu/patches-5.15/0005-riscv-sifive-unleashed-define-opp-table-cpufreq.patch b/target/linux/sifiveu/patches-5.15/0005-riscv-sifive-unleashed-define-opp-table-cpufreq.patch new file mode 100644 index 00000000000..b5779e09cb0 --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0005-riscv-sifive-unleashed-define-opp-table-cpufreq.patch @@ -0,0 +1,116 @@ +From d3cf2859a056273400fbdf9d389b75750ff6ca5e Mon Sep 17 00:00:00 2001 +From: David Abdurachmanov +Date: Fri, 14 May 2021 05:27:51 -0700 +Subject: [PATCH 6/7] riscv: sifive: unleashed: define opp table (cpufreq) + +Source: https://github.com/sifive/riscv-linux/commits/dev/paulw/cpufreq-dt-aloe-v5.3-rc4 + +Signed-off-by: David Abdurachmanov +--- + arch/riscv/Kconfig | 8 +++++ + arch/riscv/boot/dts/sifive/fu540-c000.dtsi | 5 ++++ + .../riscv/boot/dts/sifive/hifive-unleashed-a00.dts | 34 ++++++++++++++++++++++ + 3 files changed, 47 insertions(+) + +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -565,6 +565,14 @@ config BUILTIN_DTB + depends on OF + default y if XIP_KERNEL + ++menu "CPU Power Management" ++ ++source "drivers/cpuidle/Kconfig" ++ ++source "drivers/cpufreq/Kconfig" ++ ++endmenu ++ + menu "Power management options" + + source "kernel/power/Kconfig" +--- a/arch/riscv/boot/dts/sifive/fu540-c000.dtsi ++++ b/arch/riscv/boot/dts/sifive/fu540-c000.dtsi +@@ -30,6 +30,7 @@ + i-cache-size = <16384>; + reg = <0>; + riscv,isa = "rv64imac"; ++ clocks = <&prci PRCI_CLK_COREPLL>; + status = "disabled"; + cpu0_intc: interrupt-controller { + #interrupt-cells = <1>; +@@ -54,6 +55,7 @@ + reg = <1>; + riscv,isa = "rv64imafdc"; + tlb-split; ++ clocks = <&prci PRCI_CLK_COREPLL>; + next-level-cache = <&l2cache>; + cpu1_intc: interrupt-controller { + #interrupt-cells = <1>; +@@ -78,6 +80,7 @@ + reg = <2>; + riscv,isa = "rv64imafdc"; + tlb-split; ++ clocks = <&prci PRCI_CLK_COREPLL>; + next-level-cache = <&l2cache>; + cpu2_intc: interrupt-controller { + #interrupt-cells = <1>; +@@ -102,6 +105,7 @@ + reg = <3>; + riscv,isa = "rv64imafdc"; + tlb-split; ++ clocks = <&prci PRCI_CLK_COREPLL>; + next-level-cache = <&l2cache>; + cpu3_intc: interrupt-controller { + #interrupt-cells = <1>; +@@ -126,6 +130,7 @@ + reg = <4>; + riscv,isa = "rv64imafdc"; + tlb-split; ++ clocks = <&prci PRCI_CLK_COREPLL>; + next-level-cache = <&l2cache>; + cpu4_intc: interrupt-controller { + #interrupt-cells = <1>; +--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts ++++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts +@@ -84,6 +84,40 @@ + label = "d4"; + }; + }; ++ ++ fu540_c000_opp_table: opp-table { ++ compatible = "operating-points-v2"; ++ opp-shared; ++ ++ opp-350000000 { ++ opp-hz = /bits/ 64 <350000000>; ++ }; ++ opp-700000000 { ++ opp-hz = /bits/ 64 <700000000>; ++ }; ++ opp-999999999 { ++ opp-hz = /bits/ 64 <999999999>; ++ }; ++ opp-1400000000 { ++ opp-hz = /bits/ 64 <1400000000>; ++ }; ++ }; ++}; ++ ++&cpu0 { ++ operating-points-v2 = <&fu540_c000_opp_table>; ++}; ++&cpu1 { ++ operating-points-v2 = <&fu540_c000_opp_table>; ++}; ++&cpu2 { ++ operating-points-v2 = <&fu540_c000_opp_table>; ++}; ++&cpu3 { ++ operating-points-v2 = <&fu540_c000_opp_table>; ++}; ++&cpu4 { ++ operating-points-v2 = <&fu540_c000_opp_table>; + }; + + &uart0 { diff --git a/target/linux/sifiveu/patches-5.15/0006-riscv-sbi-srst-support.patch b/target/linux/sifiveu/patches-5.15/0006-riscv-sbi-srst-support.patch new file mode 100644 index 00000000000..409001bcfa8 --- /dev/null +++ b/target/linux/sifiveu/patches-5.15/0006-riscv-sbi-srst-support.patch @@ -0,0 +1,301 @@ +From mboxrd@z Thu Jan 1 00:00:00 1970 +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=-21.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, + DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, + INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,MSGID_FROM_MTA_HEADER, + SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT 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 9A34CC48BCD + for ; Wed, 9 Jun 2021 12:50:08 +0000 (UTC) +Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) + (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) + (No client certificate requested) + by mail.kernel.org (Postfix) with ESMTPS id 69795611C9 + for ; Wed, 9 Jun 2021 12:50:08 +0000 (UTC) +DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 69795611C9 +Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=wdc.com +Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org +DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; + d=lists.infradead.org; s=bombadil.20210309; h=Sender: + Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: + List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: + Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: + Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: + List-Owner; bh=64gRxQ9bX8C6wjLq0KuJ2lv98bQdXijt0LPnNpch3NU=; b=rgeSpoSWQ+Nca2 + 9PLsgI7dOYVdTu48CyVJStiizsvIvVFN2rBAgELHF2nRCCtoSiPMxgcpCKtDcm7sh9lC8AblCoBjN + LXiPRHVYJAcRNiWiQ0qOTqHdTbezFdzSjNOs6drbaiI4B8AZtychw1hP+ubsb5czAaz6510OEVct/ + h5M4Tlljcn/WIyulBd/tnuUOZPT0XL6rb2+TvRQvjXDBFHN+bWqP8OjXKnE1FTvy5MF8OTlUgI6wr + 3f4t/eS/PPbtXRD5raJzEwEQLJ6XY6NJABs40tKpWZNuUaqTfmonNdbP9y1htWhByhsAk+fw5WK/C + /KocvM6IzPmqGIBWcTdQ==; +Received: from localhost ([::1] helo=bombadil.infradead.org) + by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) + id 1lqxeS-00Do8i-Hj; Wed, 09 Jun 2021 12:49:44 +0000 +Received: from esa4.hgst.iphmx.com ([216.71.154.42]) + by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) + id 1lqx5h-00DbCQ-K0 + for linux-riscv@lists.infradead.org; Wed, 09 Jun 2021 12:13:51 +0000 +DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; + d=wdc.com; i=@wdc.com; q=dns/txt; s=dkim.wdc.com; + t=1623240829; x=1654776829; + h=from:to:cc:subject:date:message-id:in-reply-to: + references:content-transfer-encoding:mime-version; + bh=s4va/Owvq7MQI8rUsD/e02RmaYfJNzYNcxlXspGRY7g=; + b=G5GD9eN+rv669E7jyRnRAt0jw83CxhIrSiDIjXuPmhWyMhKeQGD7ACRm + ii6o0zmOREhGihtwB6X/xpY/2ZvK+cxcHmJXa+Ykyn8QN+/YKFtg3svfj + eiTN7U/mEozCoGNd1wXu59RQj11Xz60DN/qEUlYFaL6SjukUgifFVgbvG + uUj8AM8+xf1jKHi3Q/6nVPpJX8uiW/NPFHrwI8hxUwYr9viQwxXvc7FNr + fR8bH2c/HiGacGYEHosgP0WT//d9Huqn4JNINvjidK4ZSJ74cXlr8KwMG + 8snmfx4UjEWMhK1lCYalJEU7nxXFfih/6DMuFRorETpWQ+424BAKUJdDH Q==; +IronPort-SDR: pYhRsIZkhfmi45K4HfnZj39kxfUGpxs1e+q+Wh8kDE+ySh35HkJaaUcpP04mb7VeIVtPRx/h6Q + imv6sn8fYo/V8ezHAq4jpd1QadqInKi1ubLnCE3Zy7GnhVBepoV6FbI14Y01V+5QIUwYdFNcGG + RsxDOTQyU5AljH0Rc6WkrpdVf5jsrXXMddmlDdi6QsfKGy7MwQ/NYojNIqyLhRSgu5w2uTIE7X + atSbjb8j2a+EJUY0WgYTGfNHKCdQLAhjcsWZgU7Iu0vSaBU6A7seCkqun24dvF/zYuzEj7wedD + TvE= +X-IronPort-AV: E=Sophos;i="5.83,260,1616428800"; d="scan'208";a="170575129" +Received: from mail-dm6nam12lp2169.outbound.protection.outlook.com (HELO + NAM12-DM6-obe.outbound.protection.outlook.com) ([104.47.59.169]) + by ob1.hgst.iphmx.com with ESMTP; 09 Jun 2021 20:13:48 +0800 +ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; + b=hrlr4Qi66FmbQW45zI7QeA9nEYSvxO+tR++vUyFHxjRqnr6WtjKgHM8hPpp8oHXaK82U0+KQUc2+WjLbe8LihexXFQs5zRwBDwuArmkKt85cL3utD3OBzTkr4A9ZhRS5mzztnn9kvTFNplPjSydXPetJQIZ9WKmihJrdeaGQ+zQ//6TdDWVpLyBbqiBVUbUwlKQbpbbfHvzQCHYQbIiUcGn4vaSXYp2Xp1Z5yYVtrfDK+TemKG/8fKQoJjg/tdmDtQ97Cgw7nX6Oc9kdmoTIxFilMy4XjPciPcNOPdLmboGCt6+TMBeftLc1VFNnr7PwuxOogv4I7eJ/P9UaK57k5A== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; + s=arcselector9901; + h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; + bh=Ibi36Vj2s7bu56DZaU1uSl4xNA3OB2FHlI/rBKn+8xo=; + b=CE3o+0GFLPMQuw0AK1IRf/vX00diXsjayn0MmpS7ntSVXgxXIAPT9aDtk7x0NovJBTk2LHI5Mtxvz6SwVnJzUqZmNsXUktEj5Iwdd8EPIIxgCOjugo/6WC0FqaFKNvJB4hQ0tjFxv+J5DexSJ8+mPx6Ucr4DwtUXrCWzOeXyF5YK68mU5FgttbyutW3CGsGkPgaPAdOxXOgJqyYu8X25unmzG12Jq2xC4oVKsbA+RfDiaMKm97q2Bhy+LcgJNS6/ktlFKSOVu1HQ0POYgba3mtldN3vg73wLbxrfsdoe4261aJpkM05GJFDzdTNp4t3rEGhNuLR1+8OmKfLlPeSU9w== +ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass + smtp.mailfrom=wdc.com; dmarc=pass action=none header.from=wdc.com; dkim=pass + header.d=wdc.com; arc=none +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=sharedspace.onmicrosoft.com; s=selector2-sharedspace-onmicrosoft-com; + h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; + bh=Ibi36Vj2s7bu56DZaU1uSl4xNA3OB2FHlI/rBKn+8xo=; + b=Z76YsVxTHK6/ta19C5vwaBPYmtDa2GIM/ml4myQZfIaHFNzXPzZ+PFcYy/Xf1Ixd0GZHcuSZQYgs/SPHWATh+rOWBAislGv1zmSAG/g0tiDckB8WaCwh1e3qGW4ZmUTmAU7dxB0vn0pRSLTnc1hdCUZ73buM78qo9qwsQZv41d0= +Authentication-Results: dabbelt.com; dkim=none (message not signed) + header.d=none;dabbelt.com; dmarc=none action=none header.from=wdc.com; +Received: from CO6PR04MB7812.namprd04.prod.outlook.com (2603:10b6:303:138::6) + by CO6PR04MB7794.namprd04.prod.outlook.com (2603:10b6:303:13f::7) + with Microsoft SMTP Server (version=TLS1_2, + cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4219.21; Wed, 9 Jun + 2021 12:13:47 +0000 +Received: from CO6PR04MB7812.namprd04.prod.outlook.com + ([fe80::a153:b7f8:c87f:89f8]) by CO6PR04MB7812.namprd04.prod.outlook.com + ([fe80::a153:b7f8:c87f:89f8%9]) with mapi id 15.20.4219.021; Wed, 9 Jun 2021 + 12:13:47 +0000 +From: Anup Patel +To: Palmer Dabbelt , + Palmer Dabbelt , + Paul Walmsley , Albert Ou +Cc: Atish Patra , + Alistair Francis , + Anup Patel , linux-riscv@lists.infradead.org, + linux-kernel@vger.kernel.org, Anup Patel +Subject: [PATCH v7 1/1] RISC-V: Use SBI SRST extension when available +Date: Wed, 9 Jun 2021 17:43:22 +0530 +Message-Id: <20210609121322.3058-2-anup.patel@wdc.com> +X-Mailer: git-send-email 2.25.1 +In-Reply-To: <20210609121322.3058-1-anup.patel@wdc.com> +References: <20210609121322.3058-1-anup.patel@wdc.com> +X-Originating-IP: [122.172.176.125] +X-ClientProxiedBy: MA1PR0101CA0036.INDPRD01.PROD.OUTLOOK.COM + (2603:1096:a00:22::22) To CO6PR04MB7812.namprd04.prod.outlook.com + (2603:10b6:303:138::6) +MIME-Version: 1.0 +X-MS-Exchange-MessageSentRepresentingType: 1 +Received: from wdc.com (122.172.176.125) by + MA1PR0101CA0036.INDPRD01.PROD.OUTLOOK.COM (2603:1096:a00:22::22) with + Microsoft SMTP Server (version=TLS1_2, + cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4219.21 via Frontend + Transport; Wed, 9 Jun 2021 12:13:44 +0000 +X-MS-PublicTrafficType: Email +X-MS-Office365-Filtering-Correlation-Id: 17406ef0-e8d7-4dc3-eee9-08d92b40085b +X-MS-TrafficTypeDiagnostic: CO6PR04MB7794: +X-MS-Exchange-Transport-Forked: True +X-Microsoft-Antispam-PRVS: +WDCIPOUTBOUND: EOP-TRUE +X-MS-Oob-TLC-OOBClassifiers: OLM:2887; +X-MS-Exchange-SenderADCheck: 1 +X-Microsoft-Antispam: BCL:0; +X-Microsoft-Antispam-Message-Info: IxB9oKL9LkeXCQ7mZ1A5qIcKlICr/TPZ/8V0ErM5hbqnvfK6Mf0mQL0tqqlJAOvLpCEVIyX7FllGqSlWsNG3ik/WbbDYQb9wAFCuFSAlGAeGppnJjJf0zfDAmp4NONB7kshKqtUYfGltTHTkV4ni+VEwWf/Q3T4vA0k3Jkt34iZFi9tOsSHkSWxPTsQyviBdCp3/36ZCVhYs6bXkf8sh0sA4Ql/l8t2zpcEUwjAm14ie3hOUBEp1W9qOz6StmR4xyl+zy49U38byeHu5XDF/qFT8FI4WclwFwxbDeTm8cU7MMg4D0xeR0Ytm2wVrgAdiapgQYLmxPIjIG96TRTbCupyuaJXmYcI6/x27PtiQYFwcpbRUjXDKRVX2WW74WHm88OOlTexD/OsbGHD6PVnc+InniK38yNcx06U9fIkDGSYWrJqLysALlO0V5gfkc35Fhttum638ES0S2sldGkFufM372EZeooczK7jeLMpoOTAnaLtPdTCGsHnnEDDbOK7NiptQlrLMhrNQ/70harAMmB6Vvdl+jvJi34DsuX+57WeQU8Ya1cyVxzFkWX2DwvPRsAnp/VNHzeQLc5MAIUYpwQvkJBcqihYMKrLMNOT94HmxBYmY2bcW/K9fXrPQ/whyJ5HoQuxydeiy7+QKg6FWnhguTACaaTKGKKIDIlfYA5FXYlZOaQ2iJFtiZP1GbxQnDEwz4SfGgCgBdiwqFm1NfGG7wRhqQa/Kgp3jBTHwaysCwcWu/Xdz/yiCV4lfQD/PTiOr5hmtld29G7WVDy9m6Q== +X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; + IPV:NLI; SFV:NSPM; H:CO6PR04MB7812.namprd04.prod.outlook.com; PTR:; CAT:NONE; + SFS:(4636009)(396003)(39860400002)(136003)(346002)(366004)(376002)(8676002)(316002)(55236004)(38100700002)(38350700002)(110136005)(86362001)(54906003)(4326008)(956004)(966005)(478600001)(26005)(186003)(55016002)(8936002)(2616005)(7696005)(52116002)(16526019)(5660300002)(8886007)(6666004)(1076003)(66476007)(66556008)(36756003)(66946007)(83380400001)(2906002)(44832011); + DIR:OUT; SFP:1102; +X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 +X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?2kfiFAcyFkAMcwkoCUqJGxdVnwsuTm5WWTXA4Pq0ZNAjWsfYI4MNugTA9f8C?= + =?us-ascii?Q?WaQrYEizTkvIlgRDqeKVRS0vaMaeU9XaSzO6UAJMnd0jY3BzkJgKUU9xj1aU?= + =?us-ascii?Q?vmwXKxCS+vZ2VKgLifaU1JCeemiJqV9aW+6AJEycq722bz9yXmcaJsaHWtX3?= + =?us-ascii?Q?4sSoLeVPvfzwzDZEEoGsZZi1G7inY2imGEgY/r3m5/qYvavJQ3An4e4sjEqZ?= + =?us-ascii?Q?Z4a4FAd/6c3X8cjmNwGvgWoAIM5WaJYFQe30MQ79alCUfZyiKB4tR0+5OGFd?= + =?us-ascii?Q?P/rQ169Z644JNKKcEabikpL7qZZJ6OMPTS7XR9x/7GzWOJ7soV3/3I2tfCdi?= + =?us-ascii?Q?xVsOK1DRF4y6gi5udvnb+Uu1U5wC1NlT0U/+TrnTSeY/IuLmMgFUysw+fQ+D?= + =?us-ascii?Q?DIVN44TKrMoEZKx9SKcx4jYpUGYvaCH4sVOAx3zWQC0Oz1Nz3/a/isywpQW9?= + =?us-ascii?Q?1I1kl/2N97K0EoWIPf6qPjyLVWXg1dOHfk6SjNW64JIIPUNnM3h7k2igDX3o?= + =?us-ascii?Q?d7lWyFfzoWhNC7opS71uzta+ti8aHxo+xzvYvf2wLb+fdyEP9t+oQVrEQYIW?= + =?us-ascii?Q?rAUKPqjEfAZOYBB28SaabfVt/QF6hFfV/0yJ/JV/Ie8ivC1t6iO+QZQscV7K?= + =?us-ascii?Q?d4Pg+xVSE+m+LsgNwO36cTTe6hSLBPnWU1NMOW2cxTRKGm0Lwd2HyjyKTBMD?= + =?us-ascii?Q?BcnKo9GXAVgOAGCG5cwEBN76q6sXxbWy0pjni3O2bLYBg4CIYCB/JNzOIfE0?= + =?us-ascii?Q?Toz6Qwc4aw5NxRLqz9IygGT6ZunRVUWUsgJrIt5U20elX+lRmtX1cqrQNTON?= + =?us-ascii?Q?ZHzuAI587pB0zK4EiS25hc9C8RtwjlY67heuMsYZDww5TU+NV3+0WN1/NrCX?= + =?us-ascii?Q?3kB7O188tvm1sWVhCaC6hk9s19nKGRgMS5OHXTMxhmyw1Dn/zorMYff3r9ZE?= + =?us-ascii?Q?sNiBI7fwru/Jsxt2/jNCpFaEYUa9JkrdSse76BXo/UxLALnxO3bzpym3Dq+T?= + =?us-ascii?Q?s4uEA8UncKM0e+Mhp9hW1c3DR61Qjj8wb+LV3XB0qYK/1rHs8IDdJ97tw1fp?= + =?us-ascii?Q?Ux9SlgS9YE2bEp6wxcX6TpA5DoYjqdlK50/4/DZ3YTXWlPTaQbt/j36TbEgZ?= + =?us-ascii?Q?hhIV08WX7EDjBz1QrFRppEtBghOJikHLdvPo6GnZkHNQ9cxaa8Jrk0iypK4d?= + =?us-ascii?Q?bMj47kiugWCGY+ZW2ioGV1GgH1aZEvAukQgTiAAiyGU83td11Q5Pv2N5ytvk?= + =?us-ascii?Q?i+Ux/DUoeU4VyqYnb69asjdyKI5RzIxQPdQAQ7x/TBlyPp/Yj2/v31b6lCYT?= + =?us-ascii?Q?b1iYdeIKK+6I6A+e/EUnPOKC?= +X-OriginatorOrg: wdc.com +X-MS-Exchange-CrossTenant-Network-Message-Id: 17406ef0-e8d7-4dc3-eee9-08d92b40085b +X-MS-Exchange-CrossTenant-AuthSource: CO6PR04MB7812.namprd04.prod.outlook.com +X-MS-Exchange-CrossTenant-AuthAs: Internal +X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 Jun 2021 12:13:47.1304 (UTC) +X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted +X-MS-Exchange-CrossTenant-Id: b61c8803-16f3-4c35-9b17-6f65f441df86 +X-MS-Exchange-CrossTenant-MailboxType: HOSTED +X-MS-Exchange-CrossTenant-UserPrincipalName: rHld9c5jovIZF30ZL04ehEJ81O0isWetsUM3vlp/0cN1LoJ5z8guKzUTANDGVGM0Eua+2cZ1jQGTC49NwWH4hA== +X-MS-Exchange-Transport-CrossTenantHeadersStamped: CO6PR04MB7794 +X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 +X-CRM114-CacheID: sfid-20210609_051349_766689_30430D67 +X-CRM114-Status: GOOD ( 15.29 ) +X-BeenThere: linux-riscv@lists.infradead.org +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit +Sender: "linux-riscv" +Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org + +The SBI SRST extension provides a standard way to poweroff and +reboot the system irrespective to whether Linux RISC-V S-mode +is running natively (HS-mode) or inside Guest/VM (VS-mode). + +The SBI SRST extension is available in the SBI v0.3 specification. +(Refer, https://github.com/riscv/riscv-sbi-doc/releases/tag/v0.3.0-rc1) + +This patch extends Linux RISC-V SBI implementation to detect +and use SBI SRST extension. + +Signed-off-by: Anup Patel +Reviewed-by: Atish Patra +--- + arch/riscv/include/asm/sbi.h | 24 ++++++++++++++++++++++++ + arch/riscv/kernel/sbi.c | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 59 insertions(+) + +--- a/arch/riscv/include/asm/sbi.h ++++ b/arch/riscv/include/asm/sbi.h +@@ -27,6 +27,7 @@ enum sbi_ext_id { + SBI_EXT_IPI = 0x735049, + SBI_EXT_RFENCE = 0x52464E43, + SBI_EXT_HSM = 0x48534D, ++ SBI_EXT_SRST = 0x53525354, + }; + + enum sbi_ext_base_fid { +@@ -70,6 +71,21 @@ enum sbi_hsm_hart_status { + SBI_HSM_HART_STATUS_STOP_PENDING, + }; + ++enum sbi_ext_srst_fid { ++ SBI_EXT_SRST_RESET = 0, ++}; ++ ++enum sbi_srst_reset_type { ++ SBI_SRST_RESET_TYPE_SHUTDOWN = 0, ++ SBI_SRST_RESET_TYPE_COLD_REBOOT, ++ SBI_SRST_RESET_TYPE_WARM_REBOOT, ++}; ++ ++enum sbi_srst_reset_reason { ++ SBI_SRST_RESET_REASON_NONE = 0, ++ SBI_SRST_RESET_REASON_SYS_FAILURE, ++}; ++ + #define SBI_SPEC_VERSION_DEFAULT 0x1 + #define SBI_SPEC_VERSION_MAJOR_SHIFT 24 + #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f +@@ -148,6 +164,14 @@ static inline unsigned long sbi_minor_ve + return sbi_spec_version & SBI_SPEC_VERSION_MINOR_MASK; + } + ++/* Make SBI version */ ++static inline unsigned long sbi_mk_version(unsigned long major, ++ unsigned long minor) ++{ ++ return ((major & SBI_SPEC_VERSION_MAJOR_MASK) << ++ SBI_SPEC_VERSION_MAJOR_SHIFT) | minor; ++} ++ + int sbi_err_map_linux_errno(int err); + #else /* CONFIG_RISCV_SBI */ + static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; } +--- a/arch/riscv/kernel/sbi.c ++++ b/arch/riscv/kernel/sbi.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + +@@ -501,6 +502,32 @@ int sbi_remote_hfence_vvma_asid(const un + } + EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid); + ++static void sbi_srst_reset(unsigned long type, unsigned long reason) ++{ ++ sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason, ++ 0, 0, 0, 0); ++ pr_warn("%s: type=0x%lx reason=0x%lx failed\n", ++ __func__, type, reason); ++} ++ ++static int sbi_srst_reboot(struct notifier_block *this, ++ unsigned long mode, void *cmd) ++{ ++ sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ? ++ SBI_SRST_RESET_TYPE_WARM_REBOOT : ++ SBI_SRST_RESET_TYPE_COLD_REBOOT, ++ SBI_SRST_RESET_REASON_NONE); ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block sbi_srst_reboot_nb; ++ ++static void sbi_srst_power_off(void) ++{ ++ sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, ++ SBI_SRST_RESET_REASON_NONE); ++} ++ + /** + * sbi_probe_extension() - Check if an SBI extension ID is supported or not. + * @extid: The extension ID to be probed. +@@ -608,6 +635,14 @@ void __init sbi_init(void) + } else { + __sbi_rfence = __sbi_rfence_v01; + } ++ if ((sbi_spec_version >= sbi_mk_version(0, 3)) && ++ (sbi_probe_extension(SBI_EXT_SRST) > 0)) { ++ pr_info("SBI SRST extension detected\n"); ++ pm_power_off = sbi_srst_power_off; ++ sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot; ++ sbi_srst_reboot_nb.priority = 192; ++ register_restart_handler(&sbi_srst_reboot_nb); ++ } + } else { + __sbi_set_timer = __sbi_set_timer_v01; + __sbi_send_ipi = __sbi_send_ipi_v01; diff --git a/target/linux/sunxi/config-5.15 b/target/linux/sunxi/config-5.15 index 9771ee8b4cf..c33bb7ab6fd 100644 --- a/target/linux/sunxi/config-5.15 +++ b/target/linux/sunxi/config-5.15 @@ -161,7 +161,6 @@ CONFIG_FONT_SUPPORT=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y -CONFIG_FRAME_WARN=2048 CONFIG_FREEZER=y CONFIG_FS_IOMAP=y CONFIG_FS_MBCACHE=y diff --git a/target/linux/uml/Makefile b/target/linux/uml/Makefile index 2b314aa4bb0..ba3c7eae9dc 100644 --- a/target/linux/uml/Makefile +++ b/target/linux/uml/Makefile @@ -14,6 +14,7 @@ BOARDNAME:=User Mode Linux FEATURES:=audio ext4 rootfs-part squashfs KERNEL_PATCHVER:=5.15 +KERNEL_TESTING_PATCHVER:=6.1 include $(INCLUDE_DIR)/target.mk diff --git a/target/linux/uml/config-6.1 b/target/linux/uml/config-6.1 new file mode 100644 index 00000000000..da4eaac3ac3 --- /dev/null +++ b/target/linux/uml/config-6.1 @@ -0,0 +1,150 @@ +CONFIG_3_LEVEL_PGTABLES=y +CONFIG_64BIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_EPHEMERAL_INODES=y +CONFIG_ARCH_NO_PREEMPT=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_BLK_DEV_COW_COMMON=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_UBD=y +CONFIG_BLK_DEV_UBD_SYNC=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_CDROM=y +# CONFIG_COMMON_CLK is not set +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CON_CHAN="xterm" +CONFIG_CON_ZERO_CHAN="fd:0,fd:1" +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_CPU_SUP_HYGON=y +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_ZHAOXIN=y +CONFIG_CRC16=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SHA1=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DNOTIFY=y +# CONFIG_EARLY_PRINTK is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXT4_FS=y +CONFIG_F2FS_FS=y +CONFIG_FAILOVER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_CLOCKEVENTS=y +# CONFIG_GENERIC_CPU is not set +CONFIG_GENERIC_CPU_DEVICES=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GPIO_CDEV=y +CONFIG_HOSTFS=y +CONFIG_HVC_DRIVER=y +CONFIG_HW_RANDOM=y +CONFIG_HZ_PERIODIC=y +CONFIG_IA32_FEAT_CTL=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INIT_ENV_ARG_LIMIT=128 +CONFIG_IRQ_WORK=y +CONFIG_ISO9660_FS=y +CONFIG_JBD2=y +# CONFIG_JFFS2_FS is not set +CONFIG_KALLSYMS=y +CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_LOCK_DEBUGGING_SUPPORT=y +# CONFIG_MATOM is not set +CONFIG_MAY_HAVE_RUNTIME_DEPS=y +CONFIG_MCONSOLE=y +# CONFIG_MCORE2 is not set +CONFIG_MEMFD_CREATE=y +CONFIG_MIGRATION=y +CONFIG_MK8=y +# CONFIG_MMAPPER is not set +CONFIG_MODULES_USE_ELF_RELA=y +# CONFIG_MPSC is not set +CONFIG_NAMESPACES=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_FAILOVER=y +# CONFIG_NET_NS is not set +CONFIG_NLS=y +CONFIG_NO_DMA=y +CONFIG_NO_IOMEM=y +CONFIG_NO_IOPORT_MAP=y +CONFIG_NR_CPUS=1 +CONFIG_NULL_CHAN=y +# CONFIG_OF is not set +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PORT_CHAN=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_PREEMPT_NONE_BUILD=y +# CONFIG_PROCESSOR_SELECT is not set +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_PTY_CHAN=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RD_BZIP2=y +CONFIG_RD_GZIP=y +CONFIG_RELAY=y +CONFIG_SG_POOL=y +CONFIG_SOFT_WATCHDOG=m +CONFIG_SRCU=y +CONFIG_SSL=y +CONFIG_SSL_CHAN="pty" +CONFIG_STACKTRACE=y +CONFIG_STDERR_CONSOLE=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TINY_SRCU=y +CONFIG_TTY_CHAN=y +CONFIG_UML=y +CONFIG_UML_NET=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_DAEMON_DEFAULT_SOCK="/tmp/uml.ctl" +CONFIG_UML_NET_DETERMINISTIC_MAC=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_MCAST=y +# CONFIG_UML_NET_PCAP is not set +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_SLIRP=y +CONFIG_UML_NET_TUNTAP=y +# CONFIG_UML_NET_VDE is not set +CONFIG_UML_NET_VECTOR=y +# CONFIG_UML_PCI_OVER_VIRTIO is not set +CONFIG_UML_RANDOM=y +# CONFIG_UML_SOUND is not set +CONFIG_UML_TIME_TRAVEL_SUPPORT=y +# CONFIG_UML_WATCHDOG is not set +CONFIG_UML_X86=y +# CONFIG_USER_NS is not set +CONFIG_VIRTIO=y +CONFIG_VIRTIO_ANCHOR=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_UML=y +CONFIG_VMAP_STACK=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +CONFIG_X86_64=y +CONFIG_X86_CMOV=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_MINIMUM_CPU_FAMILY=64 +CONFIG_X86_TSC=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_XTERM_CHAN=y +CONFIG_XTERM_CHAN_DEFAULT_EMULATOR="xterm" +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/uml/patches-6.1/101-mconsole-exec.patch b/target/linux/uml/patches-6.1/101-mconsole-exec.patch new file mode 100644 index 00000000000..2ff9701eb9d --- /dev/null +++ b/target/linux/uml/patches-6.1/101-mconsole-exec.patch @@ -0,0 +1,213 @@ +# +# Minimalist mconsole exec patch +# +# 3.10 version (with bit more synchronous behavior) by fingon at iki dot fi +# Adaptation to kernel 3.3.8 made by David Fernández (david at dit.upm.es) for +# Starting point: mconsole-exec-2.6.30.patch for kernel 2.6.30 +# Author of original patch: Paolo Giarrusso, aka Blaisorblade +# (http://www.user-mode-linux.org/~blaisorblade) +# +# Known misfeatures: +# +# - If output is too long, blocks (and breaks horribly) +# (this misfeature from 3.10 patches, when minimalizing the patch; +# workaround: redirect to a shared filesystem if long output is expected) +# +# - Nothing useful is done with stdin +# +--- a/arch/um/drivers/mconsole.h ++++ b/arch/um/drivers/mconsole.h +@@ -85,6 +85,7 @@ extern void mconsole_cad(struct mc_reque + extern void mconsole_stop(struct mc_request *req); + extern void mconsole_go(struct mc_request *req); + extern void mconsole_log(struct mc_request *req); ++extern void mconsole_exec(struct mc_request *req); + extern void mconsole_proc(struct mc_request *req); + extern void mconsole_stack(struct mc_request *req); + +--- a/arch/um/drivers/mconsole_kern.c ++++ b/arch/um/drivers/mconsole_kern.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) + */ + ++#include + #include + #include + #include +@@ -27,6 +28,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -124,6 +126,59 @@ void mconsole_log(struct mc_request *req + mconsole_reply(req, "", 0, 0); + } + ++void mconsole_exec(struct mc_request *req) ++{ ++ struct subprocess_info *sub_info; ++ int res, len; ++ struct file *out; ++ char buf[MCONSOLE_MAX_DATA]; ++ ++ char *envp[] = { ++ "HOME=/", "TERM=linux", ++ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin", ++ NULL ++ }; ++ char *argv[] = { ++ "/bin/sh", "-c", ++ req->request.data + strlen("exec "), ++ NULL ++ }; ++ ++ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp, GFP_ATOMIC, NULL, NULL, NULL); ++ if (sub_info == NULL) { ++ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0); ++ return; ++ } ++ res = call_usermodehelper_stdoutpipe(sub_info, &out); ++ if (res < 0) { ++ kfree(sub_info); ++ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0); ++ return; ++ } ++ ++ res = call_usermodehelper_exec(sub_info, UMH_WAIT_PROC); ++ if (res < 0) { ++ kfree(sub_info); ++ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0); ++ return; ++ } ++ ++ for (;;) { ++ len = out->f_op->read(out, buf, sizeof(buf), &out->f_pos); ++ if (len < 0) { ++ mconsole_reply(req, "reading output failed", 1, 0); ++ break; ++ } ++ if (len == 0) ++ break; ++ mconsole_reply_len(req, buf, len, 0, 1); ++ } ++ fput(out); ++ ++ mconsole_reply_len(req, NULL, 0, 0, 0); ++} ++ ++ + void mconsole_proc(struct mc_request *req) + { + struct vfsmount *mnt = proc_mnt; +@@ -190,6 +245,7 @@ void mconsole_proc(struct mc_request *re + stop - pause the UML; it will do nothing until it receives a 'go' \n\ + go - continue the UML after a 'stop' \n\ + log - make UML enter into the kernel log\n\ ++ exec - pass to /bin/sh -c synchronously\n\ + proc - returns the contents of the UML's /proc/\n\ + stack - returns the stack of the specified pid\n\ + " +--- a/arch/um/drivers/mconsole_user.c ++++ b/arch/um/drivers/mconsole_user.c +@@ -30,6 +30,7 @@ static struct mconsole_command commands[ + { "stop", mconsole_stop, MCONSOLE_PROC }, + { "go", mconsole_go, MCONSOLE_INTR }, + { "log", mconsole_log, MCONSOLE_INTR }, ++ { "exec", mconsole_exec, MCONSOLE_PROC }, + { "proc", mconsole_proc, MCONSOLE_PROC }, + { "stack", mconsole_stack, MCONSOLE_INTR }, + }; +--- a/arch/um/os-Linux/file.c ++++ b/arch/um/os-Linux/file.c +@@ -560,6 +560,8 @@ int os_create_unix_socket(const char *fi + + addr.sun_family = AF_UNIX; + ++ if (len > sizeof(addr.sun_path)) ++ len = sizeof(addr.sun_path); + snprintf(addr.sun_path, len, "%s", file); + + err = bind(sock, (struct sockaddr *) &addr, sizeof(addr)); +--- a/include/linux/kmod.h ++++ b/include/linux/kmod.h +@@ -32,4 +32,6 @@ static inline int request_module_nowait( + #define try_then_request_module(x, mod...) (x) + #endif + ++int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info, struct file **filp); ++ + #endif /* __LINUX_KMOD_H__ */ +--- a/include/linux/umh.h ++++ b/include/linux/umh.h +@@ -23,6 +23,7 @@ struct subprocess_info { + const char *path; + char **argv; + char **envp; ++ struct file *stdout; + int wait; + int retval; + int (*init)(struct subprocess_info *info, struct cred *new); +--- a/kernel/umh.c ++++ b/kernel/umh.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + #include + +@@ -74,6 +75,28 @@ static int call_usermodehelper_exec_asyn + flush_signal_handlers(current, 1); + spin_unlock_irq(¤t->sighand->siglock); + ++ /* Install output when needed */ ++ if (sub_info->stdout) { ++ struct files_struct *f = current->files; ++ struct fdtable *fdt; ++ ++ sys_close(1); ++ sys_close(2); ++ get_file(sub_info->stdout); ++ fd_install(1, sub_info->stdout); ++ fd_install(2, sub_info->stdout); ++ spin_lock(&f->file_lock); ++ fdt = files_fdtable(f); ++ __set_bit(1, fdt->open_fds); ++ __clear_bit(1, fdt->close_on_exec); ++ __set_bit(2, fdt->open_fds); ++ __clear_bit(2, fdt->close_on_exec); ++ spin_unlock(&f->file_lock); ++ ++ /* disallow core files */ ++ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0}; ++ } ++ + /* + * Initial kernel threads share ther FS with init, in order to + * get the init root directory. But we've now created a new +@@ -333,6 +356,20 @@ static void helper_unlock(void) + wake_up(&running_helpers_waitq); + } + ++int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info, ++ struct file **filp) ++{ ++ struct file *f[2]; ++ ++ if (create_pipe_files(f, 0) < 0) ++ return PTR_ERR(f); ++ ++ sub_info->stdout = f[1]; ++ *filp = f[0]; ++ return 0; ++} ++EXPORT_SYMBOL(call_usermodehelper_stdoutpipe); ++ + /** + * call_usermodehelper_setup - prepare to call a usermode helper + * @path: path to usermode executable diff --git a/target/linux/uml/patches-6.1/102-pseudo-random-mac.patch b/target/linux/uml/patches-6.1/102-pseudo-random-mac.patch new file mode 100644 index 00000000000..a752e1d2779 --- /dev/null +++ b/target/linux/uml/patches-6.1/102-pseudo-random-mac.patch @@ -0,0 +1,151 @@ +=============================================================================== + +This patch makes MAC addresses of network interfaces predictable. In +particular, it adds a small routine that computes MAC addresses of based on +a SHA1 hash of the virtual machine name and interface ID. + +TECHNICAL INFORMATION: + +Applies to vanilla kernel 3.9.4. + +=============================================================================== +--- a/arch/um/drivers/Kconfig ++++ b/arch/um/drivers/Kconfig +@@ -153,6 +153,20 @@ config UML_NET + enable at least one of the following transport options to actually + make use of UML networking. + ++config UML_NET_DETERMINISTIC_MAC ++ bool "Use deterministic MAC addresses for network interfaces" ++ default y ++ depends on UML_NET ++ select CRYPTO_SHA1 ++ help ++ Virtual network devices inside a User-Mode Linux instance must be ++ assigned a MAC (Ethernet) address. If none is specified on the UML ++ command line, one must be automatically computed. If this option is ++ enabled, a randomly generated address is used. Otherwise, if this ++ option is disabled, the address is generated from a SHA1 hash of ++ the umid of the UML instance and the interface name. The latter choice ++ is useful to make MAC addresses predictable. ++ + config UML_NET_ETHERTAP + bool "Ethertap transport (obsolete)" + depends on UML_NET +--- a/arch/um/drivers/net_kern.c ++++ b/arch/um/drivers/net_kern.c +@@ -25,6 +25,14 @@ + #include + #include + ++#include ++#include ++#include ++#include ++#include ++#include ++#include "os.h" ++ + #define DRIVER_NAME "uml-netdev" + + static DEFINE_SPINLOCK(opened_lock); +@@ -274,9 +282,55 @@ static const struct ethtool_ops uml_net_ + .get_ts_info = ethtool_op_get_ts_info, + }; + ++#ifdef CONFIG_UML_NET_DETERMINISTIC_MAC ++ ++/* Compute a SHA1 hash of the UML instance's id and ++ * * an interface name. */ ++static int compute_hash(const char *umid, const char *ifname, char *hash) ++{ ++ struct ahash_request *desc = NULL; ++ struct crypto_ahash *tfm = NULL; ++ struct scatterlist sg; ++ char *vmif = NULL; ++ int ret = -ENOMEM; ++ ++ vmif = kmalloc(1024, GFP_KERNEL); ++ if (!vmif) ++ goto out; ++ ++ strcpy (vmif, umid); ++ strcat (vmif, ifname); ++ ++ tfm = crypto_alloc_ahash("sha1", 0, CRYPTO_ALG_ASYNC); ++ if (IS_ERR(tfm)) ++ goto out; ++ ++ desc = ahash_request_alloc(tfm, GFP_KERNEL); ++ if (!desc) ++ goto out; ++ ++ crypto_ahash_clear_flags(tfm, ~0); ++ ++ sg_init_table(&sg, 1); ++ sg_set_buf(&sg, vmif, strlen(vmif)); ++ ++ ahash_request_set_crypt(desc, &sg, hash, strlen(vmif)); ++ ++ ret = crypto_ahash_digest(desc); ++out: ++ crypto_free_ahash(tfm); ++ ahash_request_free(desc); ++ kfree(vmif); ++ ++ return ret; ++} ++ ++#endif ++ + void uml_net_setup_etheraddr(struct net_device *dev, char *str) + { + u8 addr[ETH_ALEN]; ++ u8 hash[SHA1_DIGEST_SIZE]; + char *end; + int i; + +@@ -320,9 +374,26 @@ void uml_net_setup_etheraddr(struct net_ + return; + + random: ++#ifndef CONFIG_UML_NET_DETERMINISTIC_MAC + printk(KERN_INFO + "Choosing a random ethernet address for device %s\n", dev->name); + eth_hw_addr_random(dev); ++#else ++ printk(KERN_INFO ++ "Computing a digest to use as ethernet address for device %s\n", dev->name); ++ if (compute_hash(get_umid(), dev->name, hash) < 0) { ++ printk(KERN_WARNING ++ "Could not compute digest to use as ethernet address for device %s. " ++ "Using random address instead.\n", dev->name); ++ eth_random_addr(addr); ++ } ++ else { ++ for (i=0; i < 6; i++) ++ addr[i] = (hash[i] + hash[i+6]) % 0x100; ++ } ++ addr [0] &= 0xfe; /* clear multicast bit */ ++ addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ ++#endif + } + + static DEFINE_SPINLOCK(devices_lock); +--- a/kernel/umh.c ++++ b/kernel/umh.c +@@ -357,12 +357,12 @@ static void helper_unlock(void) + } + + int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info, +- struct file **filp) ++ struct file **filp) + { + struct file *f[2]; + + if (create_pipe_files(f, 0) < 0) +- return PTR_ERR(f); ++ return PTR_ERR(f); + + sub_info->stdout = f[1]; + *filp = f[0]; diff --git a/target/linux/x86/base-files/etc/board.d/01_leds b/target/linux/x86/base-files/etc/board.d/01_leds index e41c78e62b2..efc5460df3c 100644 --- a/target/linux/x86/base-files/etc/board.d/01_leds +++ b/target/linux/x86/base-files/etc/board.d/01_leds @@ -19,7 +19,7 @@ pc-engines-apu1|pc-engines-apu2|pc-engines-apu3|pc-engines-apu4|pc-engines-apu5| sophos-sg-105wr1|sophos-sg-125wr1|sophos-sg-135wr1|sophos-xg-105wr1|sophos-xg-125wr1|sophos-xg-135wr1) ucidef_set_led_netdev "wlan" "WiFi" "ath9k-phy0" "phy0tpt" ;; -sophos-sg-105wr2|sophos-sg-125wr2|sophos-sg-135wr2|sophos-xg-105wr2|sophos-xg-125wr2|sophos-xg-135wr2| +sophos-sg-105wr2|sophos-sg-125wr2|sophos-sg-135wr2|sophos-xg-105wr2|sophos-xg-125wr2|sophos-xg-135wr2|\ sophos-sg-105wr3|sophos-sg-125wr3|sophos-sg-135wr3|sophos-xg-105wr3|sophos-xg-125wr3|sophos-xg-135wr3) ucidef_set_led_netdev "wlan" "WiFi" "ath10k-phy0" "phy0tpt" ;; diff --git a/target/linux/x86/config-5.15 b/target/linux/x86/config-5.15 index ee7ab9d6dee..aebe4f08c4a 100644 --- a/target/linux/x86/config-5.15 +++ b/target/linux/x86/config-5.15 @@ -415,6 +415,7 @@ CONFIG_X86_MSR=y CONFIG_X86_PAT=y CONFIG_X86_PLATFORM_DEVICES=y # CONFIG_X86_PLATFORM_DRIVERS_DELL is not set +# CONFIG_X86_PLATFORM_DRIVERS_HP is not set # CONFIG_X86_POWERNOW_K6 is not set # CONFIG_X86_POWERNOW_K7 is not set # CONFIG_X86_REBOOTFIXUPS is not set diff --git a/target/linux/x86/modules.mk b/target/linux/x86/modules.mk index f6a7c6c440b..511410d614b 100644 --- a/target/linux/x86/modules.mk +++ b/target/linux/x86/modules.mk @@ -2,23 +2,6 @@ # # Copyright (C) 2017 Cezary Jackiewicz -define KernelPackage/amazon-ena - SUBMENU:=$(NETWORK_DEVICES_MENU) - TITLE:=Elastic Network Adapter (for Amazon AWS T3) - DEPENDS:=@TARGET_x86_64 - KCONFIG:=CONFIG_ENA_ETHERNET - FILES:=$(LINUX_DIR)/drivers/net/ethernet/amazon/ena/ena.ko - AUTOLOAD:=$(call AutoLoad,12,ena) -endef - -define KernelPackage/amazon-ena/description - This driver supports Elastic Network Adapter (ENA) - used by Amazon AWS T3 instances. -endef - -$(eval $(call KernelPackage,amazon-ena)) - - define KernelPackage/amd-xgbe SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=AMD Ethernet on SoC support diff --git a/target/linux/x86/patches-5.15/103-pcengines_apu6_platform.patch b/target/linux/x86/patches-5.15/103-pcengines_apu6_platform.patch index 03cb33acbf7..9ec0a33c311 100644 --- a/target/linux/x86/patches-5.15/103-pcengines_apu6_platform.patch +++ b/target/linux/x86/patches-5.15/103-pcengines_apu6_platform.patch @@ -63,7 +63,7 @@ Sighed-off-by: Philip Prindeville --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -701,7 +701,7 @@ config XO1_RFKILL +@@ -661,7 +661,7 @@ config XO1_RFKILL laptop. config PCENGINES_APU2 @@ -72,7 +72,7 @@ Sighed-off-by: Philip Prindeville depends on INPUT && INPUT_KEYBOARD && GPIOLIB depends on LEDS_CLASS select GPIO_AMD_FCH -@@ -709,7 +709,7 @@ config PCENGINES_APU2 +@@ -669,7 +669,7 @@ config PCENGINES_APU2 select LEDS_GPIO help This driver provides support for the front button and LEDs on diff --git a/tools/firmware-utils/patches/001-add-sifiveu-guid-types.patch b/tools/firmware-utils/patches/001-add-sifiveu-guid-types.patch new file mode 100644 index 00000000000..45900e982c9 --- /dev/null +++ b/tools/firmware-utils/patches/001-add-sifiveu-guid-types.patch @@ -0,0 +1,38 @@ +diff -ruN firmware-utils-2022-02-28-002cfaf0.old/src/ptgen.c firmware-utils-2022-02-28-002cfaf0/src/ptgen.c +--- firmware-utils-2022-02-28-002cfaf0.old/src/ptgen.c 2022-04-23 19:02:07.307896842 +0200 ++++ firmware-utils-2022-02-28-002cfaf0/src/ptgen.c 2022-04-22 18:48:54.477970950 +0200 +@@ -82,6 +82,14 @@ + GUID_INIT( 0x0fc63daf, 0x8483, 0x4772, \ + 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4) + ++#define GUID_PARTITION_SIFIVE_SPL \ ++ GUID_INIT( 0x5b193300, 0xfc78, 0x40cd, \ ++ 0x80, 0x02, 0xe8, 0x6c, 0x45, 0x58, 0x0b, 0x47) ++ ++#define GUID_PARTITION_SIFIVE_UBOOT \ ++ GUID_INIT( 0x2e54b353, 0x1271, 0x4842, \ ++ 0x80, 0x6f, 0xe4, 0x36, 0xd6, 0xaf, 0x69, 0x85) ++ + #define GPT_HEADER_SIZE 92 + #define GPT_ENTRY_SIZE 128 + #define GPT_ENTRY_MAX 128 +@@ -276,6 +284,19 @@ + (1ULL << 56); /* success=1 */ + return true; + } ++ ++ if (!strcmp(type, "sifiveu_spl")) { ++ part->has_guid = true; ++ part->guid = GUID_PARTITION_SIFIVE_SPL; ++ return true; ++ } ++ ++ if (!strcmp(type, "sifiveu_uboot")) { ++ part->has_guid = true; ++ part->guid = GUID_PARTITION_SIFIVE_UBOOT; ++ return true; ++ } ++ + return false; + } + diff --git a/tools/meson/Makefile b/tools/meson/Makefile index f0810ced96c..b115e0802b5 100644 --- a/tools/meson/Makefile +++ b/tools/meson/Makefile @@ -1,11 +1,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=meson -PKG_VERSION:=1.1.0 +PKG_VERSION:=1.1.1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/mesonbuild/meson/releases/download/$(PKG_VERSION) -PKG_HASH:=d9616c44cd6c53689ff8f05fc6958a693f2e17c3472a8daf83cee55dabff829f +PKG_HASH:=d04b541f97ca439fb82fab7d0d480988be4bd4e62563a5ca35fadb5400727b1c PKG_MAINTAINER:=Andre Heider PKG_LICENSE:=Apache-2.0