From 7e464f4ef5afc06b7c5d72f5aa2c600e53bb64c7 Mon Sep 17 00:00:00 2001 From: coolsnowwolf Date: Tue, 10 Apr 2018 16:31:13 +0800 Subject: [PATCH] switch ipq806x to kernel 4.14 --- target/linux/ipq806x/Makefile | 4 +- .../ipq806x/base-files/etc/board.d/01_leds | 24 +- .../ipq806x/base-files/etc/board.d/02_network | 39 +- .../etc/hotplug.d/firmware/11-ath10k-caldata | 64 - .../base-files/lib/preinit/06_set_iface_mac | 21 - .../base-files/lib/upgrade/openmesh.sh | 110 - .../base-files/lib/upgrade/platform.sh | 26 +- .../ipq806x/base-files/lib/upgrade/zyxel.sh | 39 +- target/linux/ipq806x/config-4.14 | 565 + target/linux/ipq806x/config-4.4 | 426 + target/linux/ipq806x/config-4.9 | 21 +- .../arch/arm/boot/dts/qcom-ipq4019-bus.dtsi | 0 .../arm/boot/dts/qcom-ipq4019-fritz4040.dts | 189 +- .../arm/boot/dts/qcom-ipq4019-nbg6617.dts | 135 + .../arm/boot/dts/qcom-ipq4019-rt-ac58u.dts | 2 +- .../arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 246 + .../arch/arm/boot/dts/qcom-ipq8064-c2600.dts | 500 + .../arch/arm/boot/dts/qcom-ipq8064-d7800.dts | 419 + .../arch/arm/boot/dts/qcom-ipq8064-db149.dts | 236 + .../arch/arm/boot/dts/qcom-ipq8064-ea8500.dts | 406 + .../arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 394 + .../arm/boot/dts/qcom-ipq8064-r7500v2.dts | 425 + .../arm/boot/dts/qcom-ipq8064-vr2600v.dts | 424 + .../arch/arm/boot/dts/qcom-ipq8064.dtsi | 1334 ++ .../arm/boot/dts/qcom-ipq8065-nbg6817.dts | 391 + .../arch/arm/boot/dts/qcom-ipq8065-r7800.dts | 575 + .../arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi | 1 + .../arch/arm/boot/dts/qcom-ipq8065.dtsi | 170 + .../arch/arm/boot/dts/qcom-ipq4019-a42.dts | 244 - .../arm/boot/dts/qcom-ipq4019-rt-acrh17.dts | 291 - .../arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 8 +- .../arch/arm/boot/dts/qcom-ipq8064-wpq864.dts | 570 + .../arm/boot/dts/qcom-ipq8065-nbg6817.dts | 4 +- .../arch/arm/boot/dts/qcom-ipq8065-r7800.dts | 4 +- target/linux/ipq806x/image/Makefile | 123 +- target/linux/ipq806x/modules.mk | 14 - ...ings-qcom_adm-Fix-channel-specifiers.patch | 71 + .../0002-dmaengine-Add-ADM-driver.patch | 966 ++ ...-ipq4019-Add-compat-for-qcom-ipq4019.patch | 6 +- ...-cpu-operating-points-for-cpufreq-su.patch | 0 ...018-qcom-ipq4019-turn-on-DMA-for-i2c.patch | 2 +- ...0020-qcom-ipq4019-enable-DMA-for-spi.patch | 2 +- ...9-Add-support-for-IPQ4019-DK04-board.patch | 5 +- ...0030-clk-Disable-i2c-device-on-gsbi4.patch | 40 + ...d-add-SMEM-parser-for-QCOM-platforms.patch | 275 + .../0032-phy-add-qcom-dwc3-phy.patch | 620 + ...ically-select-PCI_DOMAINS-if-PCI-is-.patch | 29 + ...Krait-L2-register-accessor-functions.patch | 147 + ...lit-out-register-accessors-for-reuse.patch | 195 + ...pport-for-High-Frequency-PLLs-HFPLLs.patch | 352 + .../0039-clk-qcom-Add-HFPLL-driver.patch | 206 + .../0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch | 129 + ...lk-qcom-Add-support-for-Krait-clocks.patch | 241 + ...042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch | 209 + ...om-Add-Krait-clock-controller-driver.patch | 436 + .../0044-clk-Add-safe-switch-hook.patch | 160 + ...le-to-register-cpufreq-on-Krait-CPUs.patch | 307 + ...cpufreq-qcom-independent-core-clocks.patch | 66 + ...a-BBT-flag-to-access-bad-block-marke.patch | 72 + ...ow-to-set-regulator-without-opp_list.patch | 27 + ...-cpufreq-dt-support-l2-cache-scaling.patch | 203 + .../0053-regulator-add-smb208-support.patch | 55 + ...le-Add-cpuidle-support-for-QCOM-cpus.patch | 29 + ...arch-arm-force-ZRELADDR-on-arch-qcom.patch | 62 + ...conflicts-with-OpenWrt-auto-mounting.patch | 23 + ...ed-the-enable-regs-and-mask-for-PRNG.patch | 25 + .../0063-1-ipq806x-tsens-driver.patch | 627 + ...sens-support-configurable-interrupts.patch | 464 + .../patches-4.14/0064-clk-clk-rpm-fixes.patch | 93 + .../0065-arm-override-compiler-flags.patch | 21 + .../0066-GPIO-add-named-gpio-exports.patch | 166 + ...Mangle-bootloader-s-kernel-arguments.patch | 189 + .../0068-spi-add-gpio-cs-support.patch | 50 + .../0069-arm-boot-add-dts-files.patch | 31 + .../0070-qcom-spm-fix-probe-order.patch | 16 + ...I-qcom-Fixed-IPQ806x-specific-clocks.patch | 99 + ...com-Fixed-IPQ806x-PCIE-reset-changes.patch | 92 + ...qcom-Fixed-IPQ806x-PCIE-init-changes.patch | 121 + ...rogramming-the-PCIE-iATU-for-IPQ806x.patch | 118 + .../0071-6-PCI-qcom-Force-GEN1-support.patch | 64 + ...7-pcie-Set-PCIE-MRRS-and-MPS-to-256B.patch | 71 + ...qcom-Fixed-pcie_phy_clk-branch-issue.patch | 91 + ...nge-duplicate-pci-reset-to-phy-reset.patch | 25 + ...re-scm-add-ipq806x-compatible-string.patch | 26 + ...e-scm_call-to-route-GPIO-irq-to-Apps.patch | 168 + ...nd-add-Winbond-manufacturer-and-chip.patch | 37 + .../105-mtd-nor-add-mx25l25635f.patch | 22 + ...use-v2-of-the-kpss-bringup-mechanism.patch | 4 +- ...-USB-nodes-to-ipq4019-SoC-device-tre.patch | 10 +- ...307-ARM-qcom-Add-IPQ4019-SoC-support.patch | 2 +- .../310-msm-adhoc-bus-support.patch | 11026 ++++++++++++++++ ...dd-quirk-to-autoload-ubi-on-rt-ac58u.patch | 2 +- ...4019-needs-rfs-vlan_tag-callbacks-in.patch | 4 +- .../700-net-add-qualcomm-mdio-and-phy.patch | 12 +- .../701-dts-ipq4019-add-mdio-node.patch | 4 +- ...702-dts-ipq4019-add-PHY-switch-nodes.patch | 2 +- ...add-qualcomm-essedma-ethernet-driver.patch | 11 +- ...ts-ipq4019-add-ethernet-essedma-node.patch | 2 +- ...712-net-essedma-disable-default-vlan.patch | 4 +- ...19-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch | 2 +- ...r-qca-ipq4019-dwc3-in-dwc3-of-simple.patch | 0 .../850-soc-add-qualcomm-syscon.patch | 177 + ...q4019-add-nand-and-qpic-bam-dma-node.patch | 6 +- ...pq4019-ap-dk04-fix-pinctrl-node-name.patch | 0 ...19-ap-dk04-remove-xo-and-timer-nodes.patch | 0 ...4019-ap-dk01-add-tcsr-config-to-dtsi.patch | 7 +- ...19-ap-dk01-add-network-nodes-to-dtsi.patch | 2 +- ...-dk01-remove-spi-chip-node-from-dtsi.patch | 2 +- ...864-06-dts-ipq4019-fix-max-cpu-speed.patch | 0 ...9-ap-dk01.1-c1-add-spi-and-ram-nodes.patch | 0 ...9-ap-dk01.1-c1-add-compatible-string.patch | 0 .../patches-4.14/999-boost-rpm-clocks.patch | 49 + ...PI-register-brd-clks-bckwrds-cmptblt.patch | 135 + .../002-make-reset-control-ops-const.patch | 35 + .../003-mv-cxo-pxo-xo-into-DT.patch | 172 + ...e_put-after-calling-of_parse_phandle.patch | 25 + ...fd-qcom_rpm-Handle-message-RAM-clock.patch | 90 + ...dog-core-add-restart-handler-support.patch | 205 + ...dog-core-add-reboot-notifier-support.patch | 160 + ...ss-watchdog_class-instead-of-pointer.patch | 111 + ...vice-status-through-sysfs-attributes.patch | 260 + ...te-watchdog-device-in-watchdog_dev-c.patch | 261 + ...variables-based-on-variable-lifetime.patch | 969 ++ ...g-device-from-struct-watchdog_device.patch | 117 + .../009-8-watchdog-kill-unref-ref-ops.patch | 26 + ...-1-qcom-wdt-use-core-restart-handler.patch | 113 + ...ot-set-dev-in-struct-watchdog_device.patch | 25 + ...rameters-to-restart-handler-callback.patch | 51 + ...4-watchdog-qcom-Report-reboot-reason.patch | 46 + ...andalone-watchdog-not-in-timer-block.patch | 162 + ...e-BARK-time-in-addition-to-BITE-time.patch | 60 + ...river-data-reference-count-callbacks.patch | 95 + ...g-da9052_wdt-Drop-reference-counting.patch | 87 + ...g-da9055_wdt-Drop-reference-counting.patch | 80 + ...-qcom-Add-support-for-SMD-RPM-Clocks.patch | 746 ++ ...-clk-qcom-Add-support-for-RPM-Clocks.patch | 586 + ...k-qcom-clk-rpm-Fix-clk_hw-references.patch | 94 + ...m-tsens-Add-a-skeletal-TSENS-drivers.patch | 536 + ...-Add-support-for-8916-family-of-SoCs.patch | 165 + ...-Add-support-for-8974-family-of-SoCs.patch | 293 + ...-Add-support-for-8960-family-of-SoCs.patch | 364 + ...916-mark-PM-functions-__maybe_unused.patch | 46 + ...rn-error-for-non-word-aligned-access.patch | 42 + ...re-fix-error-path-in-nvmem_add_cells.patch | 34 + ...dd-flag-to-export-NVMEM-to-root-only.patch | 101 + ...ity-support-for-older-EEPROM-drivers.patch | 181 + .../patches-4.4/020-add-ap148-bootargs.patch | 61 + .../021-add-ap148-partitions.patch | 19 + .../patches-4.4/022-add-db149-dts.patch | 160 + ...-ipq806x-Disable-i2c-device-on-gsbi4.patch | 52 + .../024-ap148-add-memory-node.patch | 14 + ...ARM_CPU_SUSPEND-for-power-management.patch | 30 + ...M-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch | 33 + ...-add-SMEM-device-node-to-IPQ806x-dts.patch | 36 + ...d-add-SMEM-parser-for-QCOM-platforms.patch | 275 + ...01-usb-dwc3-core-purge-dev_dbg-calls.patch | 42 + ...ate-maximum_speed-for-SuperSpeedPlus.patch | 52 + ...Validate-the-maximum_speed-parameter.patch | 68 + ...-usb-dwc3-DWC_usb31-controller-check.patch | 28 + ...e-register-fields-for-SuperSpeedPlus.patch | 42 + ...usb-dwc3-core-improve-reset-sequence.patch | 100 + ...07-usb-dwc3-drop-FIFO-resizing-logic.patch | 242 + ...08-usb-dwc3-remove-num_event_buffers.patch | 265 + .../096-09-usb-dwc3-drop-ev_buffs-array.patch | 96 + ...core-fix-PHY-handling-during-suspend.patch | 67 + ...1-usb-dwc3-add-generic-OF-glue-layer.patch | 234 + ...c3-of-simple-fix-build-warning-on-PM.patch | 36 + ...impossible-check-for-of_clk_get_pare.patch | 47 + ..._set_drvdata-in-dwc3_of_simple_probe.patch | 27 + ...-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch | 512 + ...-qcom-add-USB-nodes-to-ipq806x-ap148.patch | 123 + ...om-Document-PCIe-devicetree-bindings.patch | 263 + ...-Add-Qualcomm-PCIe-controller-driver.patch | 752 ++ ...-add-pcie-nodes-to-ipq806x-platforms.patch | 245 + ...ically-select-PCI_DOMAINS-if-PCI-is-.patch | 29 + .../patches-4.4/114-pcie-add-ctlr-init.patch | 311 + .../115-add-pcie-aux-clk-dts.patch | 80 + .../126-add-rpm-to-ipq8064-dts.patch | 87 + ...Krait-L2-register-accessor-functions.patch | 144 + ...lit-out-register-accessors-for-reuse.patch | 177 + ...g-high-rates-to-downstream-clocks-du.patch | 122 + .../136-clk-Add-safe-switch-hook.patch | 151 + ...pport-for-High-Frequency-PLLs-HFPLLs.patch | 351 + .../138-clk-qcom-Add-HFPLL-driver.patch | 206 + .../139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch | 127 + ...lk-qcom-Add-support-for-Krait-clocks.patch | 272 + ...141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch | 207 + ...om-Add-Krait-clock-controller-driver.patch | 435 + ...le-to-register-cpufreq-on-Krait-CPUs.patch | 304 + ...-necessary-DT-data-for-Krait-cpufreq.patch | 96 + ...ings-qcom_adm-Fix-channel-specifiers.patch | 76 + .../156-dmaengine-Add-ADM-driver.patch | 964 ++ ...7-ARM-DT-ipq8064-Add-ADM-device-node.patch | 42 + ...access-bad-block-markers-in-raw-mode.patch | 83 + ...nand-Qualcomm-NAND-controller-driver.patch | 2024 +++ ...-bindings-qcom_nandc-Add-DT-bindings.patch | 82 + ...Add-NAND-controller-node-for-ipq806x.patch | 51 + ...-NAND-node-on-IPQ8064-AP148-platform.patch | 76 + ...m-dts-enable-qcom-smem-on-AP148-NAND.patch | 11 + ...-ARM-qcom_rpm_fix_support_for_smb208.patch | 50 + .../168-ARM-qcom-add-smb208-DT.patch | 74 + ...rt-adjusting-OPP-voltages-at-runtime.patch | 145 + ...dev_pm_opp_get_voltage-freq-RCU-free.patch | 114 + ...-dt-Handle-OPP-voltage-adjust-events.patch | 184 + ...-dt-Add-L2-frequency-scaling-support.patch | 161 + ...ng-rcu_read_lock-for-find_device_opp.patch | 33 + .../patches-4.4/175-add-regmap-mux-div.patch | 386 + ...lways-add-factor-clock-for-xo-clocks.patch | 53 + ...le-Add-cpuidle-support-for-QCOM-cpus.patch | 29 + ...arch-arm-force-ZRELADDR-on-arch-qcom.patch | 62 + .../302-mtd-qcom-smem-rename-rootfs-ubi.patch | 13 + ...3-add-saw_l2-and-sns-into-ipq8064-DT.patch | 41 + ...4-add-cpu-idle-state-into-ipq8064-DT.patch | 58 + ...5-add-board-clocks-and-rpmcc-into-DT.patch | 43 + .../306-add-RPM-msg-RAM-into-DT.patch | 22 + ...ed-the-enable-regs-and-mask-for-PRNG.patch | 25 + ...x-Increase-Atomic-Coherent-Pool-size.patch | 52 + .../309-clk-gcc-add-tsens-child-node.patch | 38 + .../310-add-necessary-thermal-data.patch | 150 + .../311-add-rpmcc-for-ipq806x.patch | 81 + .../315-disable-usb3-phy-suspend.patch | 36 + .../patches-4.4/316-dt-add-prng-support.patch | 16 + .../ipq806x/patches-4.4/400-dsa-add-qca.patch | 1068 ++ ...s-qcom-add-mdio-nodes-to-ap148-db149.patch | 146 + ...-add-gmac-nodes-to-ipq806x-platforms.patch | 216 + ...Fix-fifo-and-dma-support-for-IPQ806x.patch | 129 + ...ke-sure-mode-is-only-determined-once.patch | 220 + ...i-qup-Fix-transaction-done-signaling.patch | 30 + ...stmmac-fix-ipq806x-DMA-configuration.patch | 117 + ...i-qup-Fix-DMA-mode-to-work-correctly.patch | 221 + ...qup-Fix-block-mode-to-work-correctly.patch | 312 + ...qup-properly-detect-extra-interrupts.patch | 62 + ...-read-opflags-to-see-if-transaction-.patch | 27 + .../ipq806x/patches-4.4/800-devicetree.patch | 29 + .../801-override-compiler-flags.patch | 11 + .../802-GPIO-add-named-gpio-exports.patch | 166 + .../996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch | 185 + ...19-report-accurate-fixed-clock-rates.patch | 33 - ...4019-use-correct-clock-for-i2c-bus-0.patch | 28 - ...d-add-SMEM-parser-for-QCOM-platforms.patch | 9 +- .../0032-phy-add-qcom-dwc3-phy.patch | 2 +- .../0069-arm-boot-add-dts-files.patch | 8 +- ...ipq806x-usb-Control-USB-master-reset.patch | 14 +- ...-both-IPQ4019-wifi-block-definitions.patch | 105 - ...9-add-pseudo-random-number-generator.patch | 29 - ...ctrl-Updated-various-Pin-definitions.patch | 1328 -- ...support-to-configure-ipq40xx-GPIO_PU.patch | 236 - ...d-bam_dma-support-in-qcom_nand-drive.patch | 370 - ...ded-bam-transaction-and-support-addi.patch | 1267 -- ...qcom-bam_dma-Add-custom-data-mapping.patch | 209 - ...-2-dts-ipq4019-Fix-pinctrl-node-name.patch | 44 - ...-Move-xo-and-timer-nodes-to-SoC-dtsi.patch | 78 - .../900-mach-qcom-add-msm_pcie-driver.patch | 4883 ------- 253 files changed, 47893 insertions(+), 9780 deletions(-) delete mode 100644 target/linux/ipq806x/base-files/lib/preinit/06_set_iface_mac delete mode 100644 target/linux/ipq806x/base-files/lib/upgrade/openmesh.sh create mode 100644 target/linux/ipq806x/config-4.14 create mode 100644 target/linux/ipq806x/config-4.4 rename target/linux/ipq806x/{files-4.9 => files-4.14}/arch/arm/boot/dts/qcom-ipq4019-bus.dtsi (100%) rename target/linux/ipq806x/{files-4.9 => files-4.14}/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts (60%) create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-nbg6617.dts rename target/linux/ipq806x/{files-4.9 => files-4.14}/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts (98%) create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ap148.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-c2600.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-d7800.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-db149.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-r7800.dts create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi create mode 100644 target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065.dtsi delete mode 100644 target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-a42.dts delete mode 100644 target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-acrh17.dts create mode 100644 target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts delete mode 100644 target/linux/ipq806x/modules.mk create mode 100644 target/linux/ipq806x/patches-4.14/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch create mode 100644 target/linux/ipq806x/patches-4.14/0002-dmaengine-Add-ADM-driver.patch rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch (86%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch (100%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch (97%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/0020-qcom-ipq4019-enable-DMA-for-spi.patch (97%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch (98%) create mode 100644 target/linux/ipq806x/patches-4.14/0030-clk-Disable-i2c-device-on-gsbi4.patch create mode 100644 target/linux/ipq806x/patches-4.14/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch create mode 100644 target/linux/ipq806x/patches-4.14/0032-phy-add-qcom-dwc3-phy.patch create mode 100644 target/linux/ipq806x/patches-4.14/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch create mode 100644 target/linux/ipq806x/patches-4.14/0034-ARM-Add-Krait-L2-register-accessor-functions.patch create mode 100644 target/linux/ipq806x/patches-4.14/0035-clk-mux-Split-out-register-accessors-for-reuse.patch create mode 100644 target/linux/ipq806x/patches-4.14/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch create mode 100644 target/linux/ipq806x/patches-4.14/0039-clk-qcom-Add-HFPLL-driver.patch create mode 100644 target/linux/ipq806x/patches-4.14/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch create mode 100644 target/linux/ipq806x/patches-4.14/0041-clk-qcom-Add-support-for-Krait-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.14/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch create mode 100644 target/linux/ipq806x/patches-4.14/0043-clk-qcom-Add-Krait-clock-controller-driver.patch create mode 100644 target/linux/ipq806x/patches-4.14/0044-clk-Add-safe-switch-hook.patch create mode 100644 target/linux/ipq806x/patches-4.14/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch create mode 100644 target/linux/ipq806x/patches-4.14/0046-cpufreq-qcom-independent-core-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.14/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch create mode 100644 target/linux/ipq806x/patches-4.14/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch create mode 100644 target/linux/ipq806x/patches-4.14/0049-cpufreq-dt-support-l2-cache-scaling.patch create mode 100644 target/linux/ipq806x/patches-4.14/0053-regulator-add-smb208-support.patch create mode 100644 target/linux/ipq806x/patches-4.14/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch create mode 100644 target/linux/ipq806x/patches-4.14/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch create mode 100644 target/linux/ipq806x/patches-4.14/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch create mode 100644 target/linux/ipq806x/patches-4.14/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch create mode 100644 target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch create mode 100644 target/linux/ipq806x/patches-4.14/0063-2-tsens-support-configurable-interrupts.patch create mode 100644 target/linux/ipq806x/patches-4.14/0064-clk-clk-rpm-fixes.patch create mode 100644 target/linux/ipq806x/patches-4.14/0065-arm-override-compiler-flags.patch create mode 100644 target/linux/ipq806x/patches-4.14/0066-GPIO-add-named-gpio-exports.patch create mode 100644 target/linux/ipq806x/patches-4.14/0067-generic-Mangle-bootloader-s-kernel-arguments.patch create mode 100644 target/linux/ipq806x/patches-4.14/0068-spi-add-gpio-cs-support.patch create mode 100644 target/linux/ipq806x/patches-4.14/0069-arm-boot-add-dts-files.patch create mode 100644 target/linux/ipq806x/patches-4.14/0070-qcom-spm-fix-probe-order.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-1-PCI-qcom-Fixed-IPQ806x-specific-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-2-PCI-qcom-Fixed-IPQ806x-PCIE-reset-changes.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-3-PCI-qcom-Fixed-IPQ806x-PCIE-init-changes.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-5-PCI-qcom-Programming-the-PCIE-iATU-for-IPQ806x.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-6-PCI-qcom-Force-GEN1-support.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-7-pcie-Set-PCIE-MRRS-and-MPS-to-256B.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-8-pcie-qcom-Fixed-pcie_phy_clk-branch-issue.patch create mode 100644 target/linux/ipq806x/patches-4.14/0071-9-pcie-qcom-change-duplicate-pci-reset-to-phy-reset.patch create mode 100644 target/linux/ipq806x/patches-4.14/0072-firmware-scm-add-ipq806x-compatible-string.patch create mode 100644 target/linux/ipq806x/patches-4.14/0073-pinctrl-qom-use-scm_call-to-route-GPIO-irq-to-Apps.patch create mode 100644 target/linux/ipq806x/patches-4.14/104-mtd-nand-add-Winbond-manufacturer-and-chip.patch create mode 100644 target/linux/ipq806x/patches-4.14/105-mtd-nor-add-mx25l25635f.patch rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch (98%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch (96%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/307-ARM-qcom-Add-IPQ4019-SoC-support.patch (94%) create mode 100644 target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch (93%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch (95%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/700-net-add-qualcomm-mdio-and-phy.patch (99%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/701-dts-ipq4019-add-mdio-node.patch (95%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/702-dts-ipq4019-add-PHY-switch-nodes.patch (98%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/710-net-add-qualcomm-essedma-ethernet-driver.patch (99%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/711-dts-ipq4019-add-ethernet-essedma-node.patch (99%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/712-net-essedma-disable-default-vlan.patch (98%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch (99%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/830-usb-dwc3-register-qca-ipq4019-dwc3-in-dwc3-of-simple.patch (100%) create mode 100644 target/linux/ipq806x/patches-4.14/850-soc-add-qualcomm-syscon.patch rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch (98%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-01-dts-ipq4019-ap-dk04-fix-pinctrl-node-name.patch (100%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-02-dts-ipq4019-ap-dk04-remove-xo-and-timer-nodes.patch (100%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch (93%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch (95%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch (95%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-06-dts-ipq4019-fix-max-cpu-speed.patch (100%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-07-dts-ipq4019-ap-dk01.1-c1-add-spi-and-ram-nodes.patch (100%) rename target/linux/ipq806x/{patches-4.9 => patches-4.14}/864-08-dts-ipq4019-ap-dk01.1-c1-add-compatible-string.patch (100%) create mode 100644 target/linux/ipq806x/patches-4.14/999-boost-rpm-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch create mode 100644 target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch create mode 100644 target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch create mode 100644 target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch create mode 100644 target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch create mode 100644 target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch create mode 100644 target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch create mode 100644 target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch create mode 100644 target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch create mode 100644 target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch create mode 100644 target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch create mode 100644 target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch create mode 100644 target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch create mode 100644 target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch create mode 100644 target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch create mode 100644 target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch create mode 100644 target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch create mode 100644 target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch create mode 100644 target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch create mode 100644 target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch create mode 100644 target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch create mode 100644 target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch create mode 100644 target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch create mode 100644 target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch create mode 100644 target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch create mode 100644 target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch create mode 100644 target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch create mode 100644 target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch create mode 100644 target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch create mode 100644 target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch create mode 100644 target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch create mode 100644 target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch create mode 100644 target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch create mode 100644 target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch create mode 100644 target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch create mode 100644 target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch create mode 100644 target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch create mode 100644 target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch create mode 100644 target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch create mode 100644 target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch create mode 100644 target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch create mode 100644 target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch create mode 100644 target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch create mode 100644 target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch create mode 100644 target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch create mode 100644 target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch create mode 100644 target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch create mode 100644 target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch create mode 100644 target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch create mode 100644 target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch create mode 100644 target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch create mode 100644 target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch create mode 100644 target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch create mode 100644 target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch create mode 100644 target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch create mode 100644 target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch create mode 100644 target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch create mode 100644 target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch create mode 100644 target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch create mode 100644 target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch create mode 100644 target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch create mode 100644 target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch create mode 100644 target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch create mode 100644 target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch create mode 100644 target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch create mode 100644 target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch create mode 100644 target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch create mode 100644 target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch create mode 100644 target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch create mode 100644 target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch create mode 100644 target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch create mode 100644 target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch create mode 100644 target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch create mode 100644 target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch create mode 100644 target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch create mode 100644 target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch create mode 100644 target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch create mode 100644 target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch create mode 100644 target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch create mode 100644 target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch create mode 100644 target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch create mode 100644 target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch create mode 100644 target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch create mode 100644 target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch create mode 100644 target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch create mode 100644 target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch create mode 100644 target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch create mode 100644 target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch create mode 100644 target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch create mode 100644 target/linux/ipq806x/patches-4.4/800-devicetree.patch create mode 100644 target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch create mode 100644 target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch create mode 100644 target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch delete mode 100644 target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch delete mode 100644 target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch delete mode 100644 target/linux/ipq806x/patches-4.9/308-dts-ipq4019-add-both-IPQ4019-wifi-block-definitions.patch delete mode 100644 target/linux/ipq806x/patches-4.9/309-dts-ipq4019-add-pseudo-random-number-generator.patch delete mode 100644 target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch delete mode 100644 target/linux/ipq806x/patches-4.9/859-msm-pinctrl-Add-support-to-configure-ipq40xx-GPIO_PU.patch delete mode 100644 target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch delete mode 100644 target/linux/ipq806x/patches-4.9/861-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch delete mode 100644 target/linux/ipq806x/patches-4.9/862-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch delete mode 100644 target/linux/ipq806x/patches-4.9/864-00-v3-1-2-dts-ipq4019-Fix-pinctrl-node-name.patch delete mode 100644 target/linux/ipq806x/patches-4.9/864-00-v3-2-2-dts-ipq4019-Move-xo-and-timer-nodes-to-SoC-dtsi.patch delete mode 100644 target/linux/ipq806x/patches-4.9/900-mach-qcom-add-msm_pcie-driver.patch diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile index 983106186..13be76e7b 100644 --- a/target/linux/ipq806x/Makefile +++ b/target/linux/ipq806x/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk ARCH:=arm BOARD:=ipq806x BOARDNAME:=Qualcomm Atheros IPQ806X -FEATURES:=squashfs nand ubifs fpu ramdisk +FEATURES:=squashfs nand fpu CPU_TYPE:=cortex-a15 CPU_SUBTYPE:=neon-vfpv4 MAINTAINER:=John Crispin -KERNEL_PATCHVER:=4.9 +KERNEL_PATCHVER:=4.14 KERNELNAME:=zImage Image dtbs diff --git a/target/linux/ipq806x/base-files/etc/board.d/01_leds b/target/linux/ipq806x/base-files/etc/board.d/01_leds index 8e897141a..a8d9573fa 100755 --- a/target/linux/ipq806x/base-files/etc/board.d/01_leds +++ b/target/linux/ipq806x/base-files/etc/board.d/01_leds @@ -11,27 +11,9 @@ board=$(board_name) boardname="${board##*,}" case "$board" in -asus,rt-acrh17) - ucidef_set_led_default "status" "STATUS" "${boardname}:blue:status" "1" - ucidef_set_led_wlan "wlan2g" "WLAN2G" "${boardname}:blue:wlan2g" "phy1tpt" - ucidef_set_led_wlan "wlan5g" "WLAN5G" "${boardname}:blue:wlan5g" "phy0tpt" - ucidef_set_led_switch "wan" "WAN(blue)" "${boardname}:blue:wan" "switch0" "0x20" - ucidef_set_led_switch "lan1" "LAN1" "${boardname}:blue:lan1" "switch0" "0x02" - ucidef_set_led_switch "lan2" "LAN2" "${boardname}:blue:lan2" "switch0" "0x04" - ucidef_set_led_switch "lan3" "LAN3" "${boardname}:blue:lan3" "switch0" "0x08" - ucidef_set_led_switch "lan4" "LAN4" "${boardname}:blue:lan4" "switch0" "0x10" - ;; -asus,rt-ac58u) - ucidef_set_led_wlan "wlan2g" "WLAN2G" "$board:blue:wlan2G" "phy0tpt" - ucidef_set_led_wlan "wlan5g" "WLAN5G" "$board:blue:wlan5G" "phy1tpt" - ucidef_set_led_switch "wan" "WAN" "${board}:blue:wan" "switch0" "0x20" - ucidef_set_led_usbport "usb" "USB" "${board}:blue:usb" "usb1-port1" "usb2-port1" "usb3-port1" "usb4-port1" - ucidef_set_led_switch "lan" "LAN" "${board}:blue:lan" "switch0" "0x1e" - ;; -avm,fritzbox-4040) - ucidef_set_led_wlan "wlan" "WLAN" "fritz4040:green:wlan" "phy0tpt" "phy1tpt" - ucidef_set_led_netdev "wan" "WAN" "fritz4040:green:wan" "eth1" - ucidef_set_led_switch "lan" "LAN" "fritz4040:green:lan" "switch0" "0x1e" +compex,wpq864) + ucidef_set_led_usbport "usb" "USB" "wpq864:green:usb" "usb1-port1" "usb2-port1" + ucidef_set_led_usbport "pcie-usb" "PCIe USB" "wpq864:green:usb-pcie" "usb3-port1" ;; netgear,d7800 |\ netgear,r7500 |\ diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network b/target/linux/ipq806x/base-files/etc/board.d/02_network index f8d7f7469..9d8af4485 100755 --- a/target/linux/ipq806x/base-files/etc/board.d/02_network +++ b/target/linux/ipq806x/base-files/etc/board.d/02_network @@ -12,36 +12,7 @@ board_config_update board=$(board_name) case "$board" in -asus,rt-acrh17) - CI_UBIPART=UBI_DEV - lan_mac_addr=$(mtd_get_mac_binary_ubi Factory 4102) - wan_mac_addr=$(mtd_get_mac_binary_ubi Factory 36870) - ucidef_add_switch "switch0" \ - "0t@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan" - ucidef_set_interface_macaddr "lan" "$lan_mac_addr" - ucidef_set_interface_macaddr "wan" "$wan_mac_addr" - ;; -asus,rt-ac58u) - CI_UBIPART=UBI_DEV - wan_mac_addr=$(mtd_get_mac_binary_ubi Factory 20486) - lan_mac_addr=$(mtd_get_mac_binary_ubi Factory 4102) - ucidef_add_switch "switch0" \ - "0t@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan" - ucidef_set_interface_macaddr "lan" "$lan_mac_addr" - ucidef_set_interface_macaddr "wan" "$wan_mac_addr" - ;; -avm,fritzbox-4040) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" - ;; -linksys,ea8500) - hw_mac_addr=$(mtd_get_mac_ascii devinfo hw_mac_addr) - ucidef_add_switch "switch0" \ - "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan" - ucidef_set_interface_macaddr "lan" "$hw_mac_addr" - ucidef_set_interface_macaddr "wan" "$hw_mac_addr" - ;; +compex,wpq864 |\ netgear,d7800 |\ netgear,r7500 |\ netgear,r7500v2 |\ @@ -51,8 +22,12 @@ tplink,vr2600v) ucidef_add_switch "switch0" \ "1:lan" "2:lan" "3:lan" "4:lan" "6@eth1" "5:wan" "0@eth0" ;; -openmesh,a42) - ucidef_set_interfaces_lan_wan "eth1" "eth0" +linksys,ea8500) + hw_mac_addr=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ucidef_add_switch "switch0" \ + "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan" + ucidef_set_interface_macaddr "lan" "$hw_mac_addr" + ucidef_set_interface_macaddr "wan" "$hw_mac_addr" ;; qcom,ipq8064-db149) ucidef_set_interface_lan "eth1 eth2 eth3" diff --git a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata index 45d4788a2..ed99bd5ea 100644 --- a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata +++ b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata @@ -28,21 +28,6 @@ ath10kcal_extract() { ath10kcal_die "failed to extract calibration data from $mtd" } -ath10kcal_ubi_extract() { - local part=$1 - local offset=$2 - local count=$3 - local ubidev=$(nand_find_ubi $CI_UBIPART) - local ubi - - ubi=$(nand_find_volume $ubidev $part) - [ -n "$ubi" ] || \ - ath10kcal_die "no UBI volume found for $part" - - dd if=/dev/$ubi of=/lib/firmware/$FIRMWARE bs=1 skip=$offset count=$count 2>/dev/null || \ - ath10kcal_die "failed to extract from $ubi" -} - ath10kcal_patch_mac() { local mac=$1 @@ -60,57 +45,8 @@ board=$(board_name) case "$FIRMWARE" in -"ath10k/pre-cal-ahb-a000000.wifi.bin") - case "$board" in - asus,rt-acrh17) - CI_UBIPART=UBI_DEV - . /lib/upgrade/nand.sh - ath10kcal_ubi_extract "Factory" 4096 12064 - ;; - asus,rt-ac58u) - . /lib/upgrade/nand.sh - - CI_UBIPART=UBI_DEV - ath10kcal_ubi_extract "Factory" 4096 12064 - ;; - avm,fritzbox-4040) - /usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config") - ;; - openmesh,a42) - ath10kcal_extract "0:ART" 4096 12064 - ;; - qcom,ap-dk01.1-c1) - ath10kcal_extract "ART" 4096 12064 - ;; - esac - ;; -"ath10k/pre-cal-ahb-a800000.wifi.bin") - case "$board" in - asus,rt-ac58u) - . /lib/upgrade/nand.sh - - CI_UBIPART=UBI_DEV - ath10kcal_ubi_extract "Factory" 20480 12064 - ;; - avm,fritzbox-4040) - /usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config") - ;; - openmesh,a42) - ath10kcal_extract "0:ART" 20480 12064 - ;; - qcom,ap-dk01.1-c1) - ath10kcal_extract "ART" 20480 12064 - ;; - esac - ;; - "ath10k/pre-cal-pci-0000:01:00.0.bin") case $board in - asus,rt-acrh17) - CI_UBIPART=UBI_DEV - . /lib/upgrade/nand.sh - ath10kcal_ubi_extract "Factory" 36864 12064 - ;; linksys,ea8500) hw_mac_addr=$(mtd_get_mac_ascii devinfo hw_mac_addr) ath10kcal_extract "art" 4096 12064 diff --git a/target/linux/ipq806x/base-files/lib/preinit/06_set_iface_mac b/target/linux/ipq806x/base-files/lib/preinit/06_set_iface_mac deleted file mode 100644 index f0c7dda87..000000000 --- a/target/linux/ipq806x/base-files/lib/preinit/06_set_iface_mac +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright (C) 2014-2015 OpenWrt.org -# Copyright (C) 2016 LEDE-Project.org -# - -preinit_set_mac_address() { - local mac - - . /lib/functions.sh - - case $(board_name) in - asus,rt-ac58u|\ - asus,rt-acrh17) - CI_UBIPART=UBI_DEV - mac=$(mtd_get_mac_binary_ubi Factory 4102) - ifconfig eth0 hw ether $mac 2>/dev/null - ;; - esac -} - -boot_hook_add preinit_main preinit_set_mac_address diff --git a/target/linux/ipq806x/base-files/lib/upgrade/openmesh.sh b/target/linux/ipq806x/base-files/lib/upgrade/openmesh.sh deleted file mode 100644 index a2c79d9e4..000000000 --- a/target/linux/ipq806x/base-files/lib/upgrade/openmesh.sh +++ /dev/null @@ -1,110 +0,0 @@ -# The U-Boot loader of the OpenMesh devices requires image sizes and -# checksums to be provided in the U-Boot environment. -# The OpenMesh devices come with 2 main partitions - while one is active -# sysupgrade will flash the other. The boot order is changed to boot the -# newly flashed partition. If the new partition can't be booted due to -# upgrade failures the previously used partition is loaded. - -platform_do_upgrade_openmesh() { - local tar_file="$1" - local restore_backup - local primary_kernel_mtd - - local setenv_script="/tmp/fw_env_upgrade" - - local kernel_mtd="$(find_mtd_index $PART_NAME)" - local kernel_offset="$(cat /sys/class/mtd/mtd${kernel_mtd}/offset)" - local total_size="$(cat /sys/class/mtd/mtd${kernel_mtd}/size)" - - # detect to which flash region the new image is written to. - # - # 1. check what is the mtd index for the first flash region on this - # device - # 2. check if the target partition ("inactive") has the mtd index of - # the first flash region - # - # - when it is: the new bootseq will be 1,2 and the first region is - # modified - # - when it isnt: bootseq will be 2,1 and the second region is - # modified - # - # The detection has to be done via the hardcoded mtd partition because - # the current boot might be done with the fallback region. Let us - # assume that the current bootseq is 1,2. The bootloader detected that - # the image in flash region 1 is corrupt and thus switches to flash - # region 2. The bootseq in the u-boot-env is now still the same and - # the sysupgrade code can now only rely on the actual mtd indexes and - # not the bootseq variable to detect the currently booted flash - # region/image. - # - # In the above example, an implementation which uses bootseq ("1,2") to - # detect the currently booted image would assume that region 1 is booted - # and then overwrite the variables for the wrong flash region (aka the - # one which isn't modified). This could result in a device which doesn't - # boot anymore to Linux until it was reflashed with ap51-flash. - local next_boot_part="1" - case "$(board_name)" in - openmesh,a42) - primary_kernel_mtd=8 - ;; - *) - echo "failed to detect primary kernel mtd partition for board" - return 1 - ;; - esac - [ "$kernel_mtd" = "$primary_kernel_mtd" ] || next_boot_part="2" - - local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') - board_dir=${board_dir%/} - - local kernel_length=$(tar xf $tar_file ${board_dir}/kernel -O | wc -c) - local rootfs_length=$(tar xf $tar_file ${board_dir}/root -O | wc -c) - # rootfs without EOF marker - rootfs_length=$((rootfs_length-4)) - - local kernel_md5=$(tar xf $tar_file ${board_dir}/kernel -O | md5sum); kernel_md5="${kernel_md5%% *}" - # md5 checksum of rootfs with EOF marker - local rootfs_md5=$(tar xf $tar_file ${board_dir}/root -O | dd bs=1 count=$rootfs_length | md5sum); rootfs_md5="${rootfs_md5%% *}" - - # - # add tar support to get_image() to use default_do_upgrade() instead? - # - - # take care of restoring a saved config - [ "$SAVE_CONFIG" -eq 1 ] && restore_backup="${MTD_CONFIG_ARGS} -j ${CONF_TAR}" - - # write concatinated kernel + rootfs to flash - tar xf $tar_file ${board_dir}/kernel ${board_dir}/root -O | \ - mtd $restore_backup write - $PART_NAME - - # prepare new u-boot env - if [ "$next_boot_part" = "1" ]; then - echo "bootseq 1,2" > $setenv_script - else - echo "bootseq 2,1" > $setenv_script - fi - - printf "kernel_size_%i 0x%08x\n" $next_boot_part $kernel_length >> $setenv_script - printf "vmlinux_start_addr 0x%08x\n" ${kernel_offset} >> $setenv_script - printf "vmlinux_size 0x%08x\n" ${kernel_length} >> $setenv_script - printf "vmlinux_checksum %s\n" ${kernel_md5} >> $setenv_script - - printf "rootfs_size_%i 0x%08x\n" $next_boot_part $((total_size-kernel_length)) >> $setenv_script - printf "rootfs_start_addr 0x%08x\n" $((kernel_offset+kernel_length)) >> $setenv_script - printf "rootfs_size 0x%08x\n" ${rootfs_length} >> $setenv_script - printf "rootfs_checksum %s\n" ${rootfs_md5} >> $setenv_script - - # store u-boot env changes - fw_setenv -s $setenv_script || { - echo "failed to update U-Boot environment" - return 1 - } -} - -# create /var/lock for the lock "fw_setenv.lock" of fw_setenv -# the rest is copied using ipq806x's RAMFS_COPY_BIN and RAMFS_COPY_DATA -platform_add_ramfs_ubootenv() -{ - mkdir -p $RAM_ROOT/var/lock -} -append sysupgrade_pre_upgrade platform_add_ramfs_ubootenv diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh index 571c9cc8a..4f230eac8 100644 --- a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh +++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh @@ -10,33 +10,17 @@ platform_check_image() { platform_do_upgrade() { case "$(board_name)" in - asus,rt-ac58u|\ - asus,rt-acrh17) - CI_UBIPART="UBI_DEV" - CI_KERNPART="linux" - - local ubidev=$(nand_find_ubi $CI_UBIPART) - local jffs2=$(nand_find_volume $ubidev jffs2) - local linux2=$(nand_find_volume $ubidev linux2) - [ -n "$jffs2" ] && ubirmvol /dev/$ubidev --name=jffs2 - [ -n "$linux2" ] && ubirmvol /dev/$ubidev --name=linux2 - nand_do_upgrade "$1" - ;; - linksys,ea8500) - platform_do_upgrade_linksys "$ARGV" - ;; + compex,wpq864|\ netgear,d7800 |\ netgear,r7500 |\ netgear,r7500v2 |\ netgear,r7800 |\ - qcom,ap-dk04.1-c1 |\ qcom,ipq8064-ap148 |\ zyxel,nbg6817) nand_do_upgrade "$ARGV" ;; - openmesh,a42) - PART_NAME="inactive" - platform_do_upgrade_openmesh "$ARGV" + linksys,ea8500) + platform_do_upgrade_linksys "$ARGV" ;; tplink,c2600) PART_NAME="os-image:rootfs" @@ -56,10 +40,6 @@ platform_do_upgrade() { platform_nand_pre_upgrade() { case "$(board_name)" in - asus,rt-ac58u) - CI_UBIPART="UBI_DEV" - CI_KERNPART="linux" - ;; zyxel,nbg6817) zyxel_do_upgrade "$1" ;; diff --git a/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh b/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh index 3efef8f0b..ba8e82595 100644 --- a/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh +++ b/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh @@ -21,6 +21,7 @@ zyxel_do_flash() { local tar_file=$1 local kernel=$2 local rootfs=$3 + local dualflagmtd=$4 # keep sure its unbound losetup --detach-all || { @@ -32,8 +33,8 @@ zyxel_do_flash() { local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') board_dir=${board_dir%/} - echo "flashing kernel to /dev/${kernel}" - tar xf $tar_file ${board_dir}/kernel -O >/dev/$kernel + echo "flashing kernel to $kernel" + tar xf $tar_file ${board_dir}/kernel -O >$kernel echo "flashing rootfs to ${rootfs}" tar xf $tar_file ${board_dir}/root -O >"${rootfs}" @@ -47,23 +48,34 @@ zyxel_do_flash() { } # Mount loop for rootfs_data - losetup -o $offset /dev/loop0 "${rootfs}" || { + local loopdev="$(losetup -f)" + losetup -o $offset $loopdev $rootfs || { echo "Failed to mount looped rootfs_data." sleep 10 reboot -f } echo "Format new rootfs_data at position ${offset}." - mkfs.ext4 -F -L rootfs_data /dev/loop0 + mkfs.ext4 -F -L rootfs_data $loopdev mkdir /tmp/new_root - mount -t ext4 /dev/loop0 /tmp/new_root && { + mount -t ext4 $loopdev /tmp/new_root && { echo "Saving config to rootfs_data at position ${offset}." cp -v /tmp/sysupgrade.tgz /tmp/new_root/ umount /tmp/new_root } + # flashing successful, toggle the dualflag + case "$rootfs" in + "/dev/mmcblk0p5") + printf "\xff" >$dualflagmtd + ;; + "/dev/mmcblk0p8") + printf "\x01" >$dualflagmtd + ;; + esac + # Cleanup - losetup -d /dev/loop0 >/dev/null 2>&1 + losetup -d $loopdev >/dev/null 2>&1 sync umount -a reboot -f @@ -78,12 +90,21 @@ zyxel_do_upgrade() { [ -b "${rootfs}" ] || return 1 case "$board" in zyxel,nbg6817) + local dualflagmtd="$(find_mtd_part 0:DUAL_FLAG)" + [ -b $dualflagmtd ] || return 1 + case "$rootfs" in "/dev/mmcblk0p5") - kernel=mmcblk0p4 + # booted from the primary partition set + # write to the alternative set + kernel="/dev/mmcblk0p7" + rootfs="/dev/mmcblk0p8" ;; "/dev/mmcblk0p8") - kernel=mmcblk0p7 + # booted from the alternative partition set + # write to the primary set + kernel="/dev/mmcblk0p4" + rootfs="/dev/mmcblk0p5" ;; *) return 1 @@ -95,7 +116,7 @@ zyxel_do_upgrade() { ;; esac - zyxel_do_flash $tar_file $kernel $rootfs + zyxel_do_flash $tar_file $kernel $rootfs $dualflagmtd return 0 } diff --git a/target/linux/ipq806x/config-4.14 b/target/linux/ipq806x/config-4.14 new file mode 100644 index 000000000..3b5f77183 --- /dev/null +++ b/target/linux/ipq806x/config-4.14 @@ -0,0 +1,565 @@ +CONFIG_ALIGNMENT_TRAP=y +CONFIG_APQ_GCC_8084=y +CONFIG_APQ_MMCC_8084=y +CONFIG_AR40XX_PHY=y +CONFIG_AR8216_PHY=y +# CONFIG_ARCH_ACTIONS is not set +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_ARCH_HAS_SET_MEMORY=y +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_IPQ40XX=y +# CONFIG_ARCH_MDM9615 is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MSM8960=y +CONFIG_ARCH_MSM8974=y +CONFIG_ARCH_MSM8X60=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_QCOM=y +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +# CONFIG_ARCH_WANTS_THP_SWAP is not set +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_CPU_SUSPEND=y +# CONFIG_ARM_CPU_TOPOLOGY is not set +CONFIG_ARM_GIC=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +# CONFIG_ARM_LPAE is not set +CONFIG_ARM_PATCH_IDIV=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_QCOM_CPUFREQ=y +CONFIG_ARM_QCOM_CPUIDLE=y +# CONFIG_ARM_SMMU is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_AT803X_PHY=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BOUNCE=y +CONFIG_BUS_TOPOLOGY_ADHOC=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_QCOM=y +# CONFIG_CLK_HSDK is not set +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_QCOM=y +# CONFIG_COMMON_CLK_VC5 is not set +CONFIG_COMPACTION=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# 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_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_HAS_ASID=y +# CONFIG_CPU_ICACHE_DISABLE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_CRYPTO_XTS=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_INCLUDE="debug/msm.S" +CONFIG_DEBUG_QCOM_UARTDM=y +# CONFIG_DEBUG_UART_8250 is not set +CONFIG_DEBUG_UART_PHYS=0x16340000 +CONFIG_DEBUG_UART_VIRT=0xf6340000 +CONFIG_DEBUG_UNCOMPRESS=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_VIRTUAL is not set +CONFIG_DEFAULT_BBR=y +# CONFIG_DEFAULT_CUBIC is not set +CONFIG_DEFAULT_TCP_CONG="bbr" +# CONFIG_DEFAULT_VEGAS is not set +CONFIG_TCP_CONG_BBR=y +CONFIG_TCP_CONG_VEGAS=y +CONFIG_TCP_CONG_YEAH=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +# CONFIG_DMA_NOOP_OPS is not set +CONFIG_DMA_OF=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +# CONFIG_DMA_VIRT_OPS is not set +# CONFIG_DRM_LIB_RANDOM is not set +CONFIG_DTC=y +CONFIG_DT_IDLE_STATES=y +# CONFIG_DWMAC_DWC_QOS_ETH is not set +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_IPQ806X=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EARLY_PRINTK=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_ESSEDMA=y +CONFIG_ETHERNET_PACKET_MANGLE=y +CONFIG_EXPORTFS=y +CONFIG_EXTCON=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FUTEX_PI=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_SYSFS=y +# CONFIG_GRO_CELLS is not set +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +CONFIG_HAVE_ARCH_BITREVERSE=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +CONFIG_HAVE_ARM_SMCCC=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_CC_STACKPROTECTOR=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_EBPF_JIT=y +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_SMP=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_UID16=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +# CONFIG_HNS3 is not set +CONFIG_HWMON=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_HZ_FIXED=0 +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_QUP=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IOMMU_HELPER=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IPQ_GCC_4019=y +CONFIG_IPQ_GCC_806X=y +# CONFIG_IPQ_GCC_8074 is not set +# CONFIG_IPQ_LCC_806X is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_KPSS_XCC=y +CONFIG_KRAITCC=y +CONFIG_KRAIT_CLOCKS=y +CONFIG_KRAIT_L2_ACCESSORS=y +CONFIG_LIBFDT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_GPIO=y +CONFIG_MDIO_IPQ40XX=y +# CONFIG_MDM_GCC_9615 is not set +# CONFIG_MDM_LCC_9615 is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_PM8XXX is not set +CONFIG_MFD_QCOM_RPM=y +# CONFIG_MFD_SPMI_PMIC is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MICROCHIP_KSZ is not set +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_QCOM_DML=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_TIFM_SD is not set +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MSM_BUS_SCALING=y +CONFIG_MSM_GCC_8660=y +# CONFIG_MSM_GCC_8916 is not set +CONFIG_MSM_GCC_8960=y +CONFIG_MSM_GCC_8974=y +# CONFIG_MSM_GCC_8994 is not set +# CONFIG_MSM_GCC_8996 is not set +# CONFIG_MSM_IOMMU is not set +# CONFIG_MSM_LCC_8960 is not set +CONFIG_MSM_MMCC_8960=y +CONFIG_MSM_MMCC_8974=y +# CONFIG_MSM_MMCC_8996 is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_QCOM_SMEM_PARTS=y +CONFIG_MTD_SPINAND_MT29F=y +CONFIG_MTD_SPINAND_ONDIEECC=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +# CONFIG_MTD_UBI_FASTMAP is not set +CONFIG_MTD_UBI_GLUEBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MULTI_IRQ_HANDLER=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEON=y +CONFIG_NET_DSA=y +# CONFIG_NET_DSA_LOOP is not set +# CONFIG_NET_DSA_MT7530 is not set +CONFIG_NET_DSA_QCA8K=y +# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set +# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set +CONFIG_NET_DSA_TAG_QCA=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_BOOTMEM=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_ADDRESS_PCI=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PADATA=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +# CONFIG_PCI_FTPCI100 is not set +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +# CONFIG_PHY_QCOM_APQ8064_SATA is not set +CONFIG_PHY_QCOM_IPQ806X_SATA=y +# CONFIG_PHY_QCOM_QMP is not set +# CONFIG_PHY_QCOM_QUSB2 is not set +# CONFIG_PHY_QCOM_UFS is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_APQ8064=y +# CONFIG_PINCTRL_APQ8084 is not set +CONFIG_PINCTRL_IPQ4019=y +CONFIG_PINCTRL_IPQ8064=y +# CONFIG_PINCTRL_IPQ8074 is not set +# CONFIG_PINCTRL_MDM9615 is not set +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8660 is not set +# CONFIG_PINCTRL_MSM8916 is not set +# CONFIG_PINCTRL_MSM8960 is not set +# CONFIG_PINCTRL_MSM8994 is not set +# CONFIG_PINCTRL_MSM8996 is not set +CONFIG_PINCTRL_MSM8X74=y +# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +CONFIG_PM=y +CONFIG_PM_CLK=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_PM_OPP=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_SUPPLY=y +CONFIG_PPS=y +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_QCOM_ADM=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_CLK_RPM=y +# CONFIG_QCOM_EBI2 is not set +CONFIG_QCOM_GDSC=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_HFPLL=y +# CONFIG_QCOM_IOMMU is not set +CONFIG_QCOM_PM=y +CONFIG_QCOM_QFPROM=y +CONFIG_QCOM_RPMCC=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SCM_32=y +CONFIG_QCOM_SMEM=y +# CONFIG_QCOM_SMP2P is not set +# CONFIG_QCOM_SMSM is not set +CONFIG_QCOM_TCSR=y +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_WDT=y +# CONFIG_QRTR is not set +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPI=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_QCOM_RPM=y +# CONFIG_REGULATOR_QCOM_SPMI is not set +# CONFIG_REGULATOR_TPS65132 is not set +# CONFIG_REGULATOR_VCTRL is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_RESET_LANTIQ is not set +CONFIG_RFS_ACCEL=y +# CONFIG_RPMSG_QCOM_SMD is not set +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_CMOS is not set +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +# CONFIG_SCHED_INFO is not set +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_FSL=y +# CONFIG_SERIAL_AMBA_PL011 is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_QUP=y +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB=y +CONFIG_SRCU=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_STRICT_MODULE_RWX=y +CONFIG_SWCONFIG=y +CONFIG_SWCONFIG_LEDS=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_TEE 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_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THIN_ARCHIVES=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_USB=y +CONFIG_USB_COMMON=y +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_IPQ4019_PHY=y +CONFIG_USB_PHY=y +# CONFIG_USB_QCOM_8X16_PHY is not set +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VDSO=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_WATCHDOG_CORE=y +CONFIG_XPS=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4 new file mode 100644 index 000000000..898e32ff0 --- /dev/null +++ b/target/linux/ipq806x/config-4.4 @@ -0,0 +1,426 @@ +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_AMBA_PL08X is not set +CONFIG_APQ_GCC_8084=y +CONFIG_APQ_MMCC_8084=y +CONFIG_AR8216_PHY=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MSM8960=y +CONFIG_ARCH_MSM8974=y +CONFIG_ARCH_MSM8X60=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_QCOM=y +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y +CONFIG_ARM_CPU_SUSPEND=y +# CONFIG_ARM_CPU_TOPOLOGY is not set +CONFIG_ARM_GIC=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +# CONFIG_ARM_LPAE is not set +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_QCOM_CPUFREQ=y +CONFIG_ARM_QCOM_CPUIDLE=y +# CONFIG_ARM_SMMU is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_AT803X_PHY=y +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_BOUNCE=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_PROBE=y +CONFIG_CLKSRC_QCOM=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_HAS_ASID=y +# CONFIG_CPU_ICACHE_DISABLE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEBUG_UART_8250 is not set +# CONFIG_DEBUG_USER is not set +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_IPQ806X=y +# CONFIG_DWMAC_SUNXI is not set +# CONFIG_DW_DMAC_PCI is not set +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_ETHERNET_PACKET_MANGLE=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_MSI_IRQ=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=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIO_SYSFS=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_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +CONFIG_HAVE_ARCH_BITREVERSE=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_BPF_JIT=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_SMP=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_UID16=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HWMON=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_HZ_FIXED=0 +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_QUP=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IOMMU_HELPER=y +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IPQ_GCC_806X=y +# CONFIG_IPQ_LCC_806X is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_KPSS_XCC=y +CONFIG_KRAITCC=y +CONFIG_KRAIT_CLOCKS=y +CONFIG_KRAIT_L2_ACCESSORS=y +CONFIG_LIBFDT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BOARDINFO=y +CONFIG_MDIO_GPIO=y +CONFIG_MFD_QCOM_RPM=y +# CONFIG_MFD_SPMI_PMIC is not set +CONFIG_MFD_SYSCON=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_QCOM_DML=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_TIFM_SD is not set +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MSM_GCC_8660=y +# CONFIG_MSM_GCC_8916 is not set +CONFIG_MSM_GCC_8960=y +CONFIG_MSM_GCC_8974=y +# CONFIG_MSM_LCC_8960 is not set +CONFIG_MSM_MMCC_8960=y +CONFIG_MSM_MMCC_8974=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_QCOM_SMEM_PARTS=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MULTI_IRQ_HANDLER=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEON=y +CONFIG_NET_DSA=y +# CONFIG_NET_DSA_AR8XXX is not set +CONFIG_NET_DSA_HWMON=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_BOOTMEM=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_ADDRESS_PCI=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +# CONFIG_PHY_QCOM_APQ8064_SATA is not set +CONFIG_PHY_QCOM_IPQ806X_SATA=y +# CONFIG_PHY_QCOM_UFS is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_APQ8064=y +# CONFIG_PINCTRL_APQ8084 is not set +CONFIG_PINCTRL_IPQ8064=y +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8660 is not set +# CONFIG_PINCTRL_MSM8916 is not set +# CONFIG_PINCTRL_MSM8960 is not set +CONFIG_PINCTRL_MSM8X74=y +# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +# CONFIG_PL330_DMA is not set +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_SUPPLY=y +CONFIG_PPS=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_QCOM_ADM=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_CLK_RPM=y +CONFIG_QCOM_GDSC=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_HFPLL=y +CONFIG_QCOM_PM=y +CONFIG_QCOM_QFPROM=y +CONFIG_QCOM_RPMCC=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SCM_32=y +# CONFIG_QCOM_SMD is not set +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_WDT=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_STALL_COMMON=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_QCOM_RPM=y +# CONFIG_REGULATOR_QCOM_SPMI is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_CMOS is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_SCHED_HRTICK=y +# CONFIG_SCHED_INFO is not set +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_FSL=y +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_QUP=y +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB=y +CONFIG_SRCU=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +CONFIG_SWCONFIG=y +CONFIG_SWCONFIG_LEDS=y +CONFIG_SWIOTLB=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TREE_RCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VDSO=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_SYSFS is not set +# CONFIG_WL_TI is not set +CONFIG_XPS=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/config-4.9 b/target/linux/ipq806x/config-4.9 index 376ffb85c..6bc3daf27 100644 --- a/target/linux/ipq806x/config-4.9 +++ b/target/linux/ipq806x/config-4.9 @@ -2,7 +2,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_AMBA_PL08X is not set CONFIG_APQ_GCC_8084=y CONFIG_APQ_MMCC_8084=y -CONFIG_AR40XX_PHY=y CONFIG_AR8216_PHY=y CONFIG_ARCH_CLOCKSOURCE_DATA=y CONFIG_ARCH_HAS_ELF_RANDOMIZE=y @@ -11,7 +10,6 @@ CONFIG_ARCH_HAS_SG_CHAIN=y CONFIG_ARCH_HAS_TICK_BROADCAST=y CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_IPQ40XX=y # CONFIG_ARCH_MDM9615 is not set CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y CONFIG_ARCH_MSM8960=y @@ -60,7 +58,6 @@ CONFIG_ARM_THUMB=y # CONFIG_ARM_THUMBEE is not set CONFIG_ARM_UNWIND=y CONFIG_ARM_VIRT_EXT=y -CONFIG_AT803X_PHY=y # CONFIG_BINFMT_FLAT is not set CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_MQ_PCI=y @@ -144,6 +141,12 @@ CONFIG_CRYPTO_WORKQUEUE=y CONFIG_CRYPTO_XTS=y CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_INCLUDE="debug/msm.S" +CONFIG_DEBUG_QCOM_UARTDM=y +# CONFIG_DEBUG_UART_8250 is not set +CONFIG_DEBUG_UART_PHYS=0x16340000 +CONFIG_DEBUG_UART_VIRT=0xf6340000 CONFIG_DEBUG_UNCOMPRESS=y # CONFIG_DEBUG_USER is not set CONFIG_DMADEVICES=y @@ -158,7 +161,6 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_EARLY_PRINTK=y CONFIG_EDAC_ATOMIC_SCRUB=y CONFIG_EDAC_SUPPORT=y -CONFIG_ESSEDMA=y CONFIG_ETHERNET_PACKET_MANGLE=y CONFIG_FIXED_PHY=y CONFIG_FIX_EARLYCON_MEM=y @@ -249,7 +251,7 @@ CONFIG_IOMMU_HELPER=y # CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set # CONFIG_IOMMU_IO_PGTABLE_LPAE is not set CONFIG_IOMMU_SUPPORT=y -CONFIG_IPQ_GCC_4019=y +# CONFIG_IPQ_GCC_4019 is not set CONFIG_IPQ_GCC_806X=y # CONFIG_IPQ_LCC_806X is not set CONFIG_IRQCHIP=y @@ -269,7 +271,6 @@ CONFIG_LZO_DECOMPRESS=y CONFIG_MDIO_BITBANG=y CONFIG_MDIO_BOARDINFO=y CONFIG_MDIO_GPIO=y -CONFIG_MDIO_IPQ40XX=y # CONFIG_MDM_GCC_9615 is not set # CONFIG_MDM_LCC_9615 is not set # CONFIG_MFD_MAX77620 is not set @@ -308,8 +309,6 @@ CONFIG_MTD_NAND_QCOM=y # CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set CONFIG_MTD_QCOM_SMEM_PARTS=y CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_SPINAND_MT29F=y -CONFIG_MTD_SPINAND_ONDIEECC=y CONFIG_MTD_SPLIT_FIRMWARE=y CONFIG_MTD_SPLIT_FIT_FW=y CONFIG_MTD_UBI=y @@ -372,7 +371,7 @@ CONFIG_PHY_QCOM_IPQ806X_SATA=y CONFIG_PINCTRL=y CONFIG_PINCTRL_APQ8064=y # CONFIG_PINCTRL_APQ8084 is not set -CONFIG_PINCTRL_IPQ4019=y +# CONFIG_PINCTRL_IPQ4019 is not set CONFIG_PINCTRL_IPQ8064=y # CONFIG_PINCTRL_MDM9615 is not set CONFIG_PINCTRL_MSM=y @@ -438,7 +437,6 @@ CONFIG_RWSEM_XCHGADD_ALGORITHM=y # CONFIG_SCHED_INFO is not set # CONFIG_SCSI_DMA is not set CONFIG_SERIAL_8250_FSL=y -# CONFIG_SERIAL_AMBA_PL010 is not set # CONFIG_SERIAL_AMBA_PL011 is not set CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y @@ -477,8 +475,6 @@ CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" CONFIG_USB=y CONFIG_USB_COMMON=y # CONFIG_USB_EHCI_HCD is not set -CONFIG_USB_IPQ4019_PHY=y -CONFIG_USB_PHY=y CONFIG_USB_SUPPORT=y # CONFIG_USB_UHCI_HCD is not set CONFIG_USE_OF=y @@ -494,4 +490,3 @@ CONFIG_ZBOOT_ROM_BSS=0 CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZLIB_DEFLATE=y CONFIG_ZLIB_INFLATE=y -CONFIG_MSM_PCIE=y \ No newline at end of file diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-bus.dtsi b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-bus.dtsi similarity index 100% rename from target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-bus.dtsi rename to target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-bus.dtsi diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts similarity index 60% rename from target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts rename to target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts index f5ca3d5c5..f7b9e2c16 100644 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-fritz4040.dts @@ -14,107 +14,51 @@ * */ -#include "qcom-ipq4019.dtsi" +#include "qcom-ipq4019-ap.dk01.1.dtsi" #include "qcom-ipq4019-bus.dtsi" #include #include -#include / { model = "AVM FRITZ!Box 4040"; compatible = "avm,fritzbox-4040", "qcom,ipq4019"; - aliases { - led-boot = &power; - led-failsafe = &flash; - led-running = &power; - led-upgrade = &flash; - }; - reserved-memory { #address-cells = <0x1>; #size-cells = <0x1>; ranges; - tz_apps@87b80000 { - reg = <0x87b80000 0x280000>; - reusable; - }; - - smem@87e00000 { - reg = <0x87e00000 0x080000>; + rsvd1@87000000 { + reg = <0x87000000 0x500000>; no-map; }; - tz@87e80000 { - reg = <0x87e80000 0x180000>; + wifi_dump@87500000 { + reg = <0x87500000 0x600000>; + no-map; + }; + + rsvd2@87B00000 { + reg = <0x87b00000 0x500000>; no-map; }; }; +/* + This also works. Maybe it could be smaller still. + + reserved-memory { + #address-cells = <0x1>; + #size-cells = <0x1>; + ranges; + + rsvd1@87E00000 { + reg = <0x87e00000 0x200000>; + no-map; + }; + }; +*/ soc { - mdio@90000 { - status = "okay"; - }; - - ess-psgmii@98000 { - status = "okay"; - }; - - tcsr@1949000 { - compatible = "qcom,tcsr"; - reg = <0x1949000 0x100>; - qcom,wifi_glb_cfg = ; - }; - - tcsr@194b000 { - compatible = "qcom,tcsr"; - reg = <0x194b000 0x100>; - qcom,usb-hsphy-mode-select = ; - }; - - ess_tcsr@1953000 { - compatible = "qcom,tcsr"; - reg = <0x1953000 0x1000>; - qcom,ess-interface-select = ; - }; - - tcsr@1957000 { - compatible = "qcom,tcsr"; - reg = <0x1957000 0x100>; - qcom,wifi_noc_memtype_m0_m2 = ; - }; - - usb2@60f8800 { - status = "ok"; - }; - - serial@78af000 { - pinctrl-0 = <&serial_pins>; - pinctrl-names = "default"; - status = "ok"; - }; - - usb3@8af8800 { - status = "ok"; - }; - - crypto@8e3a000 { - status = "ok"; - }; - - wifi@a000000 { - status = "okay"; - }; - - wifi@a800000 { - status = "okay"; - }; - - watchdog@b017000 { - status = "ok"; - }; - qca8075: ess-switch@c000000 { status = "okay"; @@ -124,14 +68,10 @@ enable-usb-power { gpio-hog; line-name = "enable USB3 power"; - gpios = <7 GPIO_ACTIVE_HIGH>; + gpios = <0x7 GPIO_ACTIVE_HIGH>; output-high; }; }; - - edma@c080000 { - status = "okay"; - }; }; gpio-keys { @@ -139,7 +79,7 @@ wlan { label = "wlan"; - gpios = <&tlmm 58 GPIO_ACTIVE_LOW>; + gpios = <&tlmm 0x3a GPIO_ACTIVE_LOW>; linux,code = ; }; @@ -150,85 +90,52 @@ }; }; + aliases { + led-boot = &power; + led-failsafe = &flash; + led-running = &power; + led-upgrade = &flash; + }; + gpio-leds { compatible = "gpio-leds"; wlan { label = "fritz4040:green:wlan"; - gpios = <&qca8075 1 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0x1 GPIO_ACTIVE_HIGH>; }; panic: info_red { label = "fritz4040:red:info"; - gpios = <&qca8075 3 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0x3 GPIO_ACTIVE_HIGH>; panic-indicator; }; wan { label = "fritz4040:green:wan"; - gpios = <&qca8075 5 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0x5 GPIO_ACTIVE_HIGH>; }; power: power { label = "fritz4040:green:power"; - gpios = <&qca8075 11 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0xb GPIO_ACTIVE_HIGH>; }; lan { label = "fritz4040:green:lan"; - gpios = <&qca8075 13 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0xd GPIO_ACTIVE_HIGH>; }; flash: info_amber { label = "fritz4040:amber:info"; - gpios = <&qca8075 15 GPIO_ACTIVE_HIGH>; + gpios = <&qca8075 0xf GPIO_ACTIVE_HIGH>; }; }; }; -&tlmm { - serial_pins: serial_pinmux { - mux { - pins = "gpio60", "gpio61"; - function = "blsp_uart0"; - bias-disable; - }; - }; - - spi_0_pins: spi_0_pinmux { - mux { - function = "blsp_spi0"; - pins = "gpio55", "gpio56", "gpio57"; - drive-strength = <12>; - bias-disable; - }; - - mux_cs { - function = "gpio"; - pins = "gpio54"; - drive-strength = <2>; - bias-disable; - output-high; - }; - }; -}; - -&cryptobam { - status = "ok"; -}; - -&blsp_dma { - status = "ok"; -}; - &spi_0 { /* BLSP1 QUP1 */ - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - status = "ok"; - cs-gpios = <&tlmm 54 0>; - mx25l25635f@0 { - compatible = "jedec,spi-nor"; + compatible = "mx25l25635f", "jedec,spi-nor"; #address-cells = <1>; #size-cells = <0>; reg = <0>; @@ -299,24 +206,12 @@ }; partition11@2A0000 { label = "firmware"; - reg = <0x002a0000 0x01c60000>; + reg = <0x002A0000 0x01C60000>; }; partition12@1f00000 { label = "jffs2"; - reg = <0x01f00000 0x00100000>; + reg = <0x01F00000 0x00100000>; }; }; }; }; - -&usb3_ss_phy { - status = "ok"; -}; - -&usb3_hs_phy { - status = "ok"; -}; - -&usb2_hs_phy { - status = "ok"; -}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-nbg6617.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-nbg6617.dts new file mode 100644 index 000000000..a00dee7f2 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-nbg6617.dts @@ -0,0 +1,135 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "qcom-ipq4019-ap.dk01.1.dtsi" +#include "qcom-ipq4019-bus.dtsi" +#include +#include + +/ { + model = "ZyXEL NBG6617"; + compatible = "zyxel,nbg6617", "qcom,ipq4019"; + + memory { + device_type = "memory"; + reg = <0x80000000 0x10000000>; + }; + + aliases { + led-boot = &power; + led-failsafe = &power; + led-running = &power; + led-upgrade = &power; + }; + + reserved-memory { + #address-cells = <0x1>; + #size-cells = <0x1>; + ranges; + + rsvd1@87000000 { + reg = <0x87000000 0x0500000>; + no-map; + }; + + wifi_dump@87500000 { + reg = <0x87500000 0x600000>; + no-map; + }; + + rsvd2@87B00000 { + reg = <0x87b00000 0x500000>; + no-map; + }; + }; + + soc { + pinctrl@1000000 { + led_pinmux { + mux { + pins = "gpio0", "gpio1", "gpio3", "gpio5", "gpio58"; + drive-strength = <0x8>; + bias-pull-up; + output-high; + }; + }; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + wlan { + label = "wlan"; + gpios = <&tlmm 0x2 GPIO_ACTIVE_HIGH>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&tlmm 0x3f GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&tlmm 0x4 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + power: power { + label = "nbg6617:green:power"; + gpios = <&tlmm 0x3 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "nbg6617:green:wps"; + gpios = <&tlmm 0x1 GPIO_ACTIVE_HIGH>; + }; + + wlan2G { + label = "nbg6617:green:wlan2G"; + gpios = <&tlmm 0x3a GPIO_ACTIVE_HIGH>; + }; + + wlan5G { + label = "nbg6617:green:wlan5G"; + gpios = <&tlmm 0x5 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&spi_0 { /* BLSP1 QUP1 */ + n25q128a11@0 { + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition0@0 { + label = "all"; + reg = <0x00000000 0x08000000>; + read-only; /* for now */ + }; + }; + }; +}; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts similarity index 98% rename from target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts rename to target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts index 04f48ae9d..6efd0df19 100644 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq4019-rt-ac58u.dts @@ -122,7 +122,7 @@ * if we don't have it, it will spit out the following warning: * "ipq: fdt fixup unable to find compatible node". */ - compatible = "mx25l1606e", "n25q128a11", "jedec,spi-nor"; + compatible = "mx25l1606e", "n25q128a11"; reg = <0>; linux,modalias = "m25p80", "mx25l1606e", "n25q128a11"; spi-max-frequency = <24000000>; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ap148.dts new file mode 100644 index 000000000..39a0d9656 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ap148.dts @@ -0,0 +1,246 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +/ { + model = "Qualcomm IPQ8064/AP148"; + compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + i2c4_pins: i2c4_pinmux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + bias-disable; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + drive-strength = <10>; + bias-none; + }; + }; + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi4: spi@1a280000 { + status = "ok"; + spi-max-frequency = <50000000>; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 0>; + + flash: m25p80@0 { + compatible = "s25fl256s1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; + + linux,part-probe = "qcom-smem"; + }; + }; + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + }; + + usb30@1 { + status = "ok"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + force_gen1 = <1>; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + linux,part-probe = "qcom-smem"; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-c2600.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-c2600.dts new file mode 100644 index 000000000..a4fd13429 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-c2600.dts @@ -0,0 +1,500 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include + +/ { + model = "TP-Link Archer C2600"; + compatible = "tplink,c2600", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power; + led-failsafe = &general; + led-running = &power; + led-upgrade = &general; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio16", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio6", "gpio7", "gpio8", "gpio9", "gpio26", "gpio33", + "gpio53", "gpio66"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + bias-pull-down; + }; + + data { + pins = "gpio18", "gpio19"; + drive-strength = <10>; + }; + + cs { + pins = "gpio20"; + function = "gpio"; + drive-strength = <10>; + bias-pull-up; + }; + + clk { + pins = "gpio21"; + drive-strength = <12>; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + + usb0_pwr_en_pin: usb0_pwr_en_pin { + mux { + pins = "gpio25"; + function = "gpio"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + + usb1_pwr_en_pin: usb1_pwr_en_pin { + mux { + pins = "gpio23"; + function = "gpio"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi5: spi@1a280000 { + status = "ok"; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; + + flash: m25p80@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; + + SBL1@0 { + label = "SBL1"; + reg = <0x0 0x20000>; + read-only; + }; + + MIBIB@20000 { + label = "MIBIB"; + reg = <0x20000 0x20000>; + read-only; + }; + + SBL2@40000 { + label = "SBL2"; + reg = <0x40000 0x20000>; + read-only; + }; + + SBL3@60000 { + label = "SBL3"; + reg = <0x60000 0x30000>; + read-only; + }; + + DDRCONFIG@90000 { + label = "DDRCONFIG"; + reg = <0x90000 0x10000>; + read-only; + }; + + SSD@a0000 { + label = "SSD"; + reg = <0xa0000 0x10000>; + read-only; + }; + + TZ@b0000 { + label = "TZ"; + reg = <0xb0000 0x30000>; + read-only; + }; + + RPM@e0000 { + label = "RPM"; + reg = <0xe0000 0x20000>; + read-only; + }; + + fs-uboot@100000 { + label = "fs-uboot"; + reg = <0x100000 0x70000>; + read-only; + }; + + uboot-env@170000 { + label = "uboot-env"; + reg = <0x170000 0x40000>; + read-only; + }; + + radio@1b0000 { + label = "radio"; + reg = <0x1b0000 0x40000>; + read-only; + }; + + os-image@1f0000 { + label = "os-image"; + reg = <0x1f0000 0x200000>; + }; + + rootfs@3f0000 { + label = "rootfs"; + reg = <0x3f0000 0x1b00000>; + }; + + defaultmac: default-mac@1ef0000 { + label = "default-mac"; + reg = <0x1ef0000 0x00200>; + read-only; + }; + + pin@1ef0200 { + label = "pin"; + reg = <0x1ef0200 0x00200>; + read-only; + }; + + product-info@1ef0400 { + label = "product-info"; + reg = <0x1ef0400 0x0fc00>; + read-only; + }; + + partition-table@1f00000 { + label = "partition-table"; + reg = <0x1f00000 0x10000>; + read-only; + }; + + soft-version@1f10000 { + label = "soft-version"; + reg = <0x1f10000 0x10000>; + read-only; + }; + + support-list@1f20000 { + label = "support-list"; + reg = <0x1f20000 0x10000>; + read-only; + }; + + profile@1f30000 { + label = "profile"; + reg = <0x1f30000 0x10000>; + read-only; + }; + + default-config@1f40000 { + label = "default-config"; + reg = <0x1f40000 0x10000>; + read-only; + }; + + user-config@1f50000 { + label = "user-config"; + reg = <0x1f50000 0x40000>; + read-only; + }; + + qos-db@1f90000 { + label = "qos-db"; + reg = <0x1f90000 0x40000>; + read-only; + }; + + usb-config@1fd0000 { + label = "usb-config"; + reg = <0x1fd0000 0x10000>; + read-only; + }; + + log@1fe0000 { + label = "log"; + reg = <0x1fe0000 0x20000>; + read-only; + }; + }; + }; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + + pinctrl-0 = <&usb0_pwr_en_pin>; + pinctrl-names = "default"; + }; + + usb30@1 { + status = "ok"; + + pinctrl-0 = <&usb1_pwr_en_pin>; + pinctrl-names = "default"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + force_gen1 = <1>; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&defaultmac 0x8>; + mtd-mac-address-increment = <1>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&defaultmac 0x8>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 49 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + ledswitch { + label = "ledswitch"; + gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + lan { + label = "c2600:white:lan"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_HIGH>; + }; + + usb4 { + label = "c2600:white:usb_4"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb2 { + label = "c2600:white:usb_2"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "c2600:white:wps"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + wan_amber { + label = "c2600:amber:wan"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_LOW>; + }; + + wan_white { + label = "c2600:white:wan"; + gpios = <&qcom_pinmux 33 GPIO_ACTIVE_LOW>; + }; + + power: power { + label = "c2600:white:power"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + general: general { + label = "c2600:white:general"; + gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-d7800.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-d7800.dts new file mode 100644 index 000000000..b7c49cc81 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-d7800.dts @@ -0,0 +1,419 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include + +/ { + model = "Netgear Nighthawk X4 D7800"; + compatible = "netgear,d7800", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0xe000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power_white; + led-failsafe = &power_amber; + led-running = &power_white; + led-upgrade = &power_amber; + }; + + chosen { + bootargs = "rootfstype=squashfs noinitrd"; + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio6", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", + "gpio24","gpio26", "gpio53", "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + + usb0_pwr_en_pins: usb0_pwr_en_pins { + mux { + pins = "gpio15"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + + usb1_pwr_en_pins: usb1_pwr_en_pins { + mux { + pins = "gpio16", "gpio68"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + + pinctrl-0 = <&usb0_pwr_en_pins>; + pinctrl-names = "default"; + }; + + usb30@1 { + status = "ok"; + + pinctrl-0 = <&usb1_pwr_en_pins>; + pinctrl-names = "default"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; + force_gen1 = <1>; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <1>; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qcadata@0 { + label = "qcadata"; + reg = <0x0000000 0x0c80000>; + read-only; + }; + + APPSBL@c80000 { + label = "APPSBL"; + reg = <0x0c80000 0x0500000>; + read-only; + }; + + APPSBLENV@1180000 { + label = "APPSBLENV"; + reg = <0x1180000 0x0080000>; + read-only; + }; + + art: art@1200000 { + label = "art"; + reg = <0x1200000 0x0140000>; + read-only; + }; + + artbak: art@1340000 { + label = "artbak"; + reg = <0x1340000 0x0140000>; + read-only; + }; + + kernel@1480000 { + label = "kernel"; + reg = <0x1480000 0x0200000>; + }; + + ubi@1680000 { + label = "ubi"; + reg = <0x1680000 0x1E00000>; + }; + + netgear@3480000 { + label = "netgear"; + reg = <0x3480000 0x4480000>; + read-only; + }; + + reserve@7900000 { + label = "reserve"; + reg = <0x7900000 0x0700000>; + read-only; + }; + + firmware@1480000 { + label = "firmware"; + reg = <0x1480000 0x2000000>; + }; + }; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + phy-handle = <&phy4>; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&art 6>; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&art 0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + usb1 { + label = "d7800:white:usb1"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb2 { + label = "d7800:white:usb2"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + power_amber: power_amber { + label = "d7800:amber:power"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + wan_white { + label = "d7800:white:wan"; + gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; + }; + + wan_amber { + label = "d7800:amber:wan"; + gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "d7800:white:wps"; + gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; + }; + + esata { + label = "d7800:white:esata"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + }; + + power_white: power_white { + label = "d7800:white:power"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + wifi { + label = "d7800:white:wifi"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-db149.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-db149.dts new file mode 100644 index 000000000..4c5686607 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-db149.dts @@ -0,0 +1,236 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +/ { + model = "Qualcomm IPQ8064/DB149"; + compatible = "qcom,ipq8064-db149", "qcom,ipq8064"; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + alias { + serial0 = &uart2; + mdio-gpio0 = &mdio0; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + i2c4_pins: i2c4_pinmux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + bias-disable; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + drive-strength = <10>; + bias-none; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + rgmii0_pins: rgmii0_pins { + mux { + pins = "gpio2", "gpio66"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + + gsbi2: gsbi@12480000 { + qcom,mode = ; + status = "ok"; + uart2: serial@12490000 { + status = "ok"; + }; + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi4: spi@1a280000 { + status = "ok"; + spi-max-frequency = <50000000>; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 0>; + + flash: m25p80@0 { + compatible = "s25fl256s1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; + m25p,fast-read; + + partition@0 { + label = "lowlevel_init"; + reg = <0x0 0x1b0000>; + }; + + partition@1 { + label = "u-boot"; + reg = <0x1b0000 0x80000>; + }; + + partition@2 { + label = "u-boot-env"; + reg = <0x230000 0x40000>; + }; + + partition@3 { + label = "caldata"; + reg = <0x270000 0x40000>; + }; + + partition@4 { + label = "firmware"; + reg = <0x2b0000 0x1d50000>; + }; + }; + }; + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + }; + + usb30@1 { + status = "ok"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + }; + + pcie2: pci@1b900000 { + status = "ok"; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; + + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + + phy6: ethernet-phy@6 { + device_type = "ethernet-phy"; + reg = <6>; + }; + + phy7: ethernet-phy@7 { + device_type = "ethernet-phy"; + reg = <7>; + }; + }; + + gmac0: ethernet@37000000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <0>; + phy-handle = <&phy4>; + + pinctrl-0 = <&rgmii0_pins>; + pinctrl-names = "default"; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <1>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + phy-handle = <&phy6>; + }; + + gmac3: ethernet@37600000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <3>; + phy-handle = <&phy7>; + }; + }; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts new file mode 100644 index 000000000..a8628ff93 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts @@ -0,0 +1,406 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include + +/ { + model = "Linksys EA8500 WiFi Router"; + compatible = "linksys,ea8500", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power; + led-failsafe = &power; + led-running = &power; + led-upgrade = &power; + }; + + chosen { + bootargs = "console=ttyMSM0,115200n8"; + linux,stdout-path = "serial0:115200n8"; + append-rootblock = "ubi.mtd="; /* append to bootargs adding the root deviceblock nbr from bootloader */ + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio65", "gpio67", "gpio68"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio6", "gpio53", "gpio54"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + }; + + usb30@1 { + status = "ok"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + force_gen1 = <1>; + }; + + pcie1: pci@1b700000 { + status = "ok"; + }; + + pcie2: pci@1b900000 { + status = "ok"; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + SBL1@0 { + label = "SBL1"; + reg = <0x0000000 0x0040000>; + read-only; + }; + + MIBIB@40000 { + label = "MIBIB"; + reg = <0x0040000 0x0140000>; + read-only; + }; + + SBL2@180000 { + label = "SBL2"; + reg = <0x0180000 0x0140000>; + read-only; + }; + + SBL3@2c0000 { + label = "SBL3"; + reg = <0x02c0000 0x0280000>; + read-only; + }; + + DDRCONFIG@540000 { + label = "DDRCONFIG"; + reg = <0x0540000 0x0120000>; + read-only; + }; + + SSD@660000 { + label = "SSD"; + reg = <0x0660000 0x0120000>; + read-only; + }; + + TZ@780000 { + label = "TZ"; + reg = <0x0780000 0x0280000>; + read-only; + }; + + RPM@a00000 { + label = "RPM"; + reg = <0x0a00000 0x0280000>; + read-only; + }; + + art: art@c80000 { + label = "art"; + reg = <0x0c80000 0x0140000>; + read-only; + }; + + APPSBL@dc0000 { + label = "APPSBL"; + reg = <0x0dc0000 0x0100000>; + read-only; + }; + + u_env@ec0000 { + label = "u_env"; + reg = <0x0ec0000 0x0040000>; + }; + + s_env@f00000 { + label = "s_env"; + reg = <0x0f00000 0x0040000>; + }; + + devinfo@f40000 { + label = "devinfo"; + reg = <0x0f40000 0x0040000>; + }; + + linux@f80000 { + label = "kernel1"; + reg = <0x0f80000 0x2800000>; /* 3 MB spill to rootfs*/ + }; + + rootfs@1280000 { + label = "rootfs1"; + reg = <0x1280000 0x2500000>; + }; + + linux2@3780000 { + label = "kernel2"; + reg = <0x3780000 0x2800000>; + }; + + rootfs2@3a80000 { + label = "rootfs2"; + reg = <0x3a80000 0x2500000>; + }; + + syscfg@5f80000 { + label = "syscfg"; + reg = <0x5f80000 0x2080000>; + }; + }; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + qcom,phy_mdio_addr = <4>; + qcom,poll_required = <1>; + qcom,rgmii_delay = <0>; + qcom,emulation = <0>; + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + //lan + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + qcom,phy_mdio_addr = <0>; /* none */ + qcom,poll_required = <0>; /* no polling */ + qcom,rgmii_delay = <0>; + qcom,emulation = <0>; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + + adm_dma: dma@18300000 { + status = "ok"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + wps { + label = "ea8500:green:wps"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + }; + + power: power { + label = "ea8500:white:power"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; + default-state = "keep"; + }; + + wifi { + label = "ea8500:green:wifi"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_HIGH>; + }; + }; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500.dts new file mode 100644 index 000000000..3445a7925 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500.dts @@ -0,0 +1,394 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include +#include + +/ { + model = "Netgear Nighthawk X4 R7500"; + compatible = "netgear,r7500", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0xe000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power_white; + led-failsafe = &power_amber; + led-running = &power_white; + led-upgrade = &power_amber; + }; + + chosen { + bootargs = "rootfstype=squashfs noinitrd"; + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio6", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", + "gpio24","gpio26", "gpio53", "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + clocks = <&gcc USB30_0_UTMI_CLK>; + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + clocks = <&gcc USB30_0_MASTER_CLK>; + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + clocks = <&gcc USB30_1_UTMI_CLK>; + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + clocks = <&gcc USB30_1_MASTER_CLK>; + status = "ok"; + }; + + usb30@0 { + clocks = <&gcc USB30_1_MASTER_CLK>; + status = "ok"; + }; + + usb30@1 { + clocks = <&gcc USB30_0_MASTER_CLK>; + status = "ok"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + force_gen1 = <1>; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qcadata@0 { + label = "qcadata"; + reg = <0x0000000 0x0c80000>; + read-only; + }; + + APPSBL@c80000 { + label = "APPSBL"; + reg = <0x0c80000 0x0500000>; + read-only; + }; + + APPSBLENV@1180000 { + label = "APPSBLENV"; + reg = <0x1180000 0x0080000>; + read-only; + }; + + art: art@1200000 { + label = "art"; + reg = <0x1200000 0x0140000>; + read-only; + }; + + kernel@1340000 { + label = "kernel"; + reg = <0x1340000 0x0200000>; + }; + + ubi@1540000 { + label = "ubi"; + reg = <0x1540000 0x1800000>; + }; + + netgear@2d40000 { + label = "netgear"; + reg = <0x2d40000 0x0c00000>; + read-only; + }; + + reserve@3940000 { + label = "reserve"; + reg = <0x3940000 0x46c0000>; + read-only; + }; + + firmware@1340000 { + label = "firmware"; + reg = <0x1340000 0x1a00000>; + }; + }; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&art 6>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&art 0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + usb1 { + label = "r7500:white:usb1"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb2 { + label = "r7500:white:usb2"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + power_amber: power_amber { + label = "r7500:amber:power"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + wan_white { + label = "r7500:white:wan"; + gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; + }; + + wan_amber { + label = "r7500:amber:wan"; + gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "r7500:white:wps"; + gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; + }; + + esata { + label = "r7500:white:esata"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + }; + + power_white: power_white { + label = "r7500:white:power"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + wifi { + label = "r7500:white:wifi"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&tcsr { + qcom,usb-ctrl-select = ; + compatible = "qcom,tcsr"; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts new file mode 100644 index 000000000..c4b0c4b5a --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts @@ -0,0 +1,425 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include + +/ { + model = "Netgear Nighthawk X4 R7500v2"; + compatible = "netgear,r7500v2", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + + rsvd@5fe00000 { + reg = <0x5fe00000 0x200000>; + reusable; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power; + led-failsafe = &power; + led-running = &power; + led-upgrade = &power; + }; + + chosen { + bootargs = "rootfstype=squashfs noinitrd"; + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio6", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", + "gpio24","gpio26", "gpio53", "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + + usb0_pwr_en_pins: usb0_pwr_en_pins { + mux { + pins = "gpio15"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + + usb1_pwr_en_pins: usb1_pwr_en_pins { + mux { + pins = "gpio16", "gpio68"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + + pinctrl-0 = <&usb0_pwr_en_pins>; + pinctrl-names = "default"; + }; + + usb30@1 { + status = "ok"; + + pinctrl-0 = <&usb1_pwr_en_pins>; + pinctrl-names = "default"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; + force_gen1 = <1>; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qcadata@0 { + label = "qcadata"; + reg = <0x0000000 0x0c80000>; + read-only; + }; + + APPSBL@c80000 { + label = "APPSBL"; + reg = <0x0c80000 0x0500000>; + read-only; + }; + + APPSBLENV@1180000 { + label = "APPSBLENV"; + reg = <0x1180000 0x0080000>; + read-only; + }; + + art: art@1200000 { + label = "art"; + reg = <0x1200000 0x0140000>; + read-only; + }; + + artbak: art@1340000 { + label = "artbak"; + reg = <0x1340000 0x0140000>; + read-only; + }; + + kernel@1480000 { + label = "kernel"; + reg = <0x1480000 0x0200000>; + }; + + ubi@1680000 { + label = "ubi"; + reg = <0x1680000 0x1E00000>; + }; + + netgear@3480000 { + label = "netgear"; + reg = <0x3480000 0x4480000>; + read-only; + }; + + reserve@7900000 { + label = "reserve"; + reg = <0x7900000 0x0700000>; + read-only; + }; + + firmware@1480000 { + label = "firmware"; + reg = <0x1480000 0x2000000>; + }; + }; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0xaa545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&art 6>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&art 0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + usb1 { + label = "r7500v2:amber:usb1"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb3 { + label = "r7500v2:amber:usb3"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + status { + label = "r7500v2:amber:status"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + internet { + label = "r7500v2:white:internet"; + gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; + }; + + wan { + label = "r7500v2:white:wan"; + gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "r7500v2:white:wps"; + gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; + }; + + esata { + label = "r7500v2:white:esata"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + }; + + power: power { + label = "r7500v2:white:power"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + wifi { + label = "r7500v2:white:wifi"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts new file mode 100644 index 000000000..561c49aaa --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts @@ -0,0 +1,424 @@ +#include "qcom-ipq8064-v1.0.dtsi" + +#include + +/ { + model = "TP-Link Archer VR2600v"; + compatible = "tplink,vr2600v", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power; + led-failsafe = &general; + led-running = &power; + led-upgrade = &general; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + led_pins: led_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", "gpio16", "gpio17", + "gpio26", "gpio53", "gpio56", "gpio66"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + button_pins: button_pins { + mux { + pins = "gpio54", "gpio64", "gpio65", "gpio67", "gpio68"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + bias-pull-down; + }; + + data { + pins = "gpio18", "gpio19"; + drive-strength = <10>; + }; + + cs { + pins = "gpio20"; + drive-strength = <10>; + bias-pull-up; + }; + + clk { + pins = "gpio21"; + drive-strength = <12>; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi4: spi@1a280000 { + status = "ok"; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; + + flash: W25Q128@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; + + SBL1@0 { + label = "SBL1"; + reg = <0x0 0x20000>; + read-only; + }; + + MIBIB@20000 { + label = "MIBIB"; + reg = <0x20000 0x20000>; + read-only; + }; + + SBL2@40000 { + label = "SBL2"; + reg = <0x40000 0x40000>; + read-only; + }; + + SBL3@80000 { + label = "SBL3"; + reg = <0x80000 0x80000>; + read-only; + }; + + DDRCONFIG@100000 { + label = "DDRCONFIG"; + reg = <0x100000 0x10000>; + read-only; + }; + + SSD@110000 { + label = "SSD"; + reg = <0x110000 0x10000>; + read-only; + }; + + TZ@120000 { + label = "TZ"; + reg = <0x120000 0x80000>; + read-only; + }; + + RPM@1a0000 { + label = "RPM"; + reg = <0x1a0000 0x80000>; + read-only; + }; + + APPSBL@220000 { + label = "APPSBL"; + reg = <0x220000 0x80000>; + read-only; + }; + + APPSBLENV@2a0000 { + label = "APPSBLENV"; + reg = <0x2a0000 0x40000>; + read-only; + }; + + OLDART@2e0000 { + label = "OLDART"; + reg = <0x2e0000 0x40000>; + read-only; + }; + + kernel@320000 { + label = "kernel"; + reg = <0x320000 0x200000>; + }; + + rootfs@520000 { + label = "rootfs"; + reg = <0x520000 0xa60000>; + }; + + defaultmac: default-mac@0xfaf100 { + label = "default-mac"; + reg = <0xfaf100 0x00200>; + read-only; + }; + + ART@fc0000 { + label = "ART"; + reg = <0xfc0000 0x40000>; + read-only; + }; + }; + }; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + }; + + usb30@1 { + status = "ok"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + force_gen1 = <1>; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&defaultmac 0>; + mtd-mac-address-increment = <1>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&defaultmac 0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + dect { + label = "dect"; + gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + ledswitch { + label = "ledswitch"; + gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + dsl { + label = "vr2600v:white:dsl"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb { + label = "vr2600v:white:usb"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + lan { + label = "vr2600v:white:lan"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + wlan2g { + label = "vr2600v:white:wlan2g"; + gpios = <&qcom_pinmux 16 GPIO_ACTIVE_HIGH>; + }; + + wlan5g { + label = "vr2600v:white:wlan5g"; + gpios = <&qcom_pinmux 17 GPIO_ACTIVE_HIGH>; + }; + + power: power { + label = "vr2600v:white:power"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + phone { + label = "vr2600v:white:phone"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + }; + + wan { + label = "vr2600v:white:wan"; + gpios = <&qcom_pinmux 56 GPIO_ACTIVE_HIGH>; + }; + + general: general { + label = "vr2600v:white:general"; + gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi new file mode 100644 index 000000000..3d92fca6f --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi @@ -0,0 +1,1334 @@ +/dts-v1/; + +#include "skeleton.dtsi" +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + model = "Qualcomm IPQ8064"; + compatible = "qcom,ipq8064"; + interrupt-parent = <&intc>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; + clocks = <&kraitcc 0>, <&kraitcc 4>; + clock-names = "cpu", "l2"; + clock-latency = <100000>; + cpu-supply = <&smb208_s2a>; + voltage-tolerance = <5>; + cooling-min-state = <0>; + cooling-max-state = <10>; + #cooling-cells = <2>; + cpu-idle-states = <&CPU_SPC>; + }; + + cpu1: cpu@1 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; + clocks = <&kraitcc 1>, <&kraitcc 4>; + clock-names = "cpu", "l2"; + clock-latency = <100000>; + cpu-supply = <&smb208_s2b>; + voltage-tolerance = <5>; + cooling-min-state = <0>; + cooling-max-state = <10>; + #cooling-cells = <2>; + cpu-idle-states = <&CPU_SPC>; + }; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; + qcom,saw = <&saw_l2>; + l2-rates = <384000000 1000000000 1200000000>; + l2-cpufreq = <384000000 600000000 1200000000>; + l2-volt = <1100000 1100000 1150000>; + l2-supply = <&smb208_s1a>; + }; + + idle-states { + CPU_SPC: spc { + compatible = "qcom,idle-state-spc", + "arm,idle-state"; + entry-latency-us = <400>; + exit-latency-us = <900>; + min-residency-us = <3000>; + }; + }; + }; + + thermal-zones { + tsens_tz_sensor0 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 0>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor1 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 1>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor2 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 2>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor3 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 3>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor4 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 4>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor5 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 5>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor6 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 6>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor7 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 7>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor8 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 8>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor9 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 9>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + + tsens_tz_sensor10 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens 10>; + + trips { + cpu-critical-hi { + temperature = <125000>; + hysteresis = <2000>; + type = "critical_high"; + }; + + cpu-config-hi { + temperature = <105000>; + hysteresis = <2000>; + type = "configurable_hi"; + }; + + cpu-config-lo { + temperature = <95000>; + hysteresis = <2000>; + type = "configurable_lo"; + }; + + cpu-critical-low { + temperature = <0>; + hysteresis = <2000>; + type = "critical_low"; + }; + }; + }; + }; + + cpu-pmu { + compatible = "qcom,krait-pmu"; + interrupts = <1 10 0x304>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + nss@40000000 { + reg = <0x40000000 0x1000000>; + no-map; + }; + + smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; + }; + + clocks { + cxo_board { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + pxo_board { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + sleep_clk: sleep_clk { + compatible = "fixed-clock"; + clock-frequency = <32768>; + #clock-cells = <0>; + }; + }; + + firmware { + scm { + compatible = "qcom,scm-ipq806x"; + }; + }; + + kraitcc: clock-controller { + compatible = "qcom,krait-cc-v1"; + #clock-cells = <1>; + }; + + qcom,pvs { + qcom,pvs-format-a; + qcom,speed0-pvs0-bin-v0 = + < 1400000000 1250000 >, + < 1200000000 1200000 >, + < 1000000000 1150000 >, + < 800000000 1100000 >, + < 600000000 1050000 >, + < 384000000 1000000 >; + + qcom,speed0-pvs1-bin-v0 = + < 1400000000 1175000 >, + < 1200000000 1125000 >, + < 1000000000 1075000 >, + < 800000000 1025000 >, + < 600000000 975000 >, + < 384000000 925000 >; + + qcom,speed0-pvs2-bin-v0 = + < 1400000000 1125000 >, + < 1200000000 1075000 >, + < 1000000000 1025000 >, + < 800000000 995000 >, + < 600000000 925000 >, + < 384000000 875000 >; + + qcom,speed0-pvs3-bin-v0 = + < 1400000000 1050000 >, + < 1200000000 1000000 >, + < 1000000000 950000 >, + < 800000000 900000 >, + < 600000000 850000 >, + < 384000000 800000 >; + }; + + soc: soc { + #address-cells = <1>; + #size-cells = <1>; + ranges; + compatible = "simple-bus"; + + lpass@28100000 { + compatible = "qcom,lpass-cpu"; + status = "disabled"; + clocks = <&lcc AHBIX_CLK>, + <&lcc MI2S_OSR_CLK>, + <&lcc MI2S_BIT_CLK>; + clock-names = "ahbix-clk", + "mi2s-osr-clk", + "mi2s-bit-clk"; + interrupts = <0 85 1>; + interrupt-names = "lpass-irq-lpaif"; + reg = <0x28100000 0x10000>; + reg-names = "lpass-lpaif"; + }; + + qfprom: qfprom@700000 { + compatible = "qcom,qfprom", "syscon"; + reg = <0x700000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + status = "okay"; + tsens_calib: calib@400 { + reg = <0x400 0x10>; + }; + tsens_backup: backup@410 { + reg = <0x410 0x10>; + }; + }; + + rpm@108000 { + compatible = "qcom,rpm-ipq8064"; + reg = <0x108000 0x1000>; + qcom,ipc = <&l2cc 0x8 2>; + + interrupts = <0 19 0>, + <0 21 0>, + <0 22 0>; + interrupt-names = "ack", + "err", + "wakeup"; + + clocks = <&gcc RPM_MSG_RAM_H_CLK>; + clock-names = "ram"; + + #address-cells = <1>; + #size-cells = <0>; + + rpmcc: clock-controller { + compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc"; + #clock-cells = <1>; + }; + + regulators { + compatible = "qcom,rpm-smb208-regulators"; + + smb208_s1a: s1a { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1150000>; + + qcom,switch-mode-frequency = <1200000>; + + }; + + smb208_s1b: s1b { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1150000>; + + qcom,switch-mode-frequency = <1200000>; + }; + + smb208_s2a: s2a { + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <1250000>; + + qcom,switch-mode-frequency = <1200000>; + }; + + smb208_s2b: s2b { + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <1250000>; + + qcom,switch-mode-frequency = <1200000>; + }; + }; + }; + + rng@1a500000 { + compatible = "qcom,prng"; + reg = <0x1a500000 0x200>; + clocks = <&gcc PRNG_CLK>; + clock-names = "core"; + }; + + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; + + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 16 0x4>; + + pcie0_pins: pcie0_pinmux { + mux { + pins = "gpio3"; + function = "pcie1_rst"; + drive-strength = <2>; + bias-disable; + }; + }; + + pcie1_pins: pcie1_pinmux { + mux { + pins = "gpio48"; + function = "pcie2_rst"; + drive-strength = <2>; + bias-disable; + }; + }; + + pcie2_pins: pcie2_pinmux { + mux { + pins = "gpio63"; + function = "pcie3_rst"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + }; + + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0x02000000 0x1000>, + <0x02002000 0x1000>; + }; + + timer@200a000 { + compatible = "qcom,kpss-timer", "qcom,msm-timer"; + interrupts = <1 1 0x301>, + <1 2 0x301>, + <1 3 0x301>, + <1 4 0x301>, + <1 5 0x301>; + reg = <0x0200a000 0x100>; + clock-frequency = <25000000>, + <32768>; + clocks = <&sleep_clk>; + clock-names = "sleep"; + cpu-offset = <0x80000>; + }; + + acc0: clock-controller@2088000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02088000 0x1000>, <0x02008000 0x1000>; + clock-output-names = "acpu0_aux"; + }; + + acc1: clock-controller@2098000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; + clock-output-names = "acpu1_aux"; + }; + + l2cc: clock-controller@2011000 { + compatible = "qcom,kpss-gcc", "syscon"; + reg = <0x2011000 0x1000>; + clock-output-names = "acpu_l2_aux"; + }; + + saw0: regulator@2089000 { + compatible = "qcom,saw2", "syscon"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; + regulator; + }; + + saw1: regulator@2099000 { + compatible = "qcom,saw2", "syscon"; + reg = <0x02099000 0x1000>, <0x02009000 0x1000>; + regulator; + }; + + saw_l2: regulator@02012000 { + compatible = "qcom,saw2", "syscon"; + reg = <0x02012000 0x1000>; + regulator; + }; + + sic_non_secure: sic-non-secure@12100000 { + compatible = "syscon"; + reg = <0x12100000 0x10000>; + }; + + gsbi2: gsbi@12480000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <2>; + reg = <0x12480000 0x100>; + clocks = <&gcc GSBI2_H_CLK>; + clock-names = "iface"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + status = "disabled"; + + syscon-tcsr = <&tcsr>; + + uart2: serial@12490000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12490000 0x1000>, + <0x12480000 0x1000>; + interrupts = <0 195 0x0>; + clocks = <&gcc GSBI2_UART_CLK>, <&gcc GSBI2_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + }; + + i2c@124a0000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x124a0000 0x1000>; + interrupts = <0 196 0>; + + clocks = <&gcc GSBI2_QUP_CLK>, <&gcc GSBI2_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + }; + + }; + + gsbi4: gsbi@16300000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <4>; + reg = <0x16300000 0x100>; + clocks = <&gcc GSBI4_H_CLK>; + clock-names = "iface"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + status = "disabled"; + + syscon-tcsr = <&tcsr>; + + gsbi4_serial: serial@16340000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16340000 0x1000>, + <0x16300000 0x1000>; + interrupts = <0 152 0x0>; + clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + }; + + i2c@16380000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x16380000 0x1000>; + interrupts = <0 153 0>; + + clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + gsbi5: gsbi@1a200000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <5>; + reg = <0x1a200000 0x100>; + clocks = <&gcc GSBI5_H_CLK>; + clock-names = "iface"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + status = "disabled"; + + syscon-tcsr = <&tcsr>; + + uart5: serial@1a240000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x1a240000 0x1000>, + <0x1a200000 0x1000>; + interrupts = <0 154 0x0>; + clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + }; + + i2c@1a280000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x1a280000 0x1000>; + interrupts = <0 155 0>; + + clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + }; + + spi@1a280000 { + compatible = "qcom,spi-qup-v1.1.1"; + reg = <0x1a280000 0x1000>; + interrupts = <0 155 0>; + + clocks = <&gcc GSBI5_QUP_CLK>, <&gcc GSBI5_H_CLK>; + clock-names = "core", "iface"; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + sata_phy: sata-phy@1b400000 { + compatible = "qcom,ipq806x-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + status = "disabled"; + }; + + sata@29000000 { + compatible = "qcom,ipq806x-ahci", "generic-ahci"; + reg = <0x29000000 0x180>; + + interrupts = <0 209 0x0>; + + clocks = <&gcc SFAB_SATA_S_H_CLK>, + <&gcc SATA_H_CLK>, + <&gcc SATA_A_CLK>, + <&gcc SATA_RXOOB_CLK>, + <&gcc SATA_PMALIVE_CLK>; + clock-names = "slave_face", "iface", "core", + "rxoob", "pmalive"; + + assigned-clocks = <&gcc SATA_RXOOB_CLK>, <&gcc SATA_PMALIVE_CLK>; + assigned-clock-rates = <100000000>, <100000000>; + + phys = <&sata_phy>; + phy-names = "sata-phy"; + status = "disabled"; + }; + + qcom,ssbi@500000 { + compatible = "qcom,ssbi"; + reg = <0x00500000 0x1000>; + qcom,controller-type = "pmic-arbiter"; + }; + + gcc: clock-controller@900000 { + compatible = "qcom,gcc-ipq8064"; + reg = <0x00900000 0x4000>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + tsens: thermal-sensor@900000 { + compatible = "qcom,ipq8064-tsens"; + reg = <0x900000 0x3680>; + nvmem-cells = <&tsens_calib>, <&tsens_backup>; + nvmem-cell-names = "calib", "calib_backup"; + interrupts = <0 178 0>; + #thermal-sensor-cells = <1>; + }; + + tcsr: syscon@1a400000 { + compatible = "qcom,tcsr-ipq8064", "syscon"; + reg = <0x1a400000 0x100>; + }; + + lcc: clock-controller@28000000 { + compatible = "qcom,lcc-ipq8064"; + reg = <0x28000000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + sfpb_mutex_block: syscon@1200600 { + compatible = "syscon"; + reg = <0x01200600 0x100>; + }; + + hs_phy_1: phy@100f8800 { + compatible = "qcom,dwc3-hs-usb-phy"; + reg = <0x100f8800 0x30>; + clocks = <&gcc USB30_1_UTMI_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + + status = "disabled"; + }; + + ss_phy_1: phy@100f8830 { + compatible = "qcom,dwc3-ss-usb-phy"; + reg = <0x100f8830 0x30>; + clocks = <&gcc USB30_1_MASTER_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + + status = "disabled"; + }; + + hs_phy_0: phy@110f8800 { + compatible = "qcom,dwc3-hs-usb-phy"; + reg = <0x110f8800 0x30>; + clocks = <&gcc USB30_0_UTMI_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + + status = "disabled"; + }; + + ss_phy_0: phy@110f8830 { + compatible = "qcom,dwc3-ss-usb-phy"; + reg = <0x110f8830 0x30>; + clocks = <&gcc USB30_0_MASTER_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + + status = "disabled"; + }; + + usb3_0: usb30@0 { + compatible = "qcom,dwc3"; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&gcc USB30_0_MASTER_CLK>; + clock-names = "core"; + + ranges; + + resets = <&gcc USB30_0_MASTER_RESET>; + reset-names = "usb30_0_mstr_rst"; + + status = "disabled"; + + dwc3@11000000 { + compatible = "snps,dwc3"; + reg = <0x11000000 0xcd00>; + interrupts = <0 110 0x4>; + phys = <&hs_phy_0>, <&ss_phy_0>; + phy-names = "usb2-phy", "usb3-phy"; + dr_mode = "host"; + snps,dis_u3_susphy_quirk; + }; + }; + + usb3_1: usb30@1 { + compatible = "qcom,dwc3"; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&gcc USB30_1_MASTER_CLK>; + clock-names = "core"; + + ranges; + + resets = <&gcc USB30_1_MASTER_RESET>; + reset-names = "usb30_1_mstr_rst"; + + status = "disabled"; + + dwc3@10000000 { + compatible = "snps,dwc3"; + reg = <0x10000000 0xcd00>; + interrupts = <0 205 0x4>; + phys = <&hs_phy_1>, <&ss_phy_1>; + phy-names = "usb2-phy", "usb3-phy"; + dr_mode = "host"; + snps,dis_u3_susphy_quirk; + }; + }; + + pcie0: pci@1b500000 { + compatible = "qcom,pcie-ipq8064"; + reg = <0x1b500000 0x1000 + 0x1b502000 0x80 + 0x1b600000 0x100 + 0x0ff00000 0x100000>; + reg-names = "dbi", "elbi", "parf", "config"; + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ + + interrupts = ; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc PCIE_A_CLK>, + <&gcc PCIE_H_CLK>, + <&gcc PCIE_PHY_CLK>, + <&gcc PCIE_AUX_CLK>, + <&gcc PCIE_ALT_REF_CLK>; + clock-names = "core", "iface", "phy", "aux", "ref"; + + assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; + assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_ACLK_RESET>, + <&gcc PCIE_HCLK_RESET>, + <&gcc PCIE_POR_RESET>, + <&gcc PCIE_PCI_RESET>, + <&gcc PCIE_PHY_RESET>, + <&gcc PCIE_EXT_RESET>; + reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; + + perst-gpios = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; + + phy-tx0-term-offset = <7>; + + status = "disabled"; + }; + + pcie1: pci@1b700000 { + compatible = "qcom,pcie-ipq8064"; + reg = <0x1b700000 0x1000 + 0x1b702000 0x80 + 0x1b800000 0x100 + 0x31f00000 0x100000>; + reg-names = "dbi", "elbi", "parf", "config"; + device_type = "pci"; + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ + + interrupts = ; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc PCIE_1_A_CLK>, + <&gcc PCIE_1_H_CLK>, + <&gcc PCIE_1_PHY_CLK>, + <&gcc PCIE_1_AUX_CLK>, + <&gcc PCIE_1_ALT_REF_CLK>; + clock-names = "core", "iface", "phy", "aux", "ref"; + + assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; + assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_1_ACLK_RESET>, + <&gcc PCIE_1_HCLK_RESET>, + <&gcc PCIE_1_POR_RESET>, + <&gcc PCIE_1_PCI_RESET>, + <&gcc PCIE_1_PHY_RESET>, + <&gcc PCIE_1_EXT_RESET>; + reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; + + perst-gpios = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; + + phy-tx0-term-offset = <7>; + + status = "disabled"; + }; + + pcie2: pci@1b900000 { + compatible = "qcom,pcie-ipq8064"; + reg = <0x1b900000 0x1000 + 0x1b902000 0x80 + 0x1ba00000 0x100 + 0x35f00000 0x100000>; + reg-names = "dbi", "elbi", "parf", "config"; + device_type = "pci"; + linux,pci-domain = <2>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ + 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ + + interrupts = ; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc PCIE_2_A_CLK>, + <&gcc PCIE_2_H_CLK>, + <&gcc PCIE_2_PHY_CLK>, + <&gcc PCIE_2_AUX_CLK>, + <&gcc PCIE_2_ALT_REF_CLK>; + clock-names = "core", "iface", "phy", "aux", "ref"; + + assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; + assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_2_ACLK_RESET>, + <&gcc PCIE_2_HCLK_RESET>, + <&gcc PCIE_2_POR_RESET>, + <&gcc PCIE_2_PCI_RESET>, + <&gcc PCIE_2_PHY_RESET>, + <&gcc PCIE_2_EXT_RESET>; + reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie2_pins>; + pinctrl-names = "default"; + + perst-gpios = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; + + phy-tx0-term-offset = <7>; + + status = "disabled"; + }; + + adm_dma: dma@18300000 { + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; + #dma-cells = <1>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; + clock-names = "core", "iface"; + + resets = <&gcc ADM0_RESET>, + <&gcc ADM0_PBUS_RESET>, + <&gcc ADM0_C0_RESET>, + <&gcc ADM0_C1_RESET>, + <&gcc ADM0_C2_RESET>; + reset-names = "clk", "pbus", "c0", "c1", "c2"; + qcom,ee = <0>; + + status = "disabled"; + }; + + nand@1ac00000 { + compatible = "qcom,ipq806x-nand"; + reg = <0x1ac00000 0x800>; + + clocks = <&gcc EBI2_CLK>, + <&gcc EBI2_AON_CLK>; + clock-names = "core", "aon"; + + dmas = <&adm_dma 3>; + dma-names = "rxtx"; + qcom,cmd-crci = <15>; + qcom,data-crci = <3>; + + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + }; + + nss_common: syscon@03000000 { + compatible = "syscon"; + reg = <0x03000000 0x0000FFFF>; + }; + + qsgmii_csr: syscon@1bb00000 { + compatible = "syscon"; + reg = <0x1bb00000 0x000001FF>; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <7>; + snps,rd_osr_lmt = <7>; + snps,blen = <16 0 0 0 0 0 0>; + }; + + gmac0: ethernet@37000000 { + device_type = "network"; + compatible = "qcom,ipq806x-gmac"; + reg = <0x37000000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; + + snps,axi-config = <&stmmac_axi_setup>; + snps,pbl = <32>; + snps,aal = <1>; + + qcom,nss-common = <&nss_common>; + qcom,qsgmii-csr = <&qsgmii_csr>; + + clocks = <&gcc GMAC_CORE1_CLK>; + clock-names = "stmmaceth"; + + resets = <&gcc GMAC_CORE1_RESET>; + reset-names = "stmmaceth"; + + status = "disabled"; + }; + + gmac1: ethernet@37200000 { + device_type = "network"; + compatible = "qcom,ipq806x-gmac"; + reg = <0x37200000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; + + snps,axi-config = <&stmmac_axi_setup>; + snps,pbl = <32>; + snps,aal = <1>; + + qcom,nss-common = <&nss_common>; + qcom,qsgmii-csr = <&qsgmii_csr>; + + clocks = <&gcc GMAC_CORE2_CLK>; + clock-names = "stmmaceth"; + + resets = <&gcc GMAC_CORE2_RESET>; + reset-names = "stmmaceth"; + + status = "disabled"; + }; + + gmac2: ethernet@37400000 { + device_type = "network"; + compatible = "qcom,ipq806x-gmac"; + reg = <0x37400000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; + + snps,axi-config = <&stmmac_axi_setup>; + snps,pbl = <32>; + snps,aal = <1>; + + qcom,nss-common = <&nss_common>; + qcom,qsgmii-csr = <&qsgmii_csr>; + + clocks = <&gcc GMAC_CORE3_CLK>; + clock-names = "stmmaceth"; + + resets = <&gcc GMAC_CORE3_RESET>; + reset-names = "stmmaceth"; + + status = "disabled"; + }; + + gmac3: ethernet@37600000 { + device_type = "network"; + compatible = "qcom,ipq806x-gmac"; + reg = <0x37600000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; + + snps,axi-config = <&stmmac_axi_setup>; + snps,pbl = <32>; + snps,aal = <1>; + + qcom,nss-common = <&nss_common>; + qcom,qsgmii-csr = <&qsgmii_csr>; + + clocks = <&gcc GMAC_CORE4_CLK>; + clock-names = "stmmaceth"; + + resets = <&gcc GMAC_CORE4_RESET>; + reset-names = "stmmaceth"; + + status = "disabled"; + }; + }; + + sfpb_mutex: sfpb-mutex { + compatible = "qcom,sfpb-mutex"; + syscon = <&sfpb_mutex_block 4 4>; + + #hwlock-cells = <1>; + }; + + smem { + compatible = "qcom,smem"; + memory-region = <&smem>; + hwlocks = <&sfpb_mutex 3>; + }; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts new file mode 100644 index 000000000..987ee852c --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts @@ -0,0 +1,391 @@ +#include "qcom-ipq8065-v1.0.dtsi" + +#include + +/ { + model = "ZyXEL NBG6817"; + compatible = "zyxel,nbg6817", "qcom,ipq8065"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + sdcc1 = &sdcc1; + + led-boot = &power; + led-failsafe = &power; + led-running = &power; + led-upgrade = &power; + }; + + chosen { + bootargs = "rootfstype=squashfs,ext4 rootwait noinitrd"; + linux,stdout-path = "serial0:115200n8"; + append-rootblock = "root=/dev/mmcblk0p"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio53", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio9", "gpio26", "gpio33", "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + clk { + pins = "gpio1"; + input-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + + tx { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ; + input-disable; + }; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + drive-strength = <10>; + bias-none; + }; + + cs { + pins = "gpio20"; + drive-strength = <12>; + }; + }; + + usb0_pwr_en_pins: usb0_pwr_en_pins { + mux { + pins = "gpio16", "gpio17"; + function = "gpio"; + drive-strength = <12>; + }; + + pwr { + pins = "gpio17"; + bias-pull-down; + output-high; + }; + + ovc { + pins = "gpio16"; + bias-pull-up; + }; + }; + + usb1_pwr_en_pins: usb1_pwr_en_pins { + mux { + pins = "gpio14", "gpio15"; + function = "gpio"; + drive-strength = <12>; + }; + + pwr { + pins = "gpio14"; + bias-pull-down; + output-high; + }; + + ovc { + pins = "gpio15"; + bias-pull-up; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi4: spi@1a280000 { + status = "ok"; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; + + flash: m25p80@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <51200000>; + reg = <0>; + + linux,part-probe = "qcom-smem"; + }; + }; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + + pinctrl-0 = <&usb0_pwr_en_pins>; + pinctrl-names = "default"; + }; + + usb30@1 { + status = "ok"; + + pinctrl-0 = <&usb1_pwr_en_pins>; + pinctrl-names = "default"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; + force_gen1 = <1>; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0xaa545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + 0x00970 0x1e864443 /* QM_PORT0_CTRL0 */ + 0x00974 0x000001c6 /* QM_PORT0_CTRL1 */ + 0x00978 0x19008643 /* QM_PORT1_CTRL0 */ + 0x0097c 0x000001c6 /* QM_PORT1_CTRL1 */ + 0x00980 0x19008643 /* QM_PORT2_CTRL0 */ + 0x00984 0x000001c6 /* QM_PORT2_CTRL1 */ + 0x00988 0x19008643 /* QM_PORT3_CTRL0 */ + 0x0098c 0x000001c6 /* QM_PORT3_CTRL1 */ + 0x00990 0x19008643 /* QM_PORT4_CTRL0 */ + 0x00994 0x000001c6 /* QM_PORT4_CTRL1 */ + 0x00998 0x1e864443 /* QM_PORT5_CTRL0 */ + 0x0099c 0x000001c6 /* QM_PORT5_CTRL1 */ + 0x009a0 0x1e864443 /* QM_PORT6_CTRL0 */ + 0x009a4 0x000001c6 /* QM_PORT6_CTRL1 */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + qca,ar8327-initvals = < + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x0000c 0x80 /* PAD6_MODE */ + >; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + qcom,phy_mdio_addr = <4>; + qcom,poll_required = <0>; + qcom,rgmii_delay = <1>; + qcom,phy_mii_type = <0>; + qcom,emulation = <0>; + qcom,irq = <255>; + mdiobus = <&mdio0>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + qcom,phy_mdio_addr = <0>; /* none */ + qcom,poll_required = <0>; /* no polling */ + qcom,rgmii_delay = <0>; + qcom,phy_mii_type = <1>; + qcom,emulation = <0>; + qcom,irq = <258>; + mdiobus = <&mdio0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + rpm@108000 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "default"; + }; + + amba { + sdcc1: sdcc@12400000 { + status = "okay"; + }; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_LOW>; + linux,code = ; + linux,input-type = ; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + internet { + label = "nbg6817:white:internet"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; + }; + + power: power { + label = "nbg6817:white:power"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + wifi2g { + label = "nbg6817:amber:wifi2g"; + gpios = <&qcom_pinmux 33 GPIO_ACTIVE_HIGH>; + }; + + /* wifi2g amber from the manual is missing */ + + wifi5g { + label = "nbg6817:amber:wifi5g"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + }; + + /* wifi5g amber from the manual is missing */ + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-r7800.dts b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-r7800.dts new file mode 100644 index 000000000..0b4f49d69 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-r7800.dts @@ -0,0 +1,575 @@ +#include "qcom-ipq8065-v1.0.dtsi" + +#include + +/ { + model = "Netgear Nighthawk X4S R7800"; + compatible = "netgear,r7800", "qcom,ipq8065", "qcom,ipq8064"; + + memory@0 { + reg = <0x42000000 0x1e000000>; + device_type = "memory"; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + rsvd@41200000 { + reg = <0x41200000 0x300000>; + no-map; + }; + + rsvd@5fe00000 { + reg = <0x5fe00000 0x200000>; + reusable; + }; + }; + + aliases { + serial0 = &gsbi4_serial; + mdio-gpio0 = &mdio0; + + led-boot = &power_white; + led-failsafe = &power_amber; + led-running = &power_white; + led-upgrade = &power_amber; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + pinmux@800000 { + button_pins: button_pins { + mux { + pins = "gpio6", "gpio54", "gpio65"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + i2c4_pins: i2c4_pinmux { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <12>; + bias-disable; + }; + }; + + led_pins: led_pins { + pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", + "gpio24","gpio26", "gpio53", "gpio64"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", + "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + pullups { + pins = "gpio39"; + bias-pull-up; + }; + hold { + pins = "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + clk { + pins = "gpio1"; + input-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", + "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + + tx { + pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ; + input-disable; + }; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; + function = "gsbi5"; + bias-pull-down; + }; + + data { + pins = "gpio18", "gpio19"; + drive-strength = <10>; + }; + + cs { + pins = "gpio20"; + drive-strength = <10>; + bias-pull-up; + }; + + clk { + pins = "gpio21"; + drive-strength = <12>; + }; + }; + + spi6_pins: spi6_pins { + mux { + pins = "gpio55", "gpio56", "gpio58"; + function = "gsbi6"; + bias-pull-down; + }; + + mosi { + pins = "gpio55"; + drive-strength = <12>; + }; + + miso { + pins = "gpio56"; + drive-strength = <14>; + }; + + cs { + pins = "gpio57"; + drive-strength = <12>; + bias-pull-up; + }; + + clk { + pins = "gpio58"; + drive-strength = <12>; + }; + + reset { + pins = "gpio33"; + drive-strength = <10>; + bias-pull-down; + output-high; + }; + }; + + usb0_pwr_en_pins: usb0_pwr_en_pins { + mux { + pins = "gpio15"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + + usb1_pwr_en_pins: usb1_pwr_en_pins { + mux { + pins = "gpio16", "gpio68"; + function = "gpio"; + drive-strength = <12>; + bias-pull-down; + output-high; + }; + }; + }; + + gsbi@16300000 { + qcom,mode = ; + status = "ok"; + serial@16340000 { + status = "ok"; + }; + /* + * The i2c device on gsbi4 should not be enabled. + * On ipq806x designs gsbi4 i2c is meant for exclusive + * RPM usage. Turning this on in kernel manifests as + * i2c failure for the RPM. + */ + }; + + gsbi5: gsbi@1a200000 { + qcom,mode = ; + status = "ok"; + + spi5: spi@1a280000 { + status = "ok"; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; + + flash: m25p80@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; + + linux,part-probe = "qcom-smem"; + }; + }; + }; + + gsbi6: gsbi@16500000 { + qcom,mode = ; + status = "ok"; + spi6: spi@16580000 { + status = "ok"; + + pinctrl-0 = <&spi6_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 57 GPIO_ACTIVE_HIGH>; + + spi-nor@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <6000000>; + }; + }; + }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + ports-implemented = <0x1>; + status = "ok"; + }; + + phy@100f8800 { /* USB3 port 1 HS phy */ + status = "ok"; + }; + + phy@100f8830 { /* USB3 port 1 SS phy */ + status = "ok"; + }; + + phy@110f8800 { /* USB3 port 0 HS phy */ + status = "ok"; + }; + + phy@110f8830 { /* USB3 port 0 SS phy */ + status = "ok"; + }; + + usb30@0 { + status = "ok"; + + pinctrl-0 = <&usb0_pwr_en_pins>; + pinctrl-names = "default"; + }; + + usb30@1 { + status = "ok"; + + pinctrl-0 = <&usb1_pwr_en_pins>; + pinctrl-names = "default"; + }; + + pcie0: pci@1b500000 { + status = "ok"; + }; + + pcie1: pci@1b700000 { + status = "ok"; + force_gen1 = <1>; + }; + + nand@1ac00000 { + status = "ok"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + cs0 { + reg = <0>; + compatible = "qcom,nandcs"; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qcadata@0 { + label = "qcadata"; + reg = <0x0000000 0x0c80000>; + read-only; + }; + + APPSBL@c80000 { + label = "APPSBL"; + reg = <0x0c80000 0x0500000>; + read-only; + }; + + APPSBLENV@1180000 { + label = "APPSBLENV"; + reg = <0x1180000 0x0080000>; + read-only; + }; + + art: art@1200000 { + label = "art"; + reg = <0x1200000 0x0140000>; + read-only; + }; + + artbak: art@1340000 { + label = "artbak"; + reg = <0x1340000 0x0140000>; + read-only; + }; + + kernel@1480000 { + label = "kernel"; + reg = <0x1480000 0x0400000>; + }; + + ubi@1880000 { + label = "ubi"; + reg = <0x1880000 0x1C00000>; + }; + + netgear@3480000 { + label = "netgear"; + reg = <0x3480000 0x4480000>; + read-only; + }; + + reserve@7900000 { + label = "reserve"; + reg = <0x7900000 0x0700000>; + read-only; + }; + + firmware@1480000 { + label = "firmware"; + reg = <0x1480000 0x2000000>; + }; + }; + }; + }; + + mdio0: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0xaa545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + 0x00970 0x1e864443 /* QM_PORT0_CTRL0 */ + 0x00974 0x000001c6 /* QM_PORT0_CTRL1 */ + 0x00978 0x19008643 /* QM_PORT1_CTRL0 */ + 0x0097c 0x000001c6 /* QM_PORT1_CTRL1 */ + 0x00980 0x19008643 /* QM_PORT2_CTRL0 */ + 0x00984 0x000001c6 /* QM_PORT2_CTRL1 */ + 0x00988 0x19008643 /* QM_PORT3_CTRL0 */ + 0x0098c 0x000001c6 /* QM_PORT3_CTRL1 */ + 0x00990 0x19008643 /* QM_PORT4_CTRL0 */ + 0x00994 0x000001c6 /* QM_PORT4_CTRL1 */ + 0x00998 0x1e864443 /* QM_PORT5_CTRL0 */ + 0x0099c 0x000001c6 /* QM_PORT5_CTRL1 */ + 0x009a0 0x1e864443 /* QM_PORT6_CTRL0 */ + 0x009a4 0x000001c6 /* QM_PORT6_CTRL1 */ + >; + qca,ar8327-vlans = < + 0x1 0x5e /* VLAN1 Ports 1/2/3/4/6 */ + 0x2 0x21 /* VLAN2 Ports 0/5 */ + >; + }; + + phy4: ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + qca,ar8327-initvals = < + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x0000c 0x80 /* PAD6_MODE */ + >; + }; + }; + + gmac1: ethernet@37200000 { + status = "ok"; + phy-mode = "rgmii"; + qcom,id = <1>; + qcom,phy_mdio_addr = <4>; + qcom,poll_required = <0>; + qcom,rgmii_delay = <1>; + qcom,phy_mii_type = <0>; + qcom,emulation = <0>; + qcom,irq = <255>; + mdiobus = <&mdio0>; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + mtd-mac-address = <&art 6>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + gmac2: ethernet@37400000 { + status = "ok"; + phy-mode = "sgmii"; + qcom,id = <2>; + qcom,phy_mdio_addr = <0>; /* none */ + qcom,poll_required = <0>; /* no polling */ + qcom,rgmii_delay = <0>; + qcom,phy_mii_type = <1>; + qcom,emulation = <0>; + qcom,irq = <258>; + mdiobus = <&mdio0>; + + mtd-mac-address = <&art 0>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + /* + * rpm@108000 { + * pinctrl-0 = <&i2c4_pins>; + * pinctrl-names = "default"; + * }; + */ +}; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wifi { + label = "wifi"; + gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <60>; + wakeup-source; + }; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <60>; + wakeup-source; + }; + + wps { + label = "wps"; + gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <60>; + wakeup-source; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + power_white: power_white { + label = "r7800:white:power"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + power_amber: power_amber { + label = "r7800:amber:power"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + wan_white { + label = "r7800:white:wan"; + gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; + }; + + wan_amber { + label = "r7800:amber:wan"; + gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; + }; + + usb1 { + label = "r7800:white:usb1"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb2 { + label = "r7800:white:usb2"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + + esata { + label = "r7800:white:esata"; + gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; + }; + + wifi { + label = "r7800:white:wifi"; + gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; + }; + + wps { + label = "r7800:white:wps"; + gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&adm_dma { + status = "ok"; +}; diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi new file mode 100644 index 000000000..22b5338cc --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065-v1.0.dtsi @@ -0,0 +1 @@ +#include "qcom-ipq8065.dtsi" diff --git a/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065.dtsi b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065.dtsi new file mode 100644 index 000000000..8c36d8fe9 --- /dev/null +++ b/target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8065.dtsi @@ -0,0 +1,170 @@ +#include "qcom-ipq8064.dtsi" + +/ { + model = "Qualcomm IPQ8065"; + compatible = "qcom,ipq8065", "qcom,ipq8064"; + + cpus { + L2: l2-cache { + l2-cpufreq = <384000000 600000000 1725000000>; + }; + }; + + qcom,pvs { + qcom,pvs-format-a; + qcom,speed0-pvs0-bin-v0 = + < 1725000000 1262500 >, + < 1400000000 1175000 >, + < 1000000000 1100000 >, + < 800000000 1050000 >, + < 600000000 1000000 >, + < 384000000 975000 >; + qcom,speed0-pvs1-bin-v0 = + < 1725000000 1225000 >, + < 1400000000 1150000 >, + < 1000000000 1075000 >, + < 800000000 1025000 >, + < 600000000 975000 >, + < 384000000 950000 >; + qcom,speed0-pvs2-bin-v0 = + < 1725000000 1200000 >, + < 1400000000 1125000 >, + < 1000000000 1050000 >, + < 800000000 1000000 >, + < 600000000 950000 >, + < 384000000 925000 >; + qcom,speed0-pvs3-bin-v0 = + < 1725000000 1175000 >, + < 1400000000 1100000 >, + < 1000000000 1025000 >, + < 800000000 975000 >, + < 600000000 925000 >, + < 384000000 900000 >; + qcom,speed0-pvs4-bin-v0 = + < 1725000000 1150000 >, + < 1400000000 1075000 >, + < 1000000000 1000000 >, + < 800000000 950000 >, + < 600000000 900000 >, + < 384000000 875000 >; + qcom,speed0-pvs5-bin-v0 = + < 1725000000 1100000 >, + < 1400000000 1025000 >, + < 1000000000 950000 >, + < 800000000 900000 >, + < 600000000 850000 >, + < 384000000 825000 >; + qcom,speed0-pvs6-bin-v0 = + < 1725000000 1050000 >, + < 1400000000 975000 >, + < 1000000000 900000 >, + < 800000000 850000 >, + < 600000000 800000 >, + < 384000000 775000 >; + }; + + soc: soc { + + rpm@108000 { + + regulators { + + smb208_s2a: s2a { + regulator-min-microvolt = <775000>; + regulator-max-microvolt = <1275000>; + }; + + smb208_s2b: s2b { + regulator-min-microvolt = <775000>; + regulator-max-microvolt = <1275000>; + }; + }; + }; + + ss_phy_0: phy@110f8830 { + rx_eq = <2>; + tx_deamp_3_5db = <32>; + mpll = <0xa0>; + }; + ss_phy_1: phy@100f8830 { + rx_eq = <2>; + tx_deamp_3_5db = <32>; + mpll = <0xa0>; + }; + + /* Temporary fixed regulator */ + vsdcc_fixed: vsdcc-regulator { + compatible = "regulator-fixed"; + regulator-name = "SDCC Power"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + sdcc1bam:dma@12402000 { + compatible = "qcom,bam-v1.3.0"; + reg = <0x12402000 0x8000>; + interrupts = <0 98 0>; + clocks = <&gcc SDC1_H_CLK>; + clock-names = "bam_clk"; + #dma-cells = <1>; + qcom,ee = <0>; + }; + + sdcc3bam:dma@12182000 { + compatible = "qcom,bam-v1.3.0"; + reg = <0x12182000 0x8000>; + interrupts = <0 96 0>; + clocks = <&gcc SDC3_H_CLK>; + clock-names = "bam_clk"; + #dma-cells = <1>; + qcom,ee = <0>; + }; + + amba { + compatible = "arm,amba-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + sdcc1: sdcc@12400000 { + status = "disabled"; + compatible = "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00051180>; + reg = <0x12400000 0x2000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&gcc SDC1_CLK>, <&gcc SDC1_H_CLK>; + clock-names = "mclk", "apb_pclk"; + bus-width = <8>; + max-frequency = <96000000>; + non-removable; + cap-sd-highspeed; + cap-mmc-highspeed; + vmmc-supply = <&vsdcc_fixed>; + dmas = <&sdcc1bam 2>, <&sdcc1bam 1>; + dma-names = "tx", "rx"; + }; + + sdcc3: sdcc@12180000 { + compatible = "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00051180>; + status = "disabled"; + reg = <0x12180000 0x2000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&gcc SDC3_CLK>, <&gcc SDC3_H_CLK>; + clock-names = "mclk", "apb_pclk"; + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <192000000>; + #mmc-ddr-1_8v; + sd-uhs-sdr104; + sd-uhs-ddr50; + vqmmc-supply = <&vsdcc_fixed>; + dmas = <&sdcc3bam 2>, <&sdcc3bam 1>; + dma-names = "tx", "rx"; + }; + }; + }; +}; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-a42.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-a42.dts deleted file mode 100644 index 887be993e..000000000 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-a42.dts +++ /dev/null @@ -1,244 +0,0 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. - * Copyright (c) 2017, Sven Eckelmann - * - * 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 "qcom-ipq4019.dtsi" -#include "qcom-ipq4019-bus.dtsi" -#include -#include -#include - -/ { - model = "OpenMesh A42"; - compatible = "openmesh,a42", "qcom,ipq4019"; - - reserved-memory { - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges; - - rsvd1@87000000 { - reg = <0x87000000 0x500000>; - no-map; - }; - - wifi_dump@87500000 { - reg = <0x87500000 0x600000>; - no-map; - }; - - rsvd2@87B00000 { - reg = <0x87b00000 0x500000>; - no-map; - }; - }; - - soc { - tcsr@194b000 { - /* select hostmode */ - compatible = "qcom,tcsr"; - reg = <0x194b000 0x100>; - qcom,usb-hsphy-mode-select = ; - status = "ok"; - }; - - ess_tcsr@1953000 { - compatible = "qcom,tcsr"; - reg = <0x1953000 0x1000>; - qcom,ess-interface-select = ; - }; - - tcsr@1949000 { - compatible = "qcom,tcsr"; - reg = <0x1949000 0x100>; - qcom,wifi_glb_cfg = ; - }; - - tcsr@1957000 { - compatible = "qcom,tcsr"; - reg = <0x1957000 0x100>; - qcom,wifi_noc_memtype_m0_m2 = ; - }; - - pinctrl@1000000 { - serial_pins: serial_pinmux { - mux { - pins = "gpio60", "gpio61"; - function = "blsp_uart0"; - bias-disable; - }; - }; - - spi_0_pins: spi_0_pinmux { - pinmux { - function = "blsp_spi0"; - pins = "gpio55", "gpio56", "gpio57"; - }; - pinmux_cs { - function = "gpio"; - pins = "gpio54"; - }; - pinconf { - pins = "gpio55", "gpio56", "gpio57"; - drive-strength = <12>; - bias-disable; - }; - pinconf_cs { - pins = "gpio54"; - drive-strength = <2>; - bias-disable; - output-high; - }; - }; - }; - - blsp_dma: dma@7884000 { - status = "ok"; - }; - - spi_0: spi@78b5000 { - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - status = "ok"; - cs-gpios = <&tlmm 54 0>; - - m25p80@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <24000000>; - - /* partitions are passed via bootloader */ - }; - }; - - serial@78af000 { - pinctrl-0 = <&serial_pins>; - pinctrl-names = "default"; - status = "ok"; - }; - - cryptobam: dma@8e04000 { - status = "ok"; - }; - - crypto@8e3a000 { - status = "ok"; - }; - - watchdog@b017000 { - status = "ok"; - }; - - usb2_hs_phy: hsphy@a8000 { - status = "ok"; - }; - - usb2: usb2@60f8800 { - status = "ok"; - }; - - mdio@90000 { - status = "okay"; - }; - - ess-switch@c000000 { - status = "okay"; - }; - - ess-psgmii@98000 { - status = "okay"; - }; - - edma@c080000 { - status = "okay"; - }; - - wifi@a000000 { - status = "okay"; - qcom,ath10k-calibration-variant = "OM-A42"; - }; - - wifi@a800000 { - status = "okay"; - qcom,ath10k-calibration-variant = "OM-A42"; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 59 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - aliases { - led-boot = &power; - led-failsafe = &power; - led-running = &power; - led-upgrade = &power; - }; - - gpio-leds { - compatible = "gpio-leds"; - - red { - label = "a42:red:status"; - gpios = <&tlmm 0 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "default-off"; - }; - - power: green { - label = "a42:green:status"; - gpios = <&tlmm 1 GPIO_ACTIVE_HIGH>; - }; - - blue { - label = "a42:blue:status"; - gpios = <&tlmm 2 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "default-off"; - }; - }; - - watchdog { - compatible = "linux,wdt-gpio"; - gpios = <&tlmm 5 GPIO_ACTIVE_LOW>; - hw_algo = "toggle"; - /* hw_margin_ms is actually 300s but driver limits it to 60s */ - hw_margin_ms = <60000>; - always-running; - }; -}; - -&gmac0 { - qcom,phy_mdio_addr = <4>; - qcom,poll_required = <1>; - qcom,forced_speed = <1000>; - qcom,forced_duplex = <1>; - vlan_tag = <2 0x20>; -}; - -&gmac1 { - qcom,phy_mdio_addr = <3>; - qcom,poll_required = <1>; - qcom,forced_speed = <1000>; - qcom,forced_duplex = <1>; - vlan_tag = <1 0x10>; -}; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-acrh17.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-acrh17.dts deleted file mode 100644 index beace8ecf..000000000 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq4019-rt-acrh17.dts +++ /dev/null @@ -1,291 +0,0 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "qcom-ipq4019-ap.dk04.1.dtsi" -#include "qcom-ipq4019-bus.dtsi" -#include -#include -#include - -/ { - model = "ASUS RT-ACRH17"; - compatible = "asus,rt-acrh17", "qcom,ipq4019"; - - memory { - device_type = "memory"; - reg = <0x80000000 0x10000000>; - }; - - aliases { - led-boot = &power; - led-failsafe = &power; - led-running = &power; - led-upgrade = &power; - }; - - reserved-memory { - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges; - - rsvd1@87E00000 { - reg = <0x87e00000 0x200000>; - no-map; - }; - }; - - soc { - spi_0: spi@78b5000 { - status = "disabled"; - }; - - pcie0: qcom,pcie@80000 { - compatible = "qcom,msm_pcie"; - cell-index = <0>; - qcom,ctrl-amt = <1>; - - reg = <0x80000 0x2000>, - <0x99000 0x800>, - <0x40000000 0xf1d>, - <0x40000f20 0xa8>, - <0x40100000 0x1000>, - <0x40200000 0x100000>, - <0x40300000 0xd00000>; - reg-names = "parf", "phy", "dm_core", "elbi", - "conf", "io", "bars"; - - #address-cells = <0>; - interrupt-parent = <&pcie0>; - interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12>; - #interrupt-cells = <1>; - interrupt-map-mask = <0xffffffff>; - interrupt-map = <0 &intc 0 141 0 - 1 &intc 0 142 0 - 2 &intc 0 143 0 - 3 &intc 0 144 0 - 4 &intc 0 145 0 - 5 &intc 0 146 0 - 6 &intc 0 147 0 - 7 &intc 0 148 0 - 8 &intc 0 149 0 - 9 &intc 0 150 0 - 10 &intc 0 151 0 - 11 &intc 0 152 0 >; - - interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d", - "int_pls_pme", "int_pme_legacy", "int_pls_err", - "int_aer_legacy", "int_pls_link_up", - "int_pls_link_down", "int_bridge_flush_n","int_wake"; - - qcom,ep-latency = <10>; - - clocks = <&gcc GCC_PCIE_AHB_CLK>, - <&gcc GCC_PCIE_AXI_M_CLK>, - <&gcc GCC_PCIE_AXI_S_CLK>; - - clock-names = "pcie_0_cfg_ahb_clk", - "pcie_0_mstr_axi_clk", - "pcie_0_slv_axi_clk"; - - max-clock-frequency-hz = <0>, <0>, <0>; - - resets = <&gcc PCIE_AXI_M_ARES>, - <&gcc PCIE_AXI_S_ARES>, - <&gcc PCIE_PIPE_ARES>, - <&gcc PCIE_AXI_M_VMIDMT_ARES>, - <&gcc PCIE_AXI_S_XPU_ARES>, - <&gcc PCIE_PARF_XPU_ARES>, - <&gcc PCIE_PHY_ARES>, - <&gcc PCIE_AXI_M_STICKY_ARES>, - <&gcc PCIE_PIPE_STICKY_ARES>, - <&gcc PCIE_PWR_ARES>, - <&gcc PCIE_AHB_ARES>, - <&gcc PCIE_PHY_AHB_ARES>; - - reset-names = "pcie_rst_axi_m_ares", - "pcie_rst_axi_s_ares", - "pcie_rst_pipe_ares", - "pcie_rst_axi_m_vmidmt_ares", - "pcie_rst_axi_s_xpu_ares", - "pcie_rst_parf_xpu_ares", - "pcie_rst_phy_ares", - "pcie_rst_axi_m_sticky_ares", - "pcie_rst_pipe_sticky_ares", - "pcie_rst_pwr_ares", - "pcie_rst_ahb_res", - "pcie_rst_phy_ahb_ares"; - - status = "ok"; - perst-gpio = <&tlmm 38 0>; - wake-gpio = <&tlmm 50 0>; - clkreq-gpio = <&tlmm 39 0>; - }; - - tcsr@194b000 { - /* select hostmode */ - compatible = "qcom,tcsr"; - reg = <0x194b000 0x100>; - qcom,usb-hsphy-mode-select = ; - status = "ok"; - }; - - ess_tcsr@1953000 { - compatible = "qcom,tcsr"; - reg = <0x1953000 0x1000>; - qcom,ess-interface-select = ; - }; - - tcsr@1949000 { - compatible = "qcom,tcsr"; - reg = <0x1949000 0x100>; - qcom,wifi_glb_cfg = ; - }; - - tcsr@1957000 { - compatible = "qcom,tcsr"; - reg = <0x1957000 0x100>; - qcom,wifi_noc_memtype_m0_m2 = ; - }; - - mdio@90000 { - status = "okay"; - }; - - ess-switch@c000000 { - status = "okay"; - }; - - ess-psgmii@98000 { - status = "okay"; - }; - - edma@c080000 { - status = "okay"; - }; - - wifi0: wifi@a000000 { - status = "ok"; - core-id = <0x0>; - qca,msi_addr = <0x0b006040>; - qca,msi_base = <0x40>; - wifi_led_num = <2>; /* Wifi 2G */ - wifi_led_source = <0>; /* source id 0 */ - qcom,mtd-name = "0:ART"; - qcom,cal-offset = <0x1000>; - qcom,cal-len = <12064>; - }; - - wifi1: wifi@a800000 { - status = "disabled"; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 18 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - wps { - label = "wps"; - gpios = <&tlmm 11 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - gpio-leds { - compatible = "gpio-leds"; - - power: status { - label = "rt-acrh17:blue:status"; - gpios = <&tlmm 40 GPIO_ACTIVE_LOW>; - }; - - lan1 { - label = "rt-acrh17:blue:lan1"; - gpios = <&tlmm 45 GPIO_ACTIVE_LOW>; - }; - - lan2 { - label = "rt-acrh17:blue:lan2"; - gpios = <&tlmm 43 GPIO_ACTIVE_LOW>; - }; - - lan3 { - label = "rt-acrh17:blue:lan3"; - gpios = <&tlmm 42 GPIO_ACTIVE_LOW>; - }; - - lan4 { - label = "rt-acrh17:blue:lan4"; - gpios = <&tlmm 49 GPIO_ACTIVE_LOW>; - }; - - wan_blue { - label = "rt-acrh17:blue:wan"; - gpios = <&tlmm 61 GPIO_ACTIVE_HIGH>; - }; - - wan_red { - label = "rt-acrh17:red:wan"; - gpios = <&tlmm 68 GPIO_ACTIVE_HIGH>; - }; - - wlan2g { - label = "rt-acrh17:blue:wlan2g"; - gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; - }; - - wlan5g { - label = "rt-acrh17:blue:wlan5g"; - gpios = <&tlmm 54 GPIO_ACTIVE_LOW>; - }; - }; -}; - -&nand_pins { - pullups { - pins = "gpio53", "gpio58", - "gpio59"; - function = "qpic"; - bias-pull-up; - }; - - pulldowns { - pins = "gpio55", "gpio56", - "gpio57", "gpio60", - "gpio62", "gpio63", "gpio64", - "gpio65", "gpio66", "gpio67", - "gpio69"; - function = "qpic"; - bias-pull-down; - }; -}; - -&i2c_0_pins { - pinmux { - function = "blsp_i2c0"; - pins = "gpio10"; - }; - pinconf { - pins = "gpio10"; - drive-strength = <16>; - bias-disable; - }; -}; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-ap148.dts index 39a0d9656..a3df82909 100644 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +++ b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-ap148.dts @@ -122,7 +122,9 @@ spi-max-frequency = <50000000>; reg = <0>; - linux,part-probe = "qcom-smem"; + partitions { + compatible = "qcom,smem"; + }; }; }; }; @@ -182,7 +184,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; - linux,part-probe = "qcom-smem"; + partitions { + compatible = "qcom,smem"; + }; }; }; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts new file mode 100644 index 000000000..9440de5b8 --- /dev/null +++ b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts @@ -0,0 +1,570 @@ +/* + * BSD LICENSE + * + * Copyright (C) 2017 Christian Mehlis + * Copyright (C) 2018 Mathias Kresin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of any + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "qcom-ipq8064-v1.0.dtsi" + +#include +#include + +/ { + compatible = "compex,wpq864", "qcom,ipq8064"; + model = "Compex WPQ864"; + + aliases { + mdio-gpio0 = &mdio0; + serial0 = &gsbi4_serial; + ethernet0 = &gmac1; + ethernet1 = &gmac0; + + led-boot = &led_pass; + led-failsafe = &led_fail; + led-running = &led_pass; + led-upgrade = &led_pass; + }; + + chosen { + linux,stdout-path = "serial0:115200n8"; + }; + + soc { + rpm@108000 { + pinctrl-0 = <&rpm_pins>; + pinctrl-names = "default"; + }; + + nand@1ac00000 { + status = "okay"; + + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + mt29f2g08abbeah4@0 { + compatible = "qcom,nandcs"; + + reg = <0>; + + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + SBL1@0 { + label = "SBL1"; + reg = <0x0000000 0x0040000>; + read-only; + }; + + MIBIB@40000 { + label = "MIBIB"; + reg = <0x0040000 0x0140000>; + read-only; + }; + + SBL2@180000 { + label = "SBL2"; + reg = <0x0180000 0x0140000>; + read-only; + }; + + SBL3@2c0000 { + label = "SBL3"; + reg = <0x02c0000 0x0280000>; + read-only; + }; + + DDRCONFIG@540000 { + label = "DDRCONFIG"; + reg = <0x0540000 0x0120000>; + read-only; + }; + + SSD@660000 { + label = "SSD"; + reg = <0x0660000 0x0120000>; + read-only; + }; + + TZ@780000 { + label = "TZ"; + reg = <0x0780000 0x0280000>; + read-only; + }; + + RPM@a00000 { + label = "RPM"; + reg = <0x0a00000 0x0280000>; + read-only; + }; + + APPSBL@c80000 { + label = "APPSBL"; + reg = <0x0c80000 0x0500000>; + read-only; + }; + + APPSBLENV@1180000 { + label = "APPSBLENV"; + reg = <0x1180000 0x0080000>; + }; + + ART@1200000 { + label = "ART"; + reg = <0x1200000 0x0140000>; + }; + + ubi@1340000 { + label = "ubi"; + reg = <0x1340000 0x4000000>; + }; + + BOOTCONFIG@1340000 { + label = "BOOTCONFIG"; + reg = <0x5340000 0x0060000>; + }; + + SBL2-1@53a0000- { + label = "SBL2_1"; + reg = <0x53a0000 0x0140000>; + read-only; + }; + + SBL3-1@54e0000 { + label = "SBL3_1"; + reg = <0x54e0000 0x0280000>; + read-only; + }; + + DDRCONFIG-1@5760000 { + label = "DDRCONFIG_1"; + reg = <0x5760000 0x0120000>; + read-only; + }; + + SSD-1@5880000 { + label = "SSD_1"; + reg = <0x5880000 0x0120000>; + read-only; + }; + + TZ-1@59a0000 { + label = "TZ_1"; + reg = <0x59a0000 0x0280000>; + read-only; + }; + + RPM-1@5c20000 { + label = "RPM_1"; + reg = <0x5c20000 0x0280000>; + read-only; + }; + + BOOTCONFIG1@5ea0000 { + label = "BOOTCONFIG1"; + reg = <0x5ea0000 0x0060000>; + }; + + APPSBL-1@5f00000 { + label = "APPSBL_1"; + reg = <0x5f00000 0x0500000>; + read-only; + }; + + ubi-1@6400000 { + label = "ubi_1"; + reg = <0x6400000 0x4000000>; + }; + + unused@a400000 { + label = "unused"; + reg = <0xa400000 0x5c00000>; + }; + }; + }; + }; + }; + + mdio0: mdio { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "virtual,mdio-gpio"; + + pinctrl-0 = <&mdio0_pins>; + pinctrl-names = "default"; + + gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; + + ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x00004 0x7600000 /* PAD0_MODE */ + 0x00008 0x1000000 /* PAD5_MODE */ + 0x0000c 0x80 /* PAD6_MODE */ + 0x000e4 0x6a545 /* MAC_POWER_SEL */ + 0x000e0 0xc74164de /* SGMII_CTRL */ + 0x0007c 0x4e /* PORT0_STATUS */ + 0x00094 0x4e /* PORT6_STATUS */ + >; + }; + + ethernet-phy@4 { + device_type = "ethernet-phy"; + reg = <4>; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + rss4 { + label = "wpq864:green:rss4"; + gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; + }; + + rss3 { + label = "wpq864:green:rss3"; + gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + rss2 { + label = "wpq864:orange:rss2"; + gpios = <&qcom_pinmux 25 GPIO_ACTIVE_HIGH>; + }; + + rss1 { + label = "wpq864:red:rss1"; + gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; + }; + + led_pass: pass { + label = "wpq864:green:pass"; + gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; + }; + + led_fail: fail { + label = "wpq864:green:fail"; + gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; + }; + + usb { + label = "wpq864:green:usb"; + gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; + }; + + usb-pcie { + label = "wpq864:green:usb-pcie"; + gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + reset { + label = "reset"; + gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + beeper { + compatible = "gpio-beeper"; + + pinctrl-0 = <&beeper_pins>; + pinctrl-names = "default"; + + gpios = <&qcom_pinmux 55 GPIO_ACTIVE_HIGH>; + }; +}; + +&adm_dma { + status = "okay"; +}; + +&gmac1 { + status = "okay"; + + pinctrl-0 = <&rgmii2_pins>; + pinctrl-names = "default"; + + phy-mode = "rgmii"; + qcom,id = <1>; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&gmac2 { + status = "okay"; + + phy-mode = "sgmii"; + qcom,id = <2>; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&gsbi4 { + status = "okay"; + qcom,mode = ; +}; + +&gsbi4_serial { + status = "okay"; + + pinctrl-0 = <&uart0_pins>; + pinctrl-names = "default"; +}; + +&gsbi5 { + status = "okay"; + + qcom,mode = ; + + spi@1a280000 { + status = "okay"; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; + + s25fl256s1@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + spi-max-frequency = <50000000>; + }; + }; +}; + +&hs_phy_0 { /* USB3 port 0 HS phy */ + status = "okay"; +}; + +&hs_phy_1 { /* USB3 port 1 HS phy */ + status = "okay"; +}; + +&ss_phy_0 { /* USB3 port 0 SS phy */ + status = "okay"; + + rx_eq = <2>; + tx_deamp_3_5db = <32>; + mpll = <160>; +}; + +&ss_phy_1 { /* USB3 port 1 SS phy */ + status = "okay"; + + rx_eq = <2>; + tx_deamp_3_5db = <32>; + mpll = <160>; +}; + +&pcie0 { + status = "okay"; + + /delete-property/ pinctrl-0; + /delete-property/ pinctrl-names; + /delete-property/ perst-gpios; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "okay"; + + /delete-property/ pinctrl-0; + /delete-property/ pinctrl-names; + /delete-property/ perst-gpios; +}; + +&qcom_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinctrl0 { + pcie0_pcie2_perst { + pins = "gpio3"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + + led_pins: led_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", "gpio22", + "gpio23", "gpio24", "gpio25", "gpio53"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + mux { + pins = "gpio54"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + beeper_pins: beeper_pins { + mux { + pins = "gpio55"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + rpm_pins: rpm_pins { + mux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; + drive-strength = <10>; + bias-disable; + }; + }; + + uart0_pins: uart0_pins { + mux { + pins = "gpio10", "gpio11"; + function = "gsbi4"; + drive-strength = <10>; + bias-disable; + }; + }; + + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19"; + function = "gsbi5"; + drive-strength = <10>; + bias-pull-down; + }; + + clk { + pins = "gpio21"; + function = "gsbi5"; + drive-strength = <12>; + bias-pull-down; + }; + + cs { + pins = "gpio20"; + function = "gpio"; + drive-strength = <10>; + bias-pull-up; + }; + }; + + nand_pins: nand_pins { + mux { + pins = "gpio34", "gpio35", "gpio36", "gpio37", + "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47"; + function = "nand"; + drive-strength = <10>; + bias-disable; + }; + + pullups { + pins = "gpio39"; + bias-pull-up; + }; + + hold { + pins = "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio46", "gpio47"; + bias-bus-hold; + }; + }; + + mdio0_pins: mdio0_pins { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + }; + + rgmii2_pins: rgmii2_pins { + mux { + pins = "gpio27", "gpio28", "gpio29", "gpio30", + "gpio31", "gpio32", "gpio51", "gpio52", + "gpio59", "gpio60", "gpio61", "gpio62"; + function = "rgmii2"; + drive-strength = <8>; + bias-disable; + }; + }; +}; + +&usb3_0 { + status = "okay"; +}; + + +&usb3_1 { + status = "okay"; +}; + +&tcsr { + qcom,usb-ctrl-select = ; +}; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts index 987ee852c..2f829795b 100644 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts +++ b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts @@ -182,7 +182,9 @@ spi-max-frequency = <51200000>; reg = <0>; - linux,part-probe = "qcom-smem"; + partitions { + compatible = "qcom,smem"; + }; }; }; }; diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-r7800.dts b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-r7800.dts index 4c89dcf76..63cb42a3e 100644 --- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-r7800.dts +++ b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8065-r7800.dts @@ -234,7 +234,9 @@ spi-max-frequency = <50000000>; reg = <0>; - linux,part-probe = "qcom-smem"; + partitions { + compatible = "qcom,smem"; + }; }; }; }; diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile index a6402cfa6..73b6ca8c3 100644 --- a/target/linux/ipq806x/image/Makefile +++ b/target/linux/ipq806x/image/Makefile @@ -3,8 +3,6 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/image.mk -UBIFS_OPTS = -m 2048 -e 124KiB -c 4096 -U -F - define Device/Default PROFILES := Default KERNEL_DEPENDS = $$(wildcard $(DTS_DIR)/$$(DEVICE_DTS).dts) @@ -36,8 +34,8 @@ endef define Device/UbiFit KERNEL_IN_UBI := 1 - IMAGES := nand-factory.ubi nand-sysupgrade.bin - IMAGE/nand-factory.ubi := append-ubi + IMAGES := nand-factory.bin nand-sysupgrade.bin + IMAGE/nand-factory.bin := append-ubi IMAGE/nand-sysupgrade.bin := sysupgrade-tar | append-metadata endef @@ -75,20 +73,16 @@ define Device/ZyXELImage IMAGE/mmcblk0p4-kernel.bin := append-kernel endef -define Device/avm_fritzbox-4040 - $(call Device/FitImageLzma) - DEVICE_DTS := qcom-ipq4019-fritz4040 - KERNEL_LOADADDR := 0x80208000 - BLOCKSIZE := 4k - PAGESIZE := 256 - BOARD_NAME := fritz4040 - DEVICE_TITLE := AVM Fritz!Box 4040 - IMAGE_SIZE := 29753344 - IMAGES = sysupgrade.bin - IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | append-metadata - DEVICE_PACKAGES := ipq-wifi-avm_fritzbox-4040 fritz-tffs fritz-caldata u-boot-fritz4040 +define Device/compex_wpq864 + $(call Device/FitImage) + $(call Device/UbiFit) + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS := qcom-ipq8064-wpq864 + DEVICE_TITLE := Compex WPQ864 + DEVICE_PACKAGES := kmod-gpio-beeper endef -TARGET_DEVICES += avm_fritzbox-4040 +TARGET_DEVICES += compex_wpq864 define Device/linksys_ea8500 $(call Device/LegacyImage) @@ -167,99 +161,6 @@ define Device/netgear_r7800 endef TARGET_DEVICES += netgear_r7800 -define Build/copy-file - cat "$(1)" > "$@" -endef - -define Device/asus_rt-ac58u - $(call Device/FitImageLzma) - DEVICE_DTS := qcom-ipq4019-rt-ac58u - KERNEL_LOADADDR := 0x80208000 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DTB_SIZE := 65536 - SUPPORTED_DEVICES += rt-ac58u - DEVICE_TITLE := Asus RT-AC58U - BOARD_NAME := rt-ac58u - IMAGE_SIZE := 20439364 - FILESYSTEMS := squashfs -# Someone - in their infinite wisdom - decided to put the firmware -# version in front of the image name \03\00\00\04 => Version 3.0.0.4 -# Since u-boot works with strings we either need another fixup step -# to add a version... or we are very careful not to add '\0' into that -# string and call it a day.... Yeah, we do the latter! - UIMAGE_NAME:=$(shell echo -e '\03\01\01\01RT-AC58U') - IMAGES = sysupgrade.tar flash-factory.trx - IMAGE/flash-factory.trx := copy-file $(KDIR)/tmp/$$(KERNEL_INITRAMFS_IMAGE) | uImage none - IMAGE/sysupgrade.tar := sysupgrade-tar | append-metadata - DEVICE_PACKAGES := kmod-usb-phy-qcom-ipq4019 ipq-wifi-rt-ac58u -endef -TARGET_DEVICES += asus_rt-ac58u - -define Device/asus_rt-acrh17 - $(call Device/FitImageLzma) - DEVICE_DTS := qcom-ipq4019-rt-acrh17 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DTB_SIZE := 65536 - BOARD_NAME := rt-acrh17 - KERNEL_LOADADDR := 0x80208000 - DEVICE_TITLE := Asus RT-ACRH17 - IMAGE_SIZE := 20439364 - FILESYSTEMS := squashfs - UIMAGE_NAME:=$(shell echo -e '\03\01\01\01RT-AC82U') - IMAGES = sysupgrade.tar flash-factory.trx - IMAGE/flash-factory.trx := copy-file $(KDIR)/tmp/$$(KERNEL_INITRAMFS_IMAGE) | uImage none - IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata - DEVICE_PACKAGES := ath10k-firmware-qca4019 ath10k-firmware-qca9984 -endef -TARGET_DEVICES += asus_rt-acrh17 - -define Device/openmesh_a42 - $(call Device/FitImageLzma) - DEVICE_DTS := qcom-ipq4019-a42 - KERNEL_LOADADDR := 0x80208000 - BLOCKSIZE := 64k - SUPPORTED_DEVICES := openmesh,a42 - DEVICE_TITLE := OpenMesh A42 - KERNEL = kernel-bin | lzma | fit lzma $$(DTS_DIR)/$$(DEVICE_DTS).dtb | pad-to $$(BLOCKSIZE) - IMAGE_SIZE := 15616k - IMAGES = factory.bin sysupgrade.bin - IMAGE/factory.bin := append-rootfs | pad-rootfs | openmesh-image ce_type=A42 - IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-rootfs | sysupgrade-tar rootfs=$$$$@ | append-metadata - DEVICE_PACKAGES := ath10k-firmware-qca4019 uboot-envtools ipq-wifi-openmesh_a42 -endef -TARGET_DEVICES += openmesh_a42 - -define Device/qcom_ap-dk01.1-c1 - DEVICE_TITLE := QCA AP-DK01.1-C1 - BOARD_NAME := ap-dk01.1-c1 - DEVICE_DTS := qcom-ipq4019-ap.dk01.1-c1 - KERNEL_LOADADDR := 0x80208000 - KERNEL_INSTALL := 1 - KERNEL_SIZE := 4096k - IMAGE_SIZE := 26624k - $(call Device/FitImage) - IMAGES := sysupgrade.bin - IMAGE/sysupgrade.bin := append-kernel | pad-to $$$${KERNEL_SIZE} | append-rootfs | pad-rootfs | append-metadata - DEVICE_PACKAGES := ath10k-firmware-qca4019 -endef -TARGET_DEVICES += qcom_ap-dk01.1-c1 - -define Device/qcom_ap-dk04.1-c1 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_DTS := qcom-ipq4019-ap.dk04.1-c1 - KERNEL_LOADADDR := 0x80208000 - KERNEL_INSTALL := 1 - KERNEL_SIZE := 4048k - BLOCKSIZE := 128k - PAGESIZE := 2048 - BOARD_NAME := ap-dk04.1-c1 - DEVICE_TITLE := QCA AP-DK04.1-C1 -endef -TARGET_DEVICES += qcom_ap-dk04.1-c1 - define Device/qcom_ipq8064-ap148 $(call Device/FitImage) $(call Device/UbiFit) @@ -339,6 +240,4 @@ define Device/zyxel_nbg6817 endef TARGET_DEVICES += zyxel_nbg6817 -.NOTPARALLEL: - $(eval $(call BuildImage)) diff --git a/target/linux/ipq806x/modules.mk b/target/linux/ipq806x/modules.mk deleted file mode 100644 index b36b2d7e3..000000000 --- a/target/linux/ipq806x/modules.mk +++ /dev/null @@ -1,14 +0,0 @@ -define KernelPackage/usb-phy-qcom-ipq4019 - TITLE:=IPQ4019 PHY wrappers support - DEPENDS:=+kmod-usb-dwc3 - KCONFIG:= CONFIG_USB_IPQ4019_PHY - FILES:= $(LINUX_DIR)/drivers/usb/phy/phy-qca-baldur.ko $(LINUX_DIR)/drivers/usb/phy/phy-qca-uniphy.ko - AUTOLOAD:=$(call AutoLoad,45,phy-qca-uniphy phy-qca-baldur,1) - $(call AddDepends/usb) -endef - -define KernelPackage/usb-phy-qcom-ipq4019/description - This driver provides support for the USB PHY transceivers on QCA961x chips. -endef - -$(eval $(call KernelPackage,usb-phy-qcom-ipq4019)) diff --git a/target/linux/ipq806x/patches-4.14/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.14/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch new file mode 100644 index 000000000..83d7bbc6f --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch @@ -0,0 +1,71 @@ +From 28d0ed88f536dd639adf1b0c7c08e04be3c8f294 Mon Sep 17 00:00:00 2001 +From: Thomas Pedersen +Date: Mon, 16 May 2016 17:58:50 -0700 +Subject: [PATCH 01/69] dtbindings: qcom_adm: Fix channel specifiers + +Original patch from Andy Gross. + +This patch removes the crci information from the dma +channel property. At least one client device requires +using more than one CRCI value for a channel. This does +not match the current binding and the crci information +needs to be removed. + +Instead, the client device will provide this information +via other means. + +Signed-off-by: Andy Gross +Signed-off-by: Thomas Pedersen +--- + Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt ++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt +@@ -4,8 +4,7 @@ Required properties: + - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 + - reg: Address range for DMA registers + - interrupts: Should contain one interrupt shared by all channels +-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell +- denotes CRCI (client rate control interface) flow control assignment. ++- #dma-cells: must be <1>. First cell denotes the channel number. + - clocks: Should contain the core clock and interface clock. + - clock-names: Must contain "core" for the core clock and "iface" for the + interface clock. +@@ -22,7 +21,7 @@ Example: + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; +- #dma-cells = <2>; ++ #dma-cells = <1>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; + clock-names = "core", "iface"; +@@ -35,15 +34,12 @@ Example: + qcom,ee = <0>; + }; + +-DMA clients must use the format descripted in the dma.txt file, using a three ++DMA clients must use the format descripted in the dma.txt file, using a two + cell specifier for each channel. + +-Each dmas request consists of 3 cells: ++Each dmas request consists of two cells: + 1. phandle pointing to the DMA controller + 2. channel number +- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. +- The CRCI is used for flow control. It identifies the peripheral device that +- is the source/destination for the transferred data. + + Example: + +@@ -55,7 +51,7 @@ Example: + + cs-gpios = <&qcom_pinmux 20 0>; + +- dmas = <&adm_dma 6 9>, +- <&adm_dma 5 10>; ++ dmas = <&adm_dma 6>, ++ <&adm_dma 5>; + dma-names = "rx", "tx"; + }; diff --git a/target/linux/ipq806x/patches-4.14/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.14/0002-dmaengine-Add-ADM-driver.patch new file mode 100644 index 000000000..fedb8ed79 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0002-dmaengine-Add-ADM-driver.patch @@ -0,0 +1,966 @@ +From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001 +From: Thomas Pedersen +Date: Mon, 16 May 2016 17:58:51 -0700 +Subject: [PATCH 02/69] dmaengine: Add ADM driver + +Original patch by Andy Gross. + +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA +controller found in the MSM8x60 and IPQ/APQ8064 platforms. + +The ADM supports both memory to memory transactions and memory +to/from peripheral device transactions. The controller also provides flow +control capabilities for transactions to/from peripheral devices. + +The initial release of this driver supports slave transfers to/from peripherals +and also incorporates CRCI (client rate control interface) flow control. + +Signed-off-by: Andy Gross +Signed-off-by: Thomas Pedersen +--- + drivers/dma/qcom/Kconfig | 10 + + drivers/dma/qcom/Makefile | 1 + + drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 911 insertions(+) + create mode 100644 drivers/dma/qcom/qcom_adm.c + +--- a/drivers/dma/qcom/Kconfig ++++ b/drivers/dma/qcom/Kconfig +@@ -27,3 +27,13 @@ config QCOM_HIDMA + (user to kernel, kernel to kernel, etc.). It only supports + memcpy interface. The core is not intended for general + purpose slave DMA. ++ ++config QCOM_ADM ++ tristate "Qualcomm ADM support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the Qualcomm ADM DMA controller. This controller ++ provides DMA capabilities for both general purpose and on-chip ++ peripheral devices. +--- a/drivers/dma/qcom/Makefile ++++ b/drivers/dma/qcom/Makefile +@@ -4,3 +4,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg + hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o + obj-$(CONFIG_QCOM_HIDMA) += hdma.o + hdma-objs := hidma_ll.o hidma.o hidma_dbg.o ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o +--- /dev/null ++++ b/drivers/dma/qcom/qcom_adm.c +@@ -0,0 +1,914 @@ ++/* ++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../dmaengine.h" ++#include "../virt-dma.h" ++ ++/* ADM registers - calculated from channel number and security domain */ ++#define ADM_CHAN_MULTI 0x4 ++#define ADM_CI_MULTI 0x4 ++#define ADM_CRCI_MULTI 0x4 ++#define ADM_EE_MULTI 0x800 ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) ++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee) ++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) ++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) ++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) ++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) ++#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI) ++#define ADM_GP_CTL 0x3d8 ++#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \ ++ ADM_EE_OFFS(ee)) ++ ++/* channel status */ ++#define ADM_CH_STATUS_VALID BIT(1) ++ ++/* channel result */ ++#define ADM_CH_RSLT_VALID BIT(31) ++#define ADM_CH_RSLT_ERR BIT(3) ++#define ADM_CH_RSLT_FLUSH BIT(2) ++#define ADM_CH_RSLT_TPD BIT(1) ++ ++/* channel conf */ ++#define ADM_CH_CONF_SHADOW_EN BIT(12) ++#define ADM_CH_CONF_MPU_DISABLE BIT(11) ++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9) ++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) ++#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11)) ++ ++/* channel result conf */ ++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) ++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) ++ ++/* CRCI CTL */ ++#define ADM_CRCI_CTL_MUX_SEL BIT(18) ++#define ADM_CRCI_CTL_RST BIT(17) ++ ++/* CI configuration */ ++#define ADM_CI_RANGE_END(x) (x << 24) ++#define ADM_CI_RANGE_START(x) (x << 16) ++#define ADM_CI_BURST_4_WORDS BIT(2) ++#define ADM_CI_BURST_8_WORDS BIT(3) ++ ++/* GP CTL */ ++#define ADM_GP_CTL_LP_EN BIT(12) ++#define ADM_GP_CTL_LP_CNT(x) (x << 8) ++ ++/* Command pointer list entry */ ++#define ADM_CPLE_LP BIT(31) ++#define ADM_CPLE_CMD_PTR_LIST BIT(29) ++ ++/* Command list entry */ ++#define ADM_CMD_LC BIT(31) ++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++ ++#define ADM_CMD_TYPE_SINGLE 0x0 ++#define ADM_CMD_TYPE_BOX 0x3 ++ ++#define ADM_CRCI_MUX_SEL BIT(4) ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K-1) ++#define ADM_MAX_ROWS (SZ_64K-1) ++#define ADM_MAX_CHANNELS 16 ++ ++struct adm_desc_hw_box { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 row_len; ++ u32 num_rows; ++ u32 row_offset; ++}; ++ ++struct adm_desc_hw_single { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 len; ++}; ++ ++struct adm_async_desc { ++ struct virt_dma_desc vd; ++ struct adm_device *adev; ++ ++ size_t length; ++ enum dma_transfer_direction dir; ++ dma_addr_t dma_addr; ++ size_t dma_len; ++ ++ void *cpl; ++ dma_addr_t cp_addr; ++ u32 crci; ++ u32 mux; ++ u32 blk_size; ++}; ++ ++struct adm_chan { ++ struct virt_dma_chan vc; ++ struct adm_device *adev; ++ ++ /* parsed from DT */ ++ u32 id; /* channel id */ ++ ++ struct adm_async_desc *curr_txd; ++ struct dma_slave_config slave; ++ struct list_head node; ++ ++ int error; ++ int initialized; ++}; ++ ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct adm_chan, vc.chan); ++} ++ ++struct adm_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct adm_chan *channels; ++ ++ u32 ee; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ ++ struct reset_control *clk_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ int irq; ++}; ++ ++/** ++ * adm_free_chan - Frees dma resources associated with the specific channel ++ * ++ * Free all allocated descriptors associated with this channel ++ * ++ */ ++static void adm_free_chan(struct dma_chan *chan) ++{ ++ /* free all queued descriptors */ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++} ++ ++/** ++ * adm_get_blksize - Get block size from burst value ++ * ++ */ ++static int adm_get_blksize(unsigned int burst) ++{ ++ int ret; ++ ++ switch (burst) { ++ case 16: ++ case 32: ++ case 64: ++ case 128: ++ ret = ffs(burst>>4) - 1; ++ break; ++ case 192: ++ ret = 4; ++ break; ++ case 256: ++ ret = 5; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @crci: CRCI value ++ * @burst: Burst size of transaction ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_fc_descriptors(struct adm_chan *achan, ++ void *desc, struct scatterlist *sg, u32 crci, u32 burst, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_box *box_desc = NULL; ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 rows, row_offset, crci_cmd; ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ crci_cmd = ADM_CMD_SRC_CRCI(crci); ++ row_offset = burst; ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ crci_cmd = ADM_CMD_DST_CRCI(crci); ++ row_offset = burst << 16; ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ while (remainder >= burst) { ++ box_desc = desc; ++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; ++ box_desc->row_offset = row_offset; ++ box_desc->src_addr = *src; ++ box_desc->dst_addr = *dst; ++ ++ rows = remainder / burst; ++ rows = min_t(u32, rows, ADM_MAX_ROWS); ++ box_desc->num_rows = rows << 16 | rows; ++ box_desc->row_len = burst << 16 | burst; ++ ++ *incr_addr += burst * rows; ++ remainder -= burst * rows; ++ desc += sizeof(*box_desc); ++ } ++ ++ /* if leftover bytes, do one single descriptor */ ++ if (remainder) { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; ++ single_desc->len = remainder; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ desc += sizeof(*single_desc); ++ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ } else { ++ if (box_desc && sg_is_last(sg)) ++ box_desc->cmd |= ADM_CMD_LC; ++ } ++ ++ return desc; ++} ++ ++/** ++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, ++ void *desc, struct scatterlist *sg, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ do { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ single_desc->len = (remainder > ADM_MAX_XFER) ? ++ ADM_MAX_XFER : remainder; ++ ++ remainder -= single_desc->len; ++ *incr_addr += single_desc->len; ++ desc += sizeof(*single_desc); ++ } while (remainder); ++ ++ /* set last command if this is the end of the whole transaction */ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ ++ return desc; ++} ++ ++/** ++ * adm_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct scatterlist *sg; ++ dma_addr_t cple_addr; ++ u32 i, burst; ++ u32 single_count = 0, box_count = 0, crci = 0; ++ void *desc; ++ u32 *cple; ++ int blk_size = 0; ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(adev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* ++ * get burst value from slave configuration ++ */ ++ burst = (direction == DMA_MEM_TO_DEV) ? ++ achan->slave.dst_maxburst : ++ achan->slave.src_maxburst; ++ ++ /* if using flow control, validate burst and crci values */ ++ if (achan->slave.device_fc) { ++ ++ blk_size = adm_get_blksize(burst); ++ if (blk_size < 0) { ++ dev_err(adev->dev, "invalid burst value: %d\n", ++ burst); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ crci = achan->slave.slave_id & 0xf; ++ if (!crci || achan->slave.slave_id > 0x1f) { ++ dev_err(adev->dev, "invalid crci value\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ /* iterate through sgs and compute allocation size of structures */ ++ for_each_sg(sgl, sg, sg_len, i) { ++ if (achan->slave.device_fc) { ++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, ++ ADM_MAX_ROWS); ++ if (sg_dma_len(sg) % burst) ++ single_count++; ++ } else { ++ single_count += DIV_ROUND_UP(sg_dma_len(sg), ++ ADM_MAX_XFER); ++ } ++ } ++ ++ async_desc = kzalloc(sizeof(*async_desc), GFP_ATOMIC); ++ if (!async_desc) ++ return ERR_PTR(-ENOMEM); ++ ++ if (crci) ++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? ++ ADM_CRCI_CTL_MUX_SEL : 0; ++ async_desc->crci = crci; ++ async_desc->blk_size = blk_size; ++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + ++ box_count * sizeof(struct adm_desc_hw_box) + ++ sizeof(*cple) + 2 * ADM_DESC_ALIGN; ++ ++ async_desc->cpl = kzalloc(async_desc->dma_len, GFP_ATOMIC); ++ if (!async_desc->cpl) ++ goto free; ++ ++ async_desc->adev = adev; ++ ++ /* both command list entry and descriptors must be 8 byte aligned */ ++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ async_desc->length += sg_dma_len(sg); ++ ++ if (achan->slave.device_fc) ++ desc = adm_process_fc_descriptors(achan, desc, sg, crci, ++ burst, direction); ++ else ++ desc = adm_process_non_fc_descriptors(achan, desc, sg, ++ direction); ++ } ++ ++ async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl, ++ async_desc->dma_len, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(adev->dev, async_desc->dma_addr)) ++ goto free; ++ ++ cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl); ++ ++ /* init cmd list */ ++ dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple), ++ DMA_TO_DEVICE); ++ *cple = ADM_CPLE_LP; ++ *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3; ++ dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple), ++ DMA_TO_DEVICE); ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++ ++free: ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++} ++ ++/** ++ * adm_terminate_all - terminate all transactions on a channel ++ * @achan: adm dma channel ++ * ++ * Dequeues and frees all transactions, aborts current transaction ++ * No callbacks are done ++ * ++ */ ++static int adm_terminate_all(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ vchan_get_all_descriptors(&achan->vc, &head); ++ ++ /* send flush command to terminate current transaction */ ++ writel_relaxed(0x0, ++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ vchan_dma_desc_free_list(&achan->vc, &head); ++ ++ return 0; ++} ++ ++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flag; ++ ++ spin_lock_irqsave(&achan->vc.lock, flag); ++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); ++ spin_unlock_irqrestore(&achan->vc.lock, flag); ++ ++ return 0; ++} ++ ++/** ++ * adm_start_dma - start next transaction ++ * @achan - ADM dma channel ++ */ ++static void adm_start_dma(struct adm_chan *achan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ ++ lockdep_assert_held(&achan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ /* write next command list out to the CMD FIFO */ ++ async_desc = container_of(vd, struct adm_async_desc, vd); ++ achan->curr_txd = async_desc; ++ ++ /* reset channel error */ ++ achan->error = 0; ++ ++ if (!achan->initialized) { ++ /* enable interrupts */ ++ writel(ADM_CH_CONF_SHADOW_EN | ++ ADM_CH_CONF_PERM_MPU_CONF | ++ ADM_CH_CONF_MPU_DISABLE | ++ ADM_CH_CONF_SEC_DOMAIN(adev->ee), ++ adev->regs + ADM_CH_CONF(achan->id)); ++ ++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ achan->initialized = 1; ++ } ++ ++ /* set the crci block size if this transaction requires CRCI */ ++ if (async_desc->crci) { ++ writel(async_desc->mux | async_desc->blk_size, ++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); ++ } ++ ++ /* make sure IRQ enable doesn't get reordered */ ++ wmb(); ++ ++ /* write next command list out to the CMD FIFO */ ++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, ++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); ++} ++ ++/** ++ * adm_dma_irq - irq handler for ADM controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t adm_dma_irq(int irq, void *data) ++{ ++ struct adm_device *adev = data; ++ u32 srcs, i; ++ struct adm_async_desc *async_desc; ++ unsigned long flags; ++ ++ srcs = readl_relaxed(adev->regs + ++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ struct adm_chan *achan = &adev->channels[i]; ++ u32 status, result; ++ ++ if (srcs & BIT(i)) { ++ status = readl_relaxed(adev->regs + ++ ADM_CH_STATUS_SD(i, adev->ee)); ++ ++ /* if no result present, skip */ ++ if (!(status & ADM_CH_STATUS_VALID)) ++ continue; ++ ++ result = readl_relaxed(adev->regs + ++ ADM_CH_RSLT(i, adev->ee)); ++ ++ /* no valid results, skip */ ++ if (!(result & ADM_CH_RSLT_VALID)) ++ continue; ++ ++ /* flag error if transaction was flushed or failed */ ++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) ++ achan->error = 1; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ async_desc = achan->curr_txd; ++ ++ achan->curr_txd = NULL; ++ ++ if (async_desc) { ++ vchan_cookie_complete(&async_desc->vd); ++ ++ /* kick off next DMA */ ++ adm_start_dma(achan); ++ } ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * adm_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ size_t residue = 0; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE || !txstate) ++ return ret; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ vd = vchan_find_desc(&achan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct adm_async_desc, vd)->length; ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ /* ++ * residue is either the full length if it is in the issued list, or 0 ++ * if it is in progress. We have no reliable way of determining ++ * anything inbetween ++ */ ++ dma_set_residue(txstate, residue); ++ ++ if (achan->error) ++ return DMA_ERROR; ++ ++ return ret; ++} ++ ++/** ++ * adm_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Issues all pending transactions and starts DMA ++ */ ++static void adm_issue_pending(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) ++ adm_start_dma(achan); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++} ++ ++/** ++ * adm_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void adm_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct adm_async_desc *async_desc = container_of(vd, ++ struct adm_async_desc, vd); ++ ++ dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr, ++ async_desc->dma_len, DMA_TO_DEVICE); ++ kfree(async_desc->cpl); ++ kfree(async_desc); ++} ++ ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, ++ u32 index) ++{ ++ achan->id = index; ++ achan->adev = adev; ++ ++ vchan_init(&achan->vc, &adev->common); ++ achan->vc.desc_free = adm_dma_free_desc; ++} ++ ++static int adm_dma_probe(struct platform_device *pdev) ++{ ++ struct adm_device *adev; ++ struct resource *iores; ++ int ret; ++ u32 i; ++ ++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); ++ if (!adev) ++ return -ENOMEM; ++ ++ adev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ adev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(adev->regs)) ++ return PTR_ERR(adev->regs); ++ ++ adev->irq = platform_get_irq(pdev, 0); ++ if (adev->irq < 0) ++ return adev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); ++ if (ret) { ++ dev_err(adev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ adev->core_clk = devm_clk_get(adev->dev, "core"); ++ if (IS_ERR(adev->core_clk)) ++ return PTR_ERR(adev->core_clk); ++ ++ ret = clk_prepare_enable(adev->core_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable core clock\n"); ++ return ret; ++ } ++ ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) { ++ ret = PTR_ERR(adev->iface_clk); ++ goto err_disable_core_clk; ++ } ++ ++ ret = clk_prepare_enable(adev->iface_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); ++ goto err_disable_core_clk; ++ } ++ ++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ ret = PTR_ERR(adev->clk_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ ret = PTR_ERR(adev->c0_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ ret = PTR_ERR(adev->c1_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ ret = PTR_ERR(adev->c2_reset); ++ goto err_disable_clks; ++ } ++ ++ reset_control_assert(adev->clk_reset); ++ reset_control_assert(adev->c0_reset); ++ reset_control_assert(adev->c1_reset); ++ reset_control_assert(adev->c2_reset); ++ ++ reset_control_deassert(adev->clk_reset); ++ reset_control_deassert(adev->c0_reset); ++ reset_control_deassert(adev->c1_reset); ++ reset_control_deassert(adev->c2_reset); ++ ++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, ++ sizeof(*adev->channels), GFP_KERNEL); ++ ++ if (!adev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clks; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&adev->common.channels); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) ++ adm_channel_init(adev, &adev->channels[i], i); ++ ++ /* reset CRCIs */ ++ for (i = 0; i < 16; i++) ++ writel(ADM_CRCI_CTL_RST, adev->regs + ++ ADM_CRCI_CTL(i, adev->ee)); ++ ++ /* configure client interfaces */ ++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); ++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); ++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); ++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), ++ adev->regs + ADM_GP_CTL); ++ ++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, ++ 0, "adm_dma", adev); ++ if (ret) ++ goto err_disable_clks; ++ ++ platform_set_drvdata(pdev, adev); ++ ++ adev->common.dev = adev->dev; ++ adev->common.dev->dma_parms = &adev->dma_parms; ++ ++ /* set capabilities */ ++ dma_cap_zero(adev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); ++ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; ++ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.device_free_chan_resources = adm_free_chan; ++ adev->common.device_prep_slave_sg = adm_prep_slave_sg; ++ adev->common.device_issue_pending = adm_issue_pending; ++ adev->common.device_tx_status = adm_tx_status; ++ adev->common.device_terminate_all = adm_terminate_all; ++ adev->common.device_config = adm_slave_config; ++ ++ ret = dma_async_device_register(&adev->common); ++ if (ret) { ++ dev_err(adev->dev, "failed to register dma async device\n"); ++ goto err_disable_clks; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, ++ of_dma_xlate_by_chan_id, ++ &adev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&adev->common); ++err_disable_clks: ++ clk_disable_unprepare(adev->iface_clk); ++err_disable_core_clk: ++ clk_disable_unprepare(adev->core_clk); ++ ++ return ret; ++} ++ ++static int adm_dma_remove(struct platform_device *pdev) ++{ ++ struct adm_device *adev = platform_get_drvdata(pdev); ++ struct adm_chan *achan; ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&adev->common); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ achan = &adev->channels[i]; ++ ++ /* mask IRQs for this channel/EE pair */ ++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ adm_terminate_all(&adev->channels[i].vc.chan); ++ } ++ ++ devm_free_irq(adev->dev, adev->irq, adev); ++ ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++ ++static struct platform_driver adm_dma_driver = { ++ .probe = adm_dma_probe, ++ .remove = adm_dma_remove, ++ .driver = { ++ .name = "adm-dma-engine", ++ .of_match_table = adm_of_match, ++ }, ++}; ++ ++module_platform_driver(adm_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross "); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch b/target/linux/ipq806x/patches-4.14/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch similarity index 86% rename from target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch rename to target/linux/ipq806x/patches-4.14/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch index 06b61a07e..900647beb 100644 --- a/target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch +++ b/target/linux/ipq806x/patches-4.14/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch @@ -16,9 +16,9 @@ Signed-off-by: Matthew McClintock --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c -@@ -35,6 +35,8 @@ static const struct of_device_id machine - - { .compatible = "marvell,berlin", }, +@@ -46,6 +46,8 @@ static const struct of_device_id whiteli + { .compatible = "marvell,pxa250", }, + { .compatible = "marvell,pxa270", }, + { .compatible = "qcom,ipq4019", }, + diff --git a/target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch b/target/linux/ipq806x/patches-4.14/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch rename to target/linux/ipq806x/patches-4.14/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch diff --git a/target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch b/target/linux/ipq806x/patches-4.14/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch similarity index 97% rename from target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch rename to target/linux/ipq806x/patches-4.14/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch index 42bb4a6e3..1fdc2141d 100644 --- a/target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch +++ b/target/linux/ipq806x/patches-4.14/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch @@ -12,7 +12,7 @@ Signed-off-by: Matthew McClintock --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -179,6 +179,8 @@ +@@ -202,6 +202,8 @@ clock-names = "iface", "core"; #address-cells = <1>; #size-cells = <0>; diff --git a/target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch b/target/linux/ipq806x/patches-4.14/0020-qcom-ipq4019-enable-DMA-for-spi.patch similarity index 97% rename from target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch rename to target/linux/ipq806x/patches-4.14/0020-qcom-ipq4019-enable-DMA-for-spi.patch index c1fa5c729..e9cfc7557 100644 --- a/target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch +++ b/target/linux/ipq806x/patches-4.14/0020-qcom-ipq4019-enable-DMA-for-spi.patch @@ -12,7 +12,7 @@ Signed-off-by: Matthew McClintock --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -167,6 +167,8 @@ +@@ -190,6 +190,8 @@ clock-names = "core", "iface"; #address-cells = <1>; #size-cells = <0>; diff --git a/target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch b/target/linux/ipq806x/patches-4.14/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch similarity index 98% rename from target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch rename to target/linux/ipq806x/patches-4.14/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch index 8cd3e205d..4c468e2cf 100644 --- a/target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch +++ b/target/linux/ipq806x/patches-4.14/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch @@ -24,7 +24,7 @@ Signed-off-by: Matthew McClintock --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -617,6 +617,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ +@@ -698,6 +698,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ qcom-apq8084-ifc6540.dtb \ qcom-apq8084-mtp.dtb \ qcom-ipq4019-ap.dk01.1-c1.dtb \ @@ -34,7 +34,7 @@ Signed-off-by: Matthew McClintock qcom-msm8960-cdp.dtb \ --- /dev/null +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts -@@ -0,0 +1,22 @@ +@@ -0,0 +1,21 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any @@ -55,7 +55,6 @@ Signed-off-by: Matthew McClintock + +/ { + model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK04.1-C1"; -+ compatible = "qcom,ap-dk04.1-c1", "qcom,ipq4019"; +}; --- /dev/null +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi diff --git a/target/linux/ipq806x/patches-4.14/0030-clk-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.14/0030-clk-Disable-i2c-device-on-gsbi4.patch new file mode 100644 index 000000000..b2a6afe0d --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0030-clk-Disable-i2c-device-on-gsbi4.patch @@ -0,0 +1,40 @@ +From 0c974b87829e007dc4fae94e20d488204e20e662 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 08:16:10 +0100 +Subject: [PATCH 30/69] clk: Disable i2c device on gsbi4 + +This patch was not annotated and comes from the v4.4 tree. + +Signed-off-by: John Crispin +--- + drivers/clk/qcom/gcc-ipq806x.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, +- .flags = CLK_SET_PARENT_GATE, ++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk + }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -890,6 +890,7 @@ static struct clk_branch gsbi1_h_clk = { + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_h_clk", + .ops = &clk_branch_ops, ++ .flags = CLK_IGNORE_UNUSED, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-4.14/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.14/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch new file mode 100644 index 000000000..2e0ce4364 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch @@ -0,0 +1,275 @@ +From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 9 Mar 2017 08:18:08 +0100 +Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms + +On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is +used to store partition layout. This new parser can now be used to read +SMEM and use it to register an MTD layout according to its content. + +Signed-off-by: Mathieu Olivari +Signed-off-by: Ram Chandra Jangir +--- + drivers/mtd/Kconfig | 7 ++ + drivers/mtd/Makefile | 1 + + drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 236 insertions(+) + create mode 100644 drivers/mtd/qcom_smem_part.c + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -194,6 +194,13 @@ config MTD_MYLOADER_PARTS + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. + ++config MTD_QCOM_SMEM_PARTS ++ tristate "QCOM SMEM partitioning support" ++ depends on QCOM_SMEM ++ help ++ This provides partitions parser for QCOM devices using SMEM ++ such as IPQ806x. ++ + comment "User Modules And Translation Layers" + + # +--- /dev/null ++++ b/drivers/mtd/qcom_smem_part.c +@@ -0,0 +1,228 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Processor/host identifier for the application processor */ ++#define SMEM_HOST_APPS 0 ++ ++/* SMEM items index */ ++#define SMEM_AARM_PARTITION_TABLE 9 ++#define SMEM_BOOT_FLASH_TYPE 421 ++#define SMEM_BOOT_FLASH_BLOCK_SIZE 424 ++ ++/* SMEM Flash types */ ++#define SMEM_FLASH_NAND 2 ++#define SMEM_FLASH_SPI 6 ++ ++#define SMEM_PART_NAME_SZ 16 ++#define SMEM_PARTS_MAX 32 ++ ++struct smem_partition { ++ char name[SMEM_PART_NAME_SZ]; ++ __le32 start; ++ __le32 size; ++ __le32 attr; ++}; ++ ++struct smem_partition_table { ++ u8 magic[8]; ++ __le32 version; ++ __le32 len; ++ struct smem_partition parts[SMEM_PARTS_MAX]; ++}; ++ ++/* SMEM Magic values in partition table */ ++static const u8 SMEM_PTABLE_MAGIC[] = { ++ 0xaa, 0x73, 0xee, 0x55, ++ 0xdb, 0xbd, 0x5e, 0xe3, ++}; ++ ++static int qcom_smem_get_flash_blksz(u64 **smem_blksz) ++{ ++ size_t size; ++ ++ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, ++ &size); ++ ++ if (IS_ERR(*smem_blksz)) { ++ pr_err("Unable to read flash blksz from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ if (size != sizeof(**smem_blksz)) { ++ pr_err("Invalid flash blksz size in SMEM\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_get_flash_type(u64 **smem_flash_type) ++{ ++ size_t size; ++ ++ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, ++ &size); ++ ++ if (IS_ERR(*smem_flash_type)) { ++ pr_err("Unable to read flash type from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ if (size != sizeof(**smem_flash_type)) { ++ pr_err("Invalid flash type size in SMEM\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) ++{ ++ size_t size; ++ ++ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, ++ &size); ++ ++ if (IS_ERR(*pparts)) { ++ pr_err("Unable to read partition table from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ return 0; ++} ++ ++static int of_dev_node_match(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static bool is_spi_device(struct device_node *np) ++{ ++ struct device *dev; ++ ++ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); ++ if (!dev) ++ return false; ++ ++ put_device(dev); ++ return true; ++} ++ ++static int parse_qcom_smem_partitions(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct smem_partition_table *smem_parts; ++ u64 *smem_flash_type, *smem_blksz; ++ struct mtd_partition *mtd_parts; ++ struct device_node *of_node = master->dev.of_node; ++ int i, ret; ++ ++ /* ++ * SMEM will only store the partition table of the boot device. ++ * If this is not the boot device, do not return any partition. ++ */ ++ ret = qcom_smem_get_flash_type(&smem_flash_type); ++ if (ret < 0) ++ return ret; ++ ++ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) ++ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) ++ return 0; ++ ++ /* ++ * Just for sanity purpose, make sure the block size in SMEM matches the ++ * block size of the MTD device ++ */ ++ ret = qcom_smem_get_flash_blksz(&smem_blksz); ++ if (ret < 0) ++ return ret; ++ ++ if (*smem_blksz != master->erasesize) { ++ pr_err("SMEM block size differs from MTD block size\n"); ++ return -EINVAL; ++ } ++ ++ /* Get partition pointer from SMEM */ ++ ret = qcom_smem_get_flash_partitions(&smem_parts); ++ if (ret < 0) ++ return ret; ++ ++ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, ++ sizeof(SMEM_PTABLE_MAGIC))) { ++ pr_err("SMEM partition magic invalid\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate and populate the mtd structures */ ++ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), ++ GFP_KERNEL); ++ if (!mtd_parts) ++ return -ENOMEM; ++ ++ for (i = 0; i < smem_parts->len; i++) { ++ struct smem_partition *s_part = &smem_parts->parts[i]; ++ struct mtd_partition *m_part = &mtd_parts[i]; ++ ++ m_part->name = s_part->name; ++ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); ++ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); ++ ++ /* ++ * The last SMEM partition may have its size marked as ++ * something like 0xffffffff, which means "until the end of the ++ * flash device". In this case, truncate it. ++ */ ++ if (m_part->offset + m_part->size > master->size) ++ m_part->size = master->size - m_part->offset; ++ } ++ ++ *pparts = mtd_parts; ++ ++ return smem_parts->len; ++} ++ ++static struct mtd_part_parser qcom_smem_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_qcom_smem_partitions, ++ .name = "qcom-smem", ++}; ++ ++static int __init qcom_smem_parser_init(void) ++{ ++ register_mtd_parser(&qcom_smem_parser); ++ return 0; ++} ++ ++static void __exit qcom_smem_parser_exit(void) ++{ ++ deregister_mtd_parser(&qcom_smem_parser); ++} ++ ++module_init(qcom_smem_parser_init); ++module_exit(qcom_smem_parser_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mathieu Olivari "); ++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o ++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o + obj-y += parsers/ + + # 'Users' - code which presents functionality to userspace. diff --git a/target/linux/ipq806x/patches-4.14/0032-phy-add-qcom-dwc3-phy.patch b/target/linux/ipq806x/patches-4.14/0032-phy-add-qcom-dwc3-phy.patch new file mode 100644 index 000000000..eda3e3887 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0032-phy-add-qcom-dwc3-phy.patch @@ -0,0 +1,620 @@ +From b9004f4fd23e4c614d71c972f3a9311665480e29 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Thu, 9 Mar 2017 08:19:18 +0100 +Subject: [PATCH 32/69] phy: add qcom dwc3 phy + +Signed-off-by: Andy Gross +--- + drivers/phy/qualcomm/Kconfig | 12 ++ + drivers/phy/qualcomm/Makefile | 1 + + drivers/phy/qualcomm/phy-qcom-dwc3.c | 484 +++++++++++++++++++++++++++++++++++ + 3 files changed, 497 insertions(+) + create mode 100644 drivers/phy/phy-qcom-dwc3.c + +--- a/drivers/phy/qualcomm/Kconfig ++++ b/drivers/phy/qualcomm/Kconfig +@@ -8,6 +8,17 @@ config PHY_QCOM_APQ8064_SATA + depends on OF + select GENERIC_PHY + ++config PHY_QCOM_DWC3 ++ tristate "QCOM DWC3 USB PHY support" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ help ++ This option enables support for the Synopsis PHYs present inside the ++ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS ++ PHY controllers. ++ + config PHY_QCOM_IPQ806X_SATA + tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" + depends on ARCH_QCOM +--- a/drivers/phy/qualcomm/Makefile ++++ b/drivers/phy/qualcomm/Makefile +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o ++obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o + obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o + obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o + obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-dwc3.c +@@ -0,0 +1,575 @@ ++/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++* This program is distributed in the hope that it will be useful, ++* but WITHOUT ANY WARRANTY; without even the implied warranty of ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++* GNU General Public License for more details. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define QSCRATCH_GENERAL_CFG (0x08) ++#define HSUSB_PHY_CTRL_REG (0x10) ++ ++/* PHY_CTRL_REG */ ++#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) ++#define HSUSB_CTRL_USB2_SUSPEND BIT(23) ++#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) ++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) ++#define HSUSB_CTRL_USE_CLKCORE BIT(18) ++#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) ++#define HSUSB_CTRL_COMMONONN BIT(11) ++#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) ++#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) ++#define HSUSB_CTRL_CLAMP_EN BIT(7) ++#define HSUSB_CTRL_RETENABLEN BIT(1) ++#define HSUSB_CTRL_POR BIT(0) ++ ++/* QSCRATCH_GENERAL_CFG */ ++#define HSUSB_GCFG_XHCI_REV BIT(2) ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define SSUSB_PHY_CTRL_REG (0x00) ++#define SSUSB_PHY_PARAM_CTRL_1 (0x04) ++#define SSUSB_PHY_PARAM_CTRL_2 (0x08) ++#define CR_PROTOCOL_DATA_IN_REG (0x0c) ++#define CR_PROTOCOL_DATA_OUT_REG (0x10) ++#define CR_PROTOCOL_CAP_ADDR_REG (0x14) ++#define CR_PROTOCOL_CAP_DATA_REG (0x18) ++#define CR_PROTOCOL_READ_REG (0x1c) ++#define CR_PROTOCOL_WRITE_REG (0x20) ++ ++/* PHY_CTRL_REG */ ++#define SSUSB_CTRL_REF_USE_PAD BIT(28) ++#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) ++#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) ++#define SSUSB_CTRL_SS_PHY_EN BIT(8) ++#define SSUSB_CTRL_SS_PHY_RESET BIT(7) ++ ++/* SSPHY control registers */ ++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane) ++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane) ++ ++/* SSPHY SoC version specific values */ ++#define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */ ++#define SSPHY_TX_DEEMPH_3_5DB 23 /* Override value for transmit ++ preemphasis */ ++#define SSPHY_MPLL_VALUE 0 /* Override value for mpll */ ++ ++/* QSCRATCH PHY_PARAM_CTRL1 fields */ ++#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK 0x07f00000u ++#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK 0x000fc000u ++#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK 0x00003f00u ++#define PHY_PARAM_CTRL1_LOS_BIAS_MASK 0x000000f8u ++ ++#define PHY_PARAM_CTRL1_MASK \ ++ (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \ ++ PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \ ++ PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \ ++ PHY_PARAM_CTRL1_LOS_BIAS_MASK) ++ ++#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ ++ (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) ++#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ ++ (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) ++#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ ++ (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) ++#define PHY_PARAM_CTRL1_LOS_BIAS(x) \ ++ (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) ++ ++/* RX OVRD IN HI bits */ ++#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) ++#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) ++#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) ++#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700 ++#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8 ++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) ++#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018 ++#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) ++#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003 ++ ++/* TX OVRD DRV LO register bits */ ++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F ++#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80 ++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7 ++#define TX_OVRD_DRV_LO_EN BIT(14) ++ ++/* SS CAP register bits */ ++#define SS_CR_CAP_ADDR_REG BIT(0) ++#define SS_CR_CAP_DATA_REG BIT(0) ++#define SS_CR_READ_REG BIT(0) ++#define SS_CR_WRITE_REG BIT(0) ++ ++struct qcom_dwc3_usb_phy { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *xo_clk; ++ struct clk *ref_clk; ++ u32 rx_eq; ++ u32 tx_deamp_3_5db; ++ u32 mpll; ++}; ++ ++struct qcom_dwc3_phy_drvdata { ++ struct phy_ops ops; ++ u32 clk_rate; ++}; ++ ++/** ++ * Write register and read back masked value to confirm it is written ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @mask - register bitmask specifying what should be updated ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_phy_write_readback( ++ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(phy_dwc3->base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, phy_dwc3->base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(phy_dwc3->base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", ++ val, offset); ++} ++ ++static int wait_for_latch(void __iomem *addr) ++{ ++ u32 retry = 10; ++ ++ while (true) { ++ if (!readl(addr)) ++ break; ++ ++ if (--retry == 0) ++ return -ETIMEDOUT; ++ ++ usleep_range(10, 20); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Write SSPHY register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to write. ++ * @val - value to write. ++ */ ++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3, ++ u32 addr, u32 val) ++{ ++ int ret; ++ ++ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); ++ ++err_wait: ++ if (ret) ++ dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); ++ return ret; ++} ++ ++/** ++ * Read SSPHY register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to read. ++ */ ++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val) ++{ ++ int ret; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); ++ if (ret) ++ goto err_wait; ++ ++ /* ++ * Due to hardware bug, first read of SSPHY register might be ++ * incorrect. Hence as workaround, SW should perform SSPHY register ++ * read twice, but use only second read and ignore first read. ++ */ ++ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); ++ if (ret) ++ goto err_wait; ++ ++ /* throwaway read */ ++ readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ ++ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); ++ if (ret) ++ goto err_wait; ++ ++ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ ++err_wait: ++ return ret; ++} ++ ++static int qcom_dwc3_hs_phy_init(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ int ret; ++ u32 val; ++ ++ ret = clk_prepare_enable(phy_dwc3->xo_clk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(phy_dwc3->ref_clk); ++ if (ret) { ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ return ret; ++ } ++ ++ /* ++ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel ++ * enable clamping, and disable RETENTION (power-on default is ENABLED) ++ */ ++ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | ++ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | ++ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | ++ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | ++ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; ++ ++ /* use core clock if external reference is not present */ ++ if (!phy_dwc3->xo_clk) ++ val |= HSUSB_CTRL_USE_CLKCORE; ++ ++ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ ++ /* Disable (bypass) VBUS and ID filters */ ++ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_hs_phy_exit(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(phy_dwc3->ref_clk); ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_ss_phy_init(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ int ret; ++ u32 data = 0; ++ ++ ret = clk_prepare_enable(phy_dwc3->xo_clk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(phy_dwc3->ref_clk); ++ if (ret) { ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ return ret; ++ } ++ ++ /* reset phy */ ++ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ writel(data | SSUSB_CTRL_SS_PHY_RESET, ++ phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* clear REF_PAD if we don't have XO clk */ ++ if (!phy_dwc3->xo_clk) ++ data &= ~SSUSB_CTRL_REF_USE_PAD; ++ else ++ data |= SSUSB_CTRL_REF_USE_PAD; ++ ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* wait for ref clk to become stable, this can take up to 30ms */ ++ msleep(30); ++ ++ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* ++ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates ++ * in HS mode instead of SS mode. Workaround it by asserting ++ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode ++ */ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, 0x102D, &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data |= (1 << 7); ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x102D, data); ++ if (ret) ++ goto err_phy_trans; ++ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, 0x1010, &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data &= ~0xff0; ++ data |= 0x20; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x1010, data); ++ if (ret) ++ goto err_phy_trans; ++ ++ /* ++ * Fix RX Equalization setting as follows ++ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 ++ */ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, ++ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data &= ~RX_OVRD_IN_HI_RX_EQ_EN; ++ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; ++ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; ++ data |= phy_dwc3->rx_eq << RX_OVRD_IN_HI_RX_EQ_SHIFT; ++ data |= RX_OVRD_IN_HI_RX_EQ_OVRD; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, ++ SSPHY_CTRL_RX_OVRD_IN_HI(0), data); ++ if (ret) ++ goto err_phy_trans; ++ ++ /* ++ * Set EQ and TX launch amplitudes as follows ++ * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version ++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 ++ * LANE0.TX_OVRD_DRV_LO.EN set to 1. ++ */ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, ++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; ++ data |= phy_dwc3->tx_deamp_3_5db << TX_OVRD_DRV_LO_PREEMPH_SHIFT; ++ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; ++ data |= 0x6E; ++ data |= TX_OVRD_DRV_LO_EN; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, ++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); ++ if (ret) ++ goto err_phy_trans; ++ ++ qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x30, phy_dwc3->mpll); ++ ++ /* ++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows ++ * TX_FULL_SWING [26:20] amplitude to 110 ++ * TX_DEEMPH_6DB [19:14] to 32 ++ * TX_DEEMPH_3_5DB [13:8] set based on SoC version ++ * LOS_BIAS [7:3] to 9 ++ */ ++ data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); ++ ++ data &= ~PHY_PARAM_CTRL1_MASK; ++ ++ data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | ++ PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | ++ PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | ++ PHY_PARAM_CTRL1_LOS_BIAS(0x9); ++ ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, ++ PHY_PARAM_CTRL1_MASK, data); ++ ++err_phy_trans: ++ return ret; ++} ++ ++static int qcom_dwc3_ss_phy_exit(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ /* Sequence to put SSPHY in low power state: ++ * 1. Clear REF_PHY_EN in PHY_CTRL_REG ++ * 2. Clear REF_USE_PAD in PHY_CTRL_REG ++ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention ++ */ ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ SSUSB_CTRL_SS_PHY_EN, 0x0); ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ SSUSB_CTRL_REF_USE_PAD, 0x0); ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ SSUSB_CTRL_TEST_POWERDOWN, 0x0); ++ ++ clk_disable_unprepare(phy_dwc3->ref_clk); ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ ++ return 0; ++} ++ ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = { ++ .ops = { ++ .init = qcom_dwc3_hs_phy_init, ++ .exit = qcom_dwc3_hs_phy_exit, ++ .owner = THIS_MODULE, ++ }, ++ .clk_rate = 60000000, ++}; ++ ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = { ++ .ops = { ++ .init = qcom_dwc3_ss_phy_init, ++ .exit = qcom_dwc3_ss_phy_exit, ++ .owner = THIS_MODULE, ++ }, ++ .clk_rate = 125000000, ++}; ++ ++static const struct of_device_id qcom_dwc3_phy_table[] = { ++ { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata }, ++ { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table); ++ ++static int qcom_dwc3_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ struct resource *res; ++ const struct of_device_id *match; ++ const struct qcom_dwc3_phy_drvdata *data; ++ struct device_node *np; ++ ++ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); ++ if (!phy_dwc3) ++ return -ENOMEM; ++ ++ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node); ++ data = match->data; ++ ++ phy_dwc3->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res); ++ if (IS_ERR(phy_dwc3->base)) ++ return PTR_ERR(phy_dwc3->base); ++ ++ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); ++ if (IS_ERR(phy_dwc3->ref_clk)) { ++ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); ++ return PTR_ERR(phy_dwc3->ref_clk); ++ } ++ ++ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); ++ ++ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); ++ if (IS_ERR(phy_dwc3->xo_clk)) { ++ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); ++ phy_dwc3->xo_clk = NULL; ++ } ++ ++ /* Parse device node to probe HSIO settings */ ++ np = of_node_get(pdev->dev.of_node); ++ if (!of_compat_cmp(match->compatible, "qcom,dwc3-ss-usb-phy", ++ strlen(match->compatible))) { ++ ++ if (of_property_read_u32(np, "rx_eq", &phy_dwc3->rx_eq) || ++ of_property_read_u32(np, "tx_deamp_3_5db", ++ &phy_dwc3->tx_deamp_3_5db) || ++ of_property_read_u32(np, "mpll", &phy_dwc3->mpll)) { ++ ++ dev_err(phy_dwc3->dev, "cannot get HSIO settings from device node, using default values\n"); ++ ++ /* Default HSIO settings */ ++ phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; ++ phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; ++ phy_dwc3->mpll = SSPHY_MPLL_VALUE; ++ } ++ } ++ ++ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, ++ &data->ops); ++ ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); ++ ++ phy_set_drvdata(generic_phy, phy_dwc3); ++ platform_set_drvdata(pdev, phy_dwc3); ++ ++ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, ++ of_phy_simple_xlate); ++ ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_dwc3_phy_driver = { ++ .probe = qcom_dwc3_phy_probe, ++ .driver = { ++ .name = "qcom-dwc3-usb-phy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_phy_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_phy_driver); ++ ++MODULE_ALIAS("platform:phy-qcom-dwc3"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Andy Gross "); ++MODULE_AUTHOR("Ivan T. Ivanov "); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/target/linux/ipq806x/patches-4.14/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.14/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 000000000..a6c7953aa --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -0,0 +1,29 @@ +From 48051ece78136e4235a2415a52797db56f8a4478 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:09:07 -0700 +Subject: [PATCH 33/69] ARM: qcom: automatically select PCI_DOMAINS if PCI is + enabled + +If multiple PCIe devices are present in the system, the kernel will +panic at boot time when trying to scan the PCI buses. This happens on +IPQ806x based platforms, which has 3 PCIe ports. + +Enabling this option allows the kernel to assign the pci-domains +according to the device-tree content. This allows multiple PCIe +controllers to coexist in the system. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM + select ARM_AMBA + select PINCTRL + select QCOM_SCM if SMP ++ select PCI_DOMAINS if PCI + help + Support for Qualcomm's devicetree based systems. + diff --git a/target/linux/ipq806x/patches-4.14/0034-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.14/0034-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 000000000..019170b6d --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0034-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,147 @@ +From patchwork Fri Dec 8 09:42:19 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,01/12] ARM: Add Krait L2 register accessor functions +From: Sricharan R +X-Patchwork-Id: 10102101 +Message-Id: <1512726150-7204-2-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org, Mark Rutland , + Russell King , + Courtney Cavin +Date: Fri, 8 Dec 2017 15:12:19 +0530 + +From: Stephen Boyd + +Krait CPUs have a handful of L2 cache controller registers that +live behind a cp15 based indirection register. First you program +the indirection register (l2cpselr) to point the L2 'window' +register (l2cpdr) at what you want to read/write. Then you +read/write the 'window' register to do what you want. The +l2cpselr register is not banked per-cpu so we must lock around +accesses to it to prevent other CPUs from re-pointing l2cpdr +underneath us. + +Cc: Mark Rutland +Cc: Russell King +Cc: Courtney Cavin +Signed-off-by: Stephen Boyd +--- + arch/arm/common/Kconfig | 3 ++ + arch/arm/common/Makefile | 1 + + arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++ + arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++ + 4 files changed, 82 insertions(+) + create mode 100644 arch/arm/common/krait-l2-accessors.c + create mode 100644 arch/arm/include/asm/krait-l2-accessors.h + +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -9,6 +9,9 @@ config DMABOUNCE + bool + select ZONE_DMA + ++config KRAIT_L2_ACCESSORS ++ bool ++ + config SHARP_LOCOMO + bool + +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -7,6 +7,7 @@ obj-y += firmware.o + obj-$(CONFIG_ICST) += icst.o + obj-$(CONFIG_SA1111) += sa1111.o + obj-$(CONFIG_DMABOUNCE) += dmabounce.o ++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o + obj-$(CONFIG_SHARP_LOCOMO) += locomo.o + obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o +--- /dev/null ++++ b/arch/arm/common/krait-l2-accessors.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++static DEFINE_RAW_SPINLOCK(krait_l2_lock); ++ ++void krait_set_l2_indirect_reg(u32 addr, u32 val) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then write to the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); ++ isb(); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++} ++EXPORT_SYMBOL(krait_set_l2_indirect_reg); ++ ++u32 krait_get_l2_indirect_reg(u32 addr) ++{ ++ u32 val; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then read from the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++ ++ return val; ++} ++EXPORT_SYMBOL(krait_get_l2_indirect_reg); +--- /dev/null ++++ b/arch/arm/include/asm/krait-l2-accessors.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (c) 2011-2013, 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 __ASMARM_KRAIT_L2_ACCESSORS_H ++#define __ASMARM_KRAIT_L2_ACCESSORS_H ++ ++extern void krait_set_l2_indirect_reg(u32 addr, u32 val); ++extern u32 krait_get_l2_indirect_reg(u32 addr); ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.14/0035-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.14/0035-clk-mux-Split-out-register-accessors-for-reuse.patch new file mode 100644 index 000000000..5a64e6c56 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0035-clk-mux-Split-out-register-accessors-for-reuse.patch @@ -0,0 +1,195 @@ +From patchwork Fri Dec 8 09:42:20 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,02/12] clk: mux: Split out register accessors for reuse +From: Sricharan R +X-Patchwork-Id: 10102103 +Message-Id: <1512726150-7204-3-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:20 +0530 + +From: Stephen Boyd + +We want to reuse the logic in clk-mux.c for other clock drivers +that don't use readl as register accessors. Fortunately, there +really isn't much to the mux code besides the table indirection +and quirk flags if you assume any bit shifting and masking has +been done already. Pull that logic out into reusable functions +that operate on an optional table and some flags so that other +drivers can use the same logic. + +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-mux.c | 75 +++++++++++++++++++++++++++----------------- + include/linux/clk-provider.h | 9 ++++-- + 2 files changed, 54 insertions(+), 30 deletions(-) + +--- a/drivers/clk/clk-mux.c ++++ b/drivers/clk/clk-mux.c +@@ -26,35 +26,24 @@ + * parent - parent is adjustable through clk_set_parent + */ + +-static u8 clk_mux_get_parent(struct clk_hw *hw) ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, ++ unsigned int *table, unsigned long flags) + { +- struct clk_mux *mux = to_clk_mux(hw); + int num_parents = clk_hw_get_num_parents(hw); +- u32 val; +- +- /* +- * FIXME need a mux-specific flag to determine if val is bitwise or numeric +- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 +- * to 0x7 (index starts at one) +- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so +- * val = 0x4 really means "bit 2, index starts at bit 0" +- */ +- val = clk_readl(mux->reg) >> mux->shift; +- val &= mux->mask; + +- if (mux->table) { ++ if (table) { + int i; + + for (i = 0; i < num_parents; i++) +- if (mux->table[i] == val) ++ if (table[i] == val) + return i; + return -EINVAL; + } + +- if (val && (mux->flags & CLK_MUX_INDEX_BIT)) ++ if (val && (flags & CLK_MUX_INDEX_BIT)) + val = ffs(val) - 1; + +- if (val && (mux->flags & CLK_MUX_INDEX_ONE)) ++ if (val && (flags & CLK_MUX_INDEX_ONE)) + val--; + + if (val >= num_parents) +@@ -62,23 +51,53 @@ static u8 clk_mux_get_parent(struct clk_ + + return val; + } ++EXPORT_SYMBOL_GPL(clk_mux_get_parent); + +-static int clk_mux_set_parent(struct clk_hw *hw, u8 index) ++static u8 _clk_mux_get_parent(struct clk_hw *hw) + { + struct clk_mux *mux = to_clk_mux(hw); + u32 val; +- unsigned long flags = 0; + +- if (mux->table) { +- index = mux->table[index]; ++ /* ++ * FIXME need a mux-specific flag to determine if val is bitwise or ++ * numeric e.g. sys_clkin_ck's clksel field is 3 bits wide, ++ * but ranges from 0x1 to 0x7 (index starts at one) ++ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so ++ * val = 0x4 really means "bit 2, index starts at bit 0" ++ */ ++ val = clk_readl(mux->reg) >> mux->shift; ++ val &= mux->mask; ++ ++ return clk_mux_get_parent(hw, val, mux->table, mux->flags); ++} ++ ++unsigned int clk_mux_reindex(u8 index, unsigned int *table, ++ unsigned long flags) ++{ ++ unsigned int val = index; ++ ++ if (table) { ++ val = table[val]; + } else { +- if (mux->flags & CLK_MUX_INDEX_BIT) +- index = 1 << index; ++ if (flags & CLK_MUX_INDEX_BIT) ++ val = 1 << index; + +- if (mux->flags & CLK_MUX_INDEX_ONE) +- index++; ++ if (flags & CLK_MUX_INDEX_ONE) ++ val++; + } + ++ return val; ++} ++EXPORT_SYMBOL_GPL(clk_mux_reindex); ++ ++static int clk_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct clk_mux *mux = to_clk_mux(hw); ++ u32 val; ++ unsigned long flags = 0; ++ ++ index = clk_mux_reindex(index, mux->table, mux->flags); ++ + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + else +@@ -102,14 +121,14 @@ static int clk_mux_set_parent(struct clk + } + + const struct clk_ops clk_mux_ops = { +- .get_parent = clk_mux_get_parent, ++ .get_parent = _clk_mux_get_parent, + .set_parent = clk_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, + }; + EXPORT_SYMBOL_GPL(clk_mux_ops); + + const struct clk_ops clk_mux_ro_ops = { +- .get_parent = clk_mux_get_parent, ++ .get_parent = _clk_mux_get_parent, + }; + EXPORT_SYMBOL_GPL(clk_mux_ro_ops); + +@@ -117,7 +136,7 @@ struct clk_hw *clk_hw_register_mux_table + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, +- u8 clk_mux_flags, u32 *table, spinlock_t *lock) ++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock) + { + struct clk_mux *mux; + struct clk_hw *hw; +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -466,7 +466,7 @@ void clk_hw_unregister_divider(struct cl + struct clk_mux { + struct clk_hw hw; + void __iomem *reg; +- u32 *table; ++ unsigned int *table; + u32 mask; + u8 shift; + u8 flags; +@@ -484,6 +484,11 @@ struct clk_mux { + extern const struct clk_ops clk_mux_ops; + extern const struct clk_ops clk_mux_ro_ops; + ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, ++ unsigned int *table, unsigned long flags); ++unsigned int clk_mux_reindex(u8 index, unsigned int *table, ++ unsigned long flags); ++ + struct clk *clk_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, +@@ -504,7 +509,7 @@ struct clk_hw *clk_hw_register_mux_table + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, +- u8 clk_mux_flags, u32 *table, spinlock_t *lock); ++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock); + + void clk_unregister_mux(struct clk *clk); + void clk_hw_unregister_mux(struct clk_hw *hw); diff --git a/target/linux/ipq806x/patches-4.14/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.14/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 000000000..70926143e --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,352 @@ +From patchwork Fri Dec 8 09:42:21 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,03/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) +From: Sricharan R +X-Patchwork-Id: 10102083 +Message-Id: <1512726150-7204-4-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:21 +0530 + +From: Stephen Boyd + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ + 3 files changed, 308 insertions(+) + create mode 100644 drivers/clk/qcom/clk-hfpll.c + create mode 100644 drivers/clk/qcom/clk-hfpll.h + +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o + +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.c +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++#define PLL_OUTCTRL BIT(0) ++#define PLL_BYPASSNL BIT(1) ++#define PLL_RESET_N BIT(2) ++ ++/* Initialize a HFPLL at a given rate and enable it. */ ++static void __clk_hfpll_init_once(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ if (likely(h->init_done)) ++ return; ++ ++ /* Configure PLL parameters for integer mode. */ ++ if (hd->config_val) ++ regmap_write(regmap, hd->config_reg, hd->config_val); ++ regmap_write(regmap, hd->m_reg, 0); ++ regmap_write(regmap, hd->n_reg, 1); ++ ++ if (hd->user_reg) { ++ u32 regval = hd->user_val; ++ unsigned long rate; ++ ++ rate = clk_hw_get_rate(hw); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate) ++ regval |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, regval); ++ } ++ ++ if (hd->droop_reg) ++ regmap_write(regmap, hd->droop_reg, hd->droop_val); ++ ++ h->init_done = true; ++} ++ ++static void __clk_hfpll_enable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 val; ++ ++ __clk_hfpll_init_once(hw); ++ ++ /* Disable PLL bypass mode. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); ++ ++ /* ++ * H/W requires a 5us delay between disabling the bypass and ++ * de-asserting the reset. Delay 10us just to be safe. ++ */ ++ usleep_range(10, 100); ++ ++ /* De-assert active-low PLL reset. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); ++ ++ /* Wait for PLL to lock. */ ++ if (hd->status_reg) { ++ do { ++ regmap_read(regmap, hd->status_reg, &val); ++ } while (!(val & BIT(hd->lock_bit))); ++ } else { ++ usleep_range(60, 100); ++ } ++ ++ /* Enable PLL output. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); ++} ++ ++/* Enable an already-configured HFPLL. */ ++static int clk_hfpll_enable(struct clk_hw *hw) ++{ ++ unsigned long flags; ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) ++ __clk_hfpll_enable(hw); ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static void __clk_hfpll_disable(struct clk_hfpll *h) ++{ ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ /* ++ * Disable the PLL output, disable test mode, enable the bypass mode, ++ * and assert the reset. ++ */ ++ regmap_update_bits(regmap, hd->mode_reg, ++ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); ++} ++ ++static void clk_hfpll_disable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ __clk_hfpll_disable(h); ++ spin_unlock_irqrestore(&h->lock, flags); ++} ++ ++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ unsigned long rrate; ++ ++ rate = clamp(rate, hd->min_rate, hd->max_rate); ++ ++ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; ++ if (rrate > hd->max_rate) ++ rrate -= *parent_rate; ++ ++ return rrate; ++} ++ ++/* ++ * For optimization reasons, assumes no downstream clocks are actively using ++ * it. ++ */ ++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ unsigned long flags; ++ u32 l_val, val; ++ bool enabled; ++ ++ l_val = rate / parent_rate; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ ++ enabled = __clk_is_enabled(hw->clk); ++ if (enabled) ++ __clk_hfpll_disable(h); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_reg && hd->user_vco_mask) { ++ regmap_read(regmap, hd->user_reg, &val); ++ if (rate <= hd->low_vco_max_rate) ++ val &= ~hd->user_vco_mask; ++ else ++ val |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, val); ++ } ++ ++ regmap_write(regmap, hd->l_reg, l_val); ++ ++ if (enabled) ++ __clk_hfpll_enable(hw); ++ ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 l_val; ++ ++ regmap_read(regmap, hd->l_reg, &l_val); ++ ++ return l_val * parent_rate; ++} ++ ++static void clk_hfpll_init(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode, status; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { ++ __clk_hfpll_init_once(hw); ++ return; ++ } ++ ++ if (hd->status_reg) { ++ regmap_read(regmap, hd->status_reg, &status); ++ if (!(status & BIT(hd->lock_bit))) { ++ WARN(1, "HFPLL %s is ON, but not locked!\n", ++ __clk_get_name(hw->clk)); ++ clk_hfpll_disable(hw); ++ __clk_hfpll_init_once(hw); ++ } ++ } ++} ++ ++static int hfpll_is_enabled(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ mode &= 0x7; ++ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); ++} ++ ++const struct clk_ops clk_ops_hfpll = { ++ .enable = clk_hfpll_enable, ++ .disable = clk_hfpll_disable, ++ .is_enabled = hfpll_is_enabled, ++ .round_rate = clk_hfpll_round_rate, ++ .set_rate = clk_hfpll_set_rate, ++ .recalc_rate = clk_hfpll_recalc_rate, ++ .init = clk_hfpll_init, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_hfpll); +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __QCOM_CLK_HFPLL_H__ ++#define __QCOM_CLK_HFPLL_H__ ++ ++#include ++#include ++#include "clk-regmap.h" ++ ++struct hfpll_data { ++ u32 mode_reg; ++ u32 l_reg; ++ u32 m_reg; ++ u32 n_reg; ++ u32 user_reg; ++ u32 droop_reg; ++ u32 config_reg; ++ u32 status_reg; ++ u8 lock_bit; ++ ++ u32 droop_val; ++ u32 config_val; ++ u32 user_val; ++ u32 user_vco_mask; ++ unsigned long low_vco_max_rate; ++ ++ unsigned long min_rate; ++ unsigned long max_rate; ++}; ++ ++struct clk_hfpll { ++ struct hfpll_data const *d; ++ int init_done; ++ ++ struct clk_regmap clkr; ++ spinlock_t lock; ++}; ++ ++#define to_clk_hfpll(_hw) \ ++ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) ++ ++extern const struct clk_ops clk_ops_hfpll; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.14/0039-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.14/0039-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 000000000..d9ad391d0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0039-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,206 @@ +From patchwork Fri Dec 8 09:42:22 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,04/12] clk: qcom: Add HFPLL driver +From: Sricharan R +X-Patchwork-Id: 10102079 +Message-Id: <1512726150-7204-5-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:22 +0530 + +From: Stephen Boyd + +On some devices (MSM8974 for example), the HFPLLs are +instantiated within the Krait processor subsystem as separate +register regions. Add a driver for these PLLs so that we can +provide HFPLL clocks for use by the system. + +Cc: +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 106 +++++++++++++++++++++ + 4 files changed, 155 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt + create mode 100644 drivers/clk/qcom/hfpll.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt +@@ -0,0 +1,40 @@ ++High-Frequency PLL (HFPLL) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be "qcom,hfpll" ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: address and size of HPLL registers. An optional second ++ element specifies the address and size of the alias ++ register region. ++ ++- clock-output-names: ++ Usage: required ++ Value type: ++ Definition: Name of the PLL. Typically hfpllX where X is a CPU number ++ starting at 0. Otherwise hfpll_Y where Y is more specific ++ such as "l2". ++ ++Example: ++ ++1) An HFPLL for the L2 cache. ++ ++ clock-controller@f9016000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf9016000 0x30>; ++ clock-output-names = "hfpll_l2"; ++ }; ++ ++2) An HFPLL for CPU0. This HFPLL has the alias register region. ++ ++ clock-controller@f908a000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf908a000 0x30>, <0xf900a000 0x30>; ++ clock-output-names = "hfpll0"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -179,3 +179,11 @@ config MSM_MMCC_8996 + Support for the multimedia clock controller on msm8996 devices. + Say Y if you want to support multimedia devices such as display, + graphics, video encode/decode, camera, etc. ++ ++config QCOM_HFPLL ++ tristate "High-Frequency PLL (HFPLL) Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the high-frequency PLLs present on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling on devices ++ such as MSM8974, APQ8084, etc. +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -32,3 +32,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 + obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++static const struct hfpll_data hdata = { ++ .mode_reg = 0x00, ++ .l_reg = 0x04, ++ .m_reg = 0x08, ++ .n_reg = 0x0c, ++ .user_reg = 0x10, ++ .config_reg = 0x14, ++ .config_val = 0x430405d, ++ .status_reg = 0x1c, ++ .lock_bit = 16, ++ ++ .user_val = 0x8, ++ .user_vco_mask = 0x100000, ++ .low_vco_max_rate = 1248000000, ++ .min_rate = 537600000UL, ++ .max_rate = 2900000000UL, ++}; ++ ++static const struct of_device_id qcom_hfpll_match_table[] = { ++ { .compatible = "qcom,hfpll" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); ++ ++static const struct regmap_config hfpll_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x30, ++ .fast_io = true, ++}; ++ ++static int qcom_hfpll_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ void __iomem *base; ++ struct regmap *regmap; ++ struct clk_hfpll *h; ++ struct clk_init_data init = { ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_ops_hfpll, ++ }; ++ ++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); ++ if (!h) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_string_index(dev->of_node, "clock-output-names", ++ 0, &init.name)) ++ return -ENODEV; ++ ++ h->d = &hdata; ++ h->clkr.hw.init = &init; ++ spin_lock_init(&h->lock); ++ ++ return devm_clk_register_regmap(&pdev->dev, &h->clkr); ++} ++ ++static struct platform_driver qcom_hfpll_driver = { ++ .probe = qcom_hfpll_probe, ++ .driver = { ++ .name = "qcom-hfpll", ++ .of_match_table = qcom_hfpll_match_table, ++ }, ++}; ++module_platform_driver(qcom_hfpll_driver); ++ ++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-hfpll"); diff --git a/target/linux/ipq806x/patches-4.14/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.14/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch new file mode 100644 index 000000000..c3af0fa55 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch @@ -0,0 +1,129 @@ +From patchwork Fri Dec 8 09:42:24 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,06/12] clk: qcom: Add IPQ806X's HFPLLs +From: Sricharan R +X-Patchwork-Id: 10102047 +Message-Id: <1512726150-7204-7-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:24 +0530 + +From: Stephen Boyd + +Describe the HFPLLs present on IPQ806X devices. + +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/gcc-ipq806x.c | 82 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 82 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -30,6 +30,7 @@ + #include "clk-pll.h" + #include "clk-rcg.h" + #include "clk-branch.h" ++#include "clk-hfpll.h" + #include "reset.h" + + static struct clk_pll pll0 = { +@@ -113,6 +114,84 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 0x3240, ++ .l_reg = 0x3248, ++ .m_reg = 0x324c, ++ .n_reg = 0x3250, ++ .config_reg = 0x3244, ++ .status_reg = 0x325c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll1 = { ++ .d = &hfpll1_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll1", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), ++}; ++ ++static struct hfpll_data hfpll_l2_data = { ++ .mode_reg = 0x3300, ++ .l_reg = 0x3308, ++ .m_reg = 0x330c, ++ .n_reg = 0x3310, ++ .config_reg = 0x3304, ++ .status_reg = 0x331c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll_l2 = { ++ .d = &hfpll_l2_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll_l2", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), ++}; ++ + static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, +@@ -2801,6 +2880,9 @@ static struct clk_regmap *gcc_ipq806x_cl + [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, + [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, + [NSSTCM_CLK] = &nss_tcm_clk.clkr, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-4.14/0041-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.14/0041-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 000000000..a6d0f0d7a --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0041-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,241 @@ +From patchwork Fri Dec 8 09:42:25 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,07/12] clk: qcom: Add support for Krait clocks +From: Sricharan R +X-Patchwork-Id: 10102051 +Message-Id: <1512726150-7204-8-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:25 +0530 + +From: Stephen Boyd + +The Krait clocks are made up of a series of muxes and a divider +that choose between a fixed rate clock and dedicated HFPLLs for +each CPU. Instead of using mmio accesses to remux parents, the +Krait implementation exposes the remux control via cp15 +registers. Support these clocks. + +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/Kconfig | 4 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-krait.c | 134 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 48 ++++++++++++++++ + 4 files changed, 187 insertions(+) + create mode 100644 drivers/clk/qcom/clk-krait.c + create mode 100644 drivers/clk/qcom/clk-krait.h + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -187,3 +187,7 @@ config QCOM_HFPLL + Support for the high-frequency PLLs present on Qualcomm devices. + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. ++ ++config KRAIT_CLOCKS ++ bool ++ select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.c +@@ -0,0 +1,134 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "clk-krait.h" ++ ++/* Secondary and primary muxes share the same cp15 register */ ++static DEFINE_SPINLOCK(krait_clock_reg_lock); ++ ++#define LPL_SHIFT 8 ++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) ++{ ++ unsigned long flags; ++ u32 regval; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(mux->offset); ++ regval &= ~(mux->mask << mux->shift); ++ regval |= (sel & mux->mask) << mux->shift; ++ if (mux->lpl) { ++ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); ++ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); ++ } ++ krait_set_l2_indirect_reg(mux->offset, regval); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ /* Wait for switch to complete. */ ++ mb(); ++ udelay(1); ++} ++ ++static int krait_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = clk_mux_reindex(index, mux->parent_map, 0); ++ mux->en_mask = sel; ++ /* Don't touch mux if CPU is off as it won't work */ ++ if (__clk_is_enabled(hw->clk)) ++ __krait_mux_set_sel(mux, sel); ++ ++ return 0; ++} ++ ++static u8 krait_mux_get_parent(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = krait_get_l2_indirect_reg(mux->offset); ++ sel >>= mux->shift; ++ sel &= mux->mask; ++ mux->en_mask = sel; ++ ++ return clk_mux_get_parent(hw, sel, mux->parent_map, 0); ++} ++ ++const struct clk_ops krait_mux_clk_ops = { ++ .set_parent = krait_mux_set_parent, ++ .get_parent = krait_mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate_closest, ++}; ++EXPORT_SYMBOL_GPL(krait_mux_clk_ops); ++ ++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ ++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); ++ return DIV_ROUND_UP(*parent_rate, 2); ++} ++ ++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ unsigned long flags; ++ u32 val; ++ u32 mask = BIT(d->width) - 1; ++ ++ if (d->lpl) ++ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ val = krait_get_l2_indirect_reg(d->offset); ++ val &= ~mask; ++ krait_set_l2_indirect_reg(d->offset, val); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long ++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ u32 mask = BIT(d->width) - 1; ++ u32 div; ++ ++ div = krait_get_l2_indirect_reg(d->offset); ++ div >>= d->shift; ++ div &= mask; ++ div = (div + 1) * 2; ++ ++ return DIV_ROUND_UP(parent_rate, div); ++} ++ ++const struct clk_ops krait_div2_clk_ops = { ++ .round_rate = krait_div2_round_rate, ++ .set_rate = krait_div2_set_rate, ++ .recalc_rate = krait_div2_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(krait_div2_clk_ops); +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (c) 2013, 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 __QCOM_CLK_KRAIT_H ++#define __QCOM_CLK_KRAIT_H ++ ++#include ++ ++struct krait_mux_clk { ++ unsigned int *parent_map; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ bool lpl; ++ ++ struct clk_hw hw; ++ struct notifier_block clk_nb; ++}; ++ ++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) ++ ++extern const struct clk_ops krait_mux_clk_ops; ++ ++struct krait_div2_clk { ++ u32 offset; ++ u8 width; ++ u32 shift; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) ++ ++extern const struct clk_ops krait_div2_clk_ops; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.14/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.14/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 000000000..5a76fbc8b --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,209 @@ +From patchwork Fri Dec 8 09:42:26 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,08/12] clk: qcom: Add KPSS ACC/GCC driver +From: Sricharan R +X-Patchwork-Id: 10102023 +Message-Id: <1512726150-7204-9-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:26 +0530 + +From: Stephen Boyd + +The ACC and GCC regions present in KPSSv1 contain registers to +control clocks and power to each Krait CPU and L2. For CPUfreq +purposes probe these devices and expose a mux clock that chooses +between PXO and PLL8. + +Cc: +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++ + .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/kpss-xcc.c | 96 ++++++++++++++++++++++ + 5 files changed, 140 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt + create mode 100644 drivers/clk/qcom/kpss-xcc.c + +--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +@@ -21,10 +21,17 @@ PROPERTIES + the register region. An optional second element specifies + the base address and size of the alias register region. + ++- clock-output-names: ++ Usage: optional ++ Value type: ++ Definition: Name of the output clock. Typically acpuX_aux where X is a ++ CPU number starting at 0. ++ + Example: + + clock-controller@2088000 { + compatible = "qcom,kpss-acc-v2"; + reg = <0x02088000 0x1000>, + <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt +@@ -0,0 +1,28 @@ ++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: should be one of: ++ "qcom,kpss-gcc" ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: base address and size of the register region ++ ++- clock-output-names: ++ Usage: required ++ Value type: ++ Definition: Name of the output clock. Typically acpu_l2_aux indicating ++ an L2 cache auxiliary clock. ++ ++Example: ++ ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -188,6 +188,14 @@ config QCOM_HFPLL + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. + ++config KPSS_XCC ++ tristate "KPSS Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the Krait ACC and GCC clock controllers. Say Y ++ if you want to support CPU frequency scaling on devices such ++ as MSM8960, APQ8064, etc. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -33,4 +33,5 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 + obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/kpss-xcc.c +@@ -0,0 +1,96 @@ ++/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static const char *aux_parents[] = { ++ "pll8_vote", ++ "pxo", ++}; ++ ++static unsigned int aux_parent_map[] = { ++ 3, ++ 0, ++}; ++ ++static const struct of_device_id kpss_xcc_match_table[] = { ++ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, ++ { .compatible = "qcom,kpss-gcc" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); ++ ++static int kpss_xcc_driver_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct clk *clk; ++ struct resource *res; ++ void __iomem *base; ++ const char *name; ++ ++ id = of_match_device(kpss_xcc_match_table, &pdev->dev); ++ if (!id) ++ return -ENODEV; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (id->data) { ++ if (of_property_read_string_index(pdev->dev.of_node, ++ "clock-output-names", ++ 0, &name)) ++ return -ENODEV; ++ base += 0x14; ++ } else { ++ name = "acpu_l2_aux"; ++ base += 0x28; ++ } ++ ++ clk = clk_register_mux_table(&pdev->dev, name, aux_parents, ++ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, ++ 0, aux_parent_map, NULL); ++ ++ platform_set_drvdata(pdev, clk); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int kpss_xcc_driver_remove(struct platform_device *pdev) ++{ ++ clk_unregister_mux(platform_get_drvdata(pdev)); ++ return 0; ++} ++ ++static struct platform_driver kpss_xcc_driver = { ++ .probe = kpss_xcc_driver_probe, ++ .remove = kpss_xcc_driver_remove, ++ .driver = { ++ .name = "kpss-xcc", ++ .of_match_table = kpss_xcc_match_table, ++ }, ++}; ++module_platform_driver(kpss_xcc_driver); ++ ++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:kpss-xcc"); diff --git a/target/linux/ipq806x/patches-4.14/0043-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.14/0043-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 000000000..1d32b2b05 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0043-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,436 @@ +From patchwork Fri Dec 8 09:42:27 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,09/12] clk: qcom: Add Krait clock controller driver +From: Sricharan R +X-Patchwork-Id: 10102061 +Message-Id: <1512726150-7204-10-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:27 +0530 + +From: Stephen Boyd + +The Krait CPU clocks are made up of a primary mux and secondary +mux for each CPU and the L2, controlled via cp15 accessors. For +Kraits within KPSSv1 each secondary mux accepts a different aux +source, but on KPSSv2 each secondary mux accepts the same aux +source. + +Cc: +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 350 +++++++++++++++++++++ + 4 files changed, 381 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt + create mode 100644 drivers/clk/qcom/krait-cc.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt +@@ -0,0 +1,22 @@ ++Krait Clock Controller ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be one of: ++ "qcom,krait-cc-v1" ++ "qcom,krait-cc-v2" ++ ++- #clock-cells: ++ Usage: required ++ Value type: ++ Definition: must be 1 ++ ++Example: ++ ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -196,6 +196,14 @@ config KPSS_XCC + if you want to support CPU frequency scaling on devices such + as MSM8960, APQ8064, etc. + ++config KRAITCC ++ tristate "Krait Clock Controller" ++ depends on COMMON_CLK_QCOM && ARM ++ select KRAIT_CLOCKS ++ help ++ Support for the Krait CPU clocks on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -35,3 +35,4 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o + obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ++obj-$(CONFIG_KRAITCC) += krait-cc.o +--- /dev/null ++++ b/drivers/clk/qcom/krait-cc.c +@@ -0,0 +1,350 @@ ++/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-krait.h" ++ ++static unsigned int sec_mux_map[] = { ++ 2, ++ 0, ++}; ++ ++static unsigned int pri_mux_map[] = { ++ 1, ++ 2, ++ 0, ++}; ++ ++static int ++krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) ++{ ++ struct krait_div2_clk *div; ++ struct clk_init_data init = { ++ .num_parents = 1, ++ .ops = &krait_div2_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ const char *p_names[1]; ++ struct clk *clk; ++ ++ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); ++ if (!div) ++ return -ENOMEM; ++ ++ div->width = 2; ++ div->shift = 6; ++ div->lpl = id >= 0; ++ div->offset = offset; ++ div->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ init.parent_names = p_names; ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ kfree(init.name); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_register(dev, &div->hw); ++ kfree(p_names[0]); ++ kfree(init.name); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int ++krait_add_sec_mux(struct device *dev, int id, const char *s, ++ unsigned int offset, bool unique_aux) ++{ ++ struct krait_mux_clk *mux; ++ static const char *sec_mux_list[] = { ++ "acpu_aux", ++ "qsb", ++ }; ++ struct clk_init_data init = { ++ .parent_names = sec_mux_list, ++ .num_parents = ARRAY_SIZE(sec_mux_list), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return -ENOMEM; ++ ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->mask = 0x3; ++ mux->shift = 2; ++ mux->parent_map = sec_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ if (unique_aux) { ++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); ++ if (!sec_mux_list[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_aux; ++ } ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ if (unique_aux) ++ kfree(sec_mux_list[0]); ++err_aux: ++ kfree(init.name); ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct clk * ++krait_add_pri_mux(struct device *dev, int id, const char *s, ++ unsigned int offset) ++{ ++ struct krait_mux_clk *mux; ++ const char *p_names[3]; ++ struct clk_init_data init = { ++ .parent_names = p_names, ++ .num_parents = ARRAY_SIZE(p_names), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return ERR_PTR(-ENOMEM); ++ ++ mux->mask = 0x3; ++ mux->shift = 0; ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->parent_map = pri_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); ++ if (!init.name) ++ return ERR_PTR(-ENOMEM); ++ ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p0; ++ } ++ ++ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!p_names[1]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p1; ++ } ++ ++ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!p_names[2]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p2; ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ kfree(p_names[2]); ++err_p2: ++ kfree(p_names[1]); ++err_p1: ++ kfree(p_names[0]); ++err_p0: ++ kfree(init.name); ++ return clk; ++} ++ ++/* id < 0 for L2, otherwise id == physical CPU number */ ++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) ++{ ++ int ret; ++ unsigned int offset; ++ void *p = NULL; ++ const char *s; ++ struct clk *clk; ++ ++ if (id >= 0) { ++ offset = 0x4501 + (0x1000 * id); ++ s = p = kasprintf(GFP_KERNEL, "%d", id); ++ if (!s) ++ return ERR_PTR(-ENOMEM); ++ } else { ++ offset = 0x500; ++ s = "_l2"; ++ } ++ ++ ret = krait_add_div(dev, id, s, offset); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ clk = krait_add_pri_mux(dev, id, s, offset); ++err: ++ kfree(p); ++ return clk; ++} ++ ++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) ++{ ++ unsigned int idx = clkspec->args[0]; ++ struct clk **clks = data; ++ ++ if (idx >= 5) { ++ pr_err("%s: invalid clock index %d\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return clks[idx] ? : ERR_PTR(-ENODEV); ++} ++ ++static const struct of_device_id krait_cc_match_table[] = { ++ { .compatible = "qcom,krait-cc-v1", (void *)1UL }, ++ { .compatible = "qcom,krait-cc-v2" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, krait_cc_match_table); ++ ++static int krait_cc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *id; ++ unsigned long cur_rate, aux_rate; ++ int cpu; ++ struct clk *clk; ++ struct clk **clks; ++ struct clk *l2_pri_mux_clk; ++ ++ id = of_match_device(krait_cc_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ ++ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ ++ clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ if (!id->data) { ++ clk = clk_register_fixed_factor(dev, "acpu_aux", ++ "gpll0_vote", 0, 1, 2); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ ++ /* Krait configurations have at most 4 CPUs and one L2 */ ++ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); ++ if (!clks) ++ return -ENOMEM; ++ ++ for_each_possible_cpu(cpu) { ++ clk = krait_add_clks(dev, cpu, id->data); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[cpu] = clk; ++ } ++ ++ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); ++ if (IS_ERR(l2_pri_mux_clk)) ++ return PTR_ERR(l2_pri_mux_clk); ++ clks[4] = l2_pri_mux_clk; ++ ++ /* ++ * We don't want the CPU or L2 clocks to be turned off at late init ++ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the ++ * refcount of these clocks. Any cpufreq/hotplug manager can assume ++ * that the clocks have already been prepared and enabled by the time ++ * they take over. ++ */ ++ for_each_online_cpu(cpu) { ++ clk_prepare_enable(l2_pri_mux_clk); ++ WARN(clk_prepare_enable(clks[cpu]), ++ "Unable to turn on CPU%d clock", cpu); ++ } ++ ++ /* ++ * Force reinit of HFPLLs and muxes to overwrite any potential ++ * incorrect configuration of HFPLLs and muxes by the bootloader. ++ * While at it, also make sure the cores are running at known rates ++ * and print the current rate. ++ * ++ * The clocks are set to aux clock rate first to make sure the ++ * secondary mux is not sourcing off of QSB. The rate is then set to ++ * two different rates to force a HFPLL reinit under all ++ * circumstances. ++ */ ++ cur_rate = clk_get_rate(l2_pri_mux_clk); ++ aux_rate = 384000000; ++ if (cur_rate == 1) { ++ pr_info("L2 @ QSB rate. Forcing new rate.\n"); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(l2_pri_mux_clk, aux_rate); ++ clk_set_rate(l2_pri_mux_clk, 2); ++ clk_set_rate(l2_pri_mux_clk, cur_rate); ++ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); ++ for_each_possible_cpu(cpu) { ++ clk = clks[cpu]; ++ cur_rate = clk_get_rate(clk); ++ if (cur_rate == 1) { ++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); ++ cur_rate = aux_rate; ++ } ++ ++ clk_set_rate(clk, aux_rate); ++ clk_set_rate(clk, 2); ++ clk_set_rate(clk, cur_rate); ++ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); ++ } ++ ++ of_clk_add_provider(dev->of_node, krait_of_get, clks); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cc_driver = { ++ .probe = krait_cc_probe, ++ .driver = { ++ .name = "krait-cc", ++ .of_match_table = krait_cc_match_table, ++ }, ++}; ++module_platform_driver(krait_cc_driver); ++ ++MODULE_DESCRIPTION("Krait CPU Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:krait-cc"); diff --git a/target/linux/ipq806x/patches-4.14/0044-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.14/0044-clk-Add-safe-switch-hook.patch new file mode 100644 index 000000000..d0eddc64c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0044-clk-Add-safe-switch-hook.patch @@ -0,0 +1,160 @@ +From patchwork Fri Dec 8 09:42:28 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,10/12] clk: qcom: Add safe switch hook for krait mux clocks +From: Sricharan R +X-Patchwork-Id: 10102057 +Message-Id: <1512726150-7204-11-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:28 +0530 + +When the Hfplls are reprogrammed during the rate change, +the primary muxes which are sourced from the same hfpll +for higher frequencies, needs to be switched to the 'safe +secondary mux' as the parent for that small window. This +is done by registering a clk notifier for the muxes and +switching to the safe parent in the PRE_RATE_CHANGE notifier +and back to the original parent in the POST_RATE_CHANGE notifier. + +Signed-off-by: Sricharan R +--- + drivers/clk/qcom/clk-krait.c | 2 ++ + drivers/clk/qcom/clk-krait.h | 3 +++ + drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 61 insertions(+) + +--- a/drivers/clk/qcom/clk-krait.c ++++ b/drivers/clk/qcom/clk-krait.c +@@ -60,6 +60,8 @@ static int krait_mux_set_parent(struct c + if (__clk_is_enabled(hw->clk)) + __krait_mux_set_sel(mux, sel); + ++ mux->reparent = true; ++ + return 0; + } + +--- a/drivers/clk/qcom/clk-krait.h ++++ b/drivers/clk/qcom/clk-krait.h +@@ -23,6 +23,9 @@ struct krait_mux_clk { + u32 shift; + u32 en_mask; + bool lpl; ++ u8 safe_sel; ++ u8 old_index; ++ bool reparent; + + struct clk_hw hw; + struct notifier_block clk_nb; +--- a/drivers/clk/qcom/krait-cc.c ++++ b/drivers/clk/qcom/krait-cc.c +@@ -35,6 +35,49 @@ static unsigned int pri_mux_map[] = { + 0, + }; + ++/* ++ * Notifier function for switching the muxes to safe parent ++ * while the hfpll is getting reprogrammed. ++ */ ++static int krait_notifier_cb(struct notifier_block *nb, ++ unsigned long event, ++ void *data) ++{ ++ int ret = 0; ++ struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk, ++ clk_nb); ++ /* Switch to safe parent */ ++ if (event == PRE_RATE_CHANGE) { ++ mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw); ++ ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel); ++ mux->reparent = false; ++ /* ++ * By the time POST_RATE_CHANGE notifier is called, ++ * clk framework itself would have changed the parent for the new rate. ++ * Only otherwise, put back to the old parent. ++ */ ++ } else if (event == POST_RATE_CHANGE) { ++ if (!mux->reparent) ++ ret = krait_mux_clk_ops.set_parent(&mux->hw, ++ mux->old_index); ++ } ++ ++ return notifier_from_errno(ret); ++} ++ ++static int krait_notifier_register(struct device *dev, struct clk *clk, ++ struct krait_mux_clk *mux) ++{ ++ int ret = 0; ++ ++ mux->clk_nb.notifier_call = krait_notifier_cb; ++ ret = clk_notifier_register(clk, &mux->clk_nb); ++ if (ret) ++ dev_err(dev, "failed to register clock notifier: %d\n", ret); ++ ++ return ret; ++} ++ + static int + krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) + { +@@ -79,6 +122,7 @@ static int + krait_add_sec_mux(struct device *dev, int id, const char *s, + unsigned int offset, bool unique_aux) + { ++ int ret; + struct krait_mux_clk *mux; + static const char *sec_mux_list[] = { + "acpu_aux", +@@ -102,6 +146,7 @@ krait_add_sec_mux(struct device *dev, in + mux->shift = 2; + mux->parent_map = sec_mux_map; + mux->hw.init = &init; ++ mux->safe_sel = 0; + + init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); + if (!init.name) +@@ -117,6 +162,11 @@ krait_add_sec_mux(struct device *dev, in + + clk = devm_clk_register(dev, &mux->hw); + ++ ret = krait_notifier_register(dev, clk, mux); ++ if (ret) ++ goto unique_aux; ++ ++unique_aux: + if (unique_aux) + kfree(sec_mux_list[0]); + err_aux: +@@ -128,6 +178,7 @@ static struct clk * + krait_add_pri_mux(struct device *dev, int id, const char *s, + unsigned int offset) + { ++ int ret; + struct krait_mux_clk *mux; + const char *p_names[3]; + struct clk_init_data init = { +@@ -148,6 +199,7 @@ krait_add_pri_mux(struct device *dev, in + mux->lpl = id >= 0; + mux->parent_map = pri_mux_map; + mux->hw.init = &init; ++ mux->safe_sel = 2; + + init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); + if (!init.name) +@@ -173,6 +225,10 @@ krait_add_pri_mux(struct device *dev, in + + clk = devm_clk_register(dev, &mux->hw); + ++ ret = krait_notifier_register(dev, clk, mux); ++ if (ret) ++ goto err_p3; ++err_p3: + kfree(p_names[2]); + err_p2: + kfree(p_names[1]); diff --git a/target/linux/ipq806x/patches-4.14/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.14/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch new file mode 100644 index 000000000..820d13f3c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch @@ -0,0 +1,307 @@ +From patchwork Fri Dec 8 09:42:29 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,11/12] cpufreq: Add module to register cpufreq on Krait CPUs +From: Sricharan R +X-Patchwork-Id: 10102075 +Message-Id: <1512726150-7204-12-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:29 +0530 + +From: Stephen Boyd + +Register a cpufreq-generic device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Cc: +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++ + drivers/cpufreq/Kconfig.arm | 9 + + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++ + 4 files changed, 252 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt + create mode 100644 drivers/cpufreq/qcom-cpufreq.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt +@@ -0,0 +1,38 @@ ++Qualcomm Process Voltage Scaling Tables ++ ++The node name is required to be "qcom,pvs". There shall only be one ++such node present in the root of the tree. ++ ++PROPERTIES ++ ++- qcom,pvs-format-a or qcom,pvs-format-b: ++ Usage: required ++ Value type: ++ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties. ++ If qcom,pvs-format-a is used the table is two columns ++ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage, ++ and current in that order). ++ ++- qcom,speedX-pvsY-bin-vZ: ++ Usage: required ++ Value type: ++ Definition: The PVS table corresponding to the speed bin X, pvs bin Y, ++ and version Z. ++Example: ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 384000000 950000 >, ++ < 486000000 975000 >, ++ < 594000000 1000000 >, ++ < 702000000 1025000 >, ++ < 810000000 1075000 >, ++ < 918000000 1100000 >, ++ < 1026000000 1125000 >, ++ < 1134000000 1175000 >, ++ < 1242000000 1200000 >, ++ < 1350000000 1225000 >, ++ < 1458000000 1237500 >, ++ < 1512000000 1250000 >; ++ }; +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -88,6 +88,15 @@ config ARM_OMAP2PLUS_CPUFREQ + depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS + ++config ARM_QCOM_CPUFREQ ++ tristate "Qualcomm based" ++ depends on ARCH_QCOM ++ select PM_OPP ++ help ++ This adds the CPUFreq driver for Qualcomm SoC based boards. ++ ++ If in doubt, say N. ++ + config ARM_S3C_CPUFREQ + bool + help +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt81 + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o + obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o + obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -0,0 +1,204 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cpufreq-dt.h" ++ ++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver) ++{ ++ void __iomem *base; ++ u32 pte_efuse; ++ ++ *speed = *pvs = *pvs_ver = 0; ++ ++ base = ioremap(0x007000c0, 4); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ iounmap(base); ++ ++ *speed = pte_efuse & 0xf; ++ if (*speed == 0xf) ++ *speed = (pte_efuse >> 4) & 0xf; ++ ++ if (*speed == 0xf) { ++ *speed = 0; ++ pr_warn("Speed bin: Defaulting to %d\n", *speed); ++ } else { ++ pr_info("Speed bin: %d\n", *speed); ++ } ++ ++ *pvs = (pte_efuse >> 10) & 0x7; ++ if (*pvs == 0x7) ++ *pvs = (pte_efuse >> 13) & 0x7; ++ ++ if (*pvs == 0x7) { ++ *pvs = 0; ++ pr_warn("PVS bin: Defaulting to %d\n", *pvs); ++ } else { ++ pr_info("PVS bin: %d\n", *pvs); ++ } ++} ++ ++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver) ++{ ++ u32 pte_efuse, redundant_sel; ++ void __iomem *base; ++ ++ *speed = 0; ++ *pvs = 0; ++ *pvs_ver = 0; ++ ++ base = ioremap(0xfc4b80b0, 8); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ redundant_sel = (pte_efuse >> 24) & 0x7; ++ *speed = pte_efuse & 0x7; ++ /* 4 bits of PVS are in efuse register bits 31, 8-6. */ ++ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); ++ *pvs_ver = (pte_efuse >> 4) & 0x3; ++ ++ switch (redundant_sel) { ++ case 1: ++ *speed = (pte_efuse >> 27) & 0xf; ++ break; ++ case 2: ++ *pvs = (pte_efuse >> 27) & 0xf; ++ break; ++ } ++ ++ /* Check SPEED_BIN_BLOW_STATUS */ ++ if (pte_efuse & BIT(3)) { ++ pr_info("Speed bin: %d\n", *speed); ++ } else { ++ pr_warn("Speed bin not set. Defaulting to 0!\n"); ++ *speed = 0; ++ } ++ ++ /* Check PVS_BLOW_STATUS */ ++ pte_efuse = readl_relaxed(base + 0x4) & BIT(21); ++ if (pte_efuse) { ++ pr_info("PVS bin: %d\n", *pvs); ++ } else { ++ pr_warn("PVS bin not set. Defaulting to 0!\n"); ++ *pvs = 0; ++ } ++ ++ pr_info("PVS version: %d\n", *pvs_ver); ++ iounmap(base); ++} ++ ++static int __init qcom_cpufreq_populate_opps(void) ++{ ++ int len, rows, cols, i, k, speed, pvs, pvs_ver; ++ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX"; ++ struct device_node *np; ++ struct device *dev; ++ int cpu = 0; ++ ++ np = of_find_node_by_name(NULL, "qcom,pvs"); ++ if (!np) ++ return -ENODEV; ++ ++ if (of_property_read_bool(np, "qcom,pvs-format-a")) { ++ get_krait_bin_format_a(&speed, &pvs, &pvs_ver); ++ cols = 2; ++ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) { ++ get_krait_bin_format_b(&speed, &pvs, &pvs_ver); ++ cols = 3; ++ } else { ++ return -ENODEV; ++ } ++ ++ snprintf(table_name, sizeof(table_name), ++ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver); ++ ++ if (!of_find_property(np, table_name, &len)) ++ return -EINVAL; ++ ++ len /= sizeof(u32); ++ if (len % cols || len == 0) ++ return -EINVAL; ++ ++ rows = len / cols; ++ ++ for (i = 0, k = 0; i < rows; i++) { ++ u32 freq, volt; ++ ++ of_property_read_u32_index(np, table_name, k++, &freq); ++ of_property_read_u32_index(np, table_name, k++, &volt); ++ while (k % cols) ++ k++; /* Skip uA entries if present */ ++ for (cpu = 0; cpu < num_possible_cpus(); cpu++) { ++ dev = get_cpu_device(cpu); ++ if (!dev) ++ return -ENODEV; ++ if (dev_pm_opp_add(dev, freq, volt)) ++ pr_warn("failed to add OPP %u\n", freq); ++ } ++ } ++ ++ return 0; ++} ++ ++static int __init qcom_cpufreq_driver_init(void) ++{ ++ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; ++ struct platform_device_info devinfo = { ++ .name = "cpufreq-dt", ++ .data = &pdata, ++ .size_data = sizeof(pdata), ++ }; ++ struct device *cpu_dev; ++ struct device_node *np; ++ int ret; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) ++ return -ENODEV; ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) ++ return -ENOENT; ++ ++ if (!of_device_is_compatible(np, "qcom,krait")) { ++ of_node_put(np); ++ return -ENODEV; ++ } ++ of_node_put(np); ++ ++ ret = qcom_cpufreq_populate_opps(); ++ if (ret) ++ return ret; ++ ++ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo)); ++} ++module_init(qcom_cpufreq_driver_init); ++ ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-4.14/0046-cpufreq-qcom-independent-core-clocks.patch b/target/linux/ipq806x/patches-4.14/0046-cpufreq-qcom-independent-core-clocks.patch new file mode 100644 index 000000000..d767dbf5f --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0046-cpufreq-qcom-independent-core-clocks.patch @@ -0,0 +1,66 @@ +From patchwork Fri Dec 8 09:42:30 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v4,12/12] cpufreq: dt: Reintroduce independent_clocks platform data +From: Sricharan R +X-Patchwork-Id: 10102073 +Message-Id: <1512726150-7204-13-git-send-email-sricharan@codeaurora.org> +To: mturquette@baylibre.com, sboyd@codeaurora.org, + devicetree@vger.kernel.org, linux-pm@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org +Cc: sricharan@codeaurora.org +Date: Fri, 8 Dec 2017 15:12:30 +0530 + +The Platform data was removed earlier by, +'commit eb96924acddc ("cpufreq: dt: Kill platform-data")' +since there were no users at that time. +Now this is required when the each of the cpu clocks +can be scaled independently, which is the case +for krait cores. So reintroduce it. + +Signed-off-by: Sricharan R +--- + drivers/cpufreq/cpufreq-dt.c | 7 ++++++- + drivers/cpufreq/cpufreq-dt.h | 6 ++++++ + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -221,7 +221,10 @@ static int cpufreq_init(struct cpufreq_p + } + + if (fallback) { +- cpumask_setall(policy->cpus); ++ struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); ++ ++ if (!pd || !pd->independent_clocks) ++ cpumask_setall(policy->cpus); + + /* + * OPP tables are initialized only for policy->cpu, do it for +@@ -376,6 +379,8 @@ static int dt_cpufreq_probe(struct platf + if (data && data->have_governor_per_policy) + dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY; + ++ dt_cpufreq_driver.driver_data = data; ++ + ret = cpufreq_register_driver(&dt_cpufreq_driver); + if (ret) + dev_err(&pdev->dev, "failed register driver: %d\n", ret); +--- a/drivers/cpufreq/cpufreq-dt.h ++++ b/drivers/cpufreq/cpufreq-dt.h +@@ -13,6 +13,12 @@ + #include + + struct cpufreq_dt_platform_data { ++ /* ++ * True when each CPU has its own clock to control its ++ * frequency, false when all CPUs are controlled by a single ++ * clock. ++ */ ++ bool independent_clocks; + bool have_governor_per_policy; + }; + diff --git a/target/linux/ipq806x/patches-4.14/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch b/target/linux/ipq806x/patches-4.14/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch new file mode 100644 index 000000000..fe9898076 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch @@ -0,0 +1,72 @@ +From c7c6a0f50f9ac3620c611ce06ba1f9fafea0444e Mon Sep 17 00:00:00 2001 +From: Archit Taneja +Date: Mon, 3 Aug 2015 10:38:14 +0530 +Subject: [PATCH 47/69] mtd: nand: Create a BBT flag to access bad block + markers in raw mode + +Some controllers can access the factory bad block marker from OOB only +when they read it in raw mode. When ECC is enabled, these controllers +discard reading/writing bad block markers, preventing access to them +altogether. + +The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks. +This results in the nand driver's ecc->read_oob() op to be called, which +works with ECC enabled. + +Create a new BBT option flag that tells nand_bbt to force the mode to +MTD_OPS_RAW. This would result in the correct op being called for the +underlying nand controller driver. + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja +--- + drivers/mtd/nand/nand_base.c | 6 +++++- + drivers/mtd/nand/nand_bbt.c | 6 +++++- + include/linux/mtd/bbm.h | 6 ++++++ + 3 files changed, 16 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -481,7 +481,11 @@ static int nand_default_block_markbad(st + } else { + ops.len = ops.ooblen = 1; + } +- ops.mode = MTD_OPS_PLACE_OOB; ++ ++ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW)) ++ ops.mode = MTD_OPS_RAW; ++ else ++ ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) +--- a/drivers/mtd/nand/nand_bbt.c ++++ b/drivers/mtd/nand/nand_bbt.c +@@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; +- ops.mode = MTD_OPS_PLACE_OOB; ++ ++ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW)) ++ ops.mode = MTD_OPS_RAW; ++ else ++ ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* +--- a/include/linux/mtd/bbm.h ++++ b/include/linux/mtd/bbm.h +@@ -116,6 +116,12 @@ struct nand_bbt_descr { + #define NAND_BBT_NO_OOB_BBM 0x00080000 + + /* ++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To ++ * be used by controllers which can access BBM only when ECC is disabled, i.e, ++ * when in RAW access mode ++ */ ++#define NAND_BBT_ACCESS_BBM_RAW 0x00100000 ++/* + * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr + * was allocated dynamicaly and must be freed in nand_release(). Has no meaning + * in nand_chip.bbt_options. diff --git a/target/linux/ipq806x/patches-4.14/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch b/target/linux/ipq806x/patches-4.14/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch new file mode 100644 index 000000000..44e473830 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch @@ -0,0 +1,27 @@ +From 5c294df1715d673f94f3b0a6e1ea3a426ca35e6e Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Thu, 28 Apr 2016 16:20:12 +0300 +Subject: [PATCH 48/69] PM / OPP: HACK: Allow to set regulator without opp_list + +Signed-off-by: Georgi Djakov +--- + drivers/base/power/opp/core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/base/power/opp/core.c ++++ b/drivers/base/power/opp/core.c +@@ -1276,12 +1276,13 @@ struct opp_table *dev_pm_opp_set_regulat + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); +- ++#if 0 + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } ++#endif + + /* Already have regulators set */ + if (opp_table->regulators) { diff --git a/target/linux/ipq806x/patches-4.14/0049-cpufreq-dt-support-l2-cache-scaling.patch b/target/linux/ipq806x/patches-4.14/0049-cpufreq-dt-support-l2-cache-scaling.patch new file mode 100644 index 000000000..d6ffa1fa5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0049-cpufreq-dt-support-l2-cache-scaling.patch @@ -0,0 +1,203 @@ +From 6d18b351ee5b91e5310c730882e52a01309ac55a Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Sun, 7 Jan 2018 01:23:30 +0300 +Subject: [PATCH] cpufreq-dt: support l2 cache scaling + +Allows to scale L2 cache frequency and voltage depending on cpu +frequency. + +L2 frequency, voltage and target cpu frequencies are taken from DT. + +Signed-off-by: Pavel Kubelun +--- + drivers/cpufreq/cpufreq-dt.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + include/linux/cpufreq.h | 10 ++++++++++ + 2 files changed, 126 insertions(+), 5 deletions(-) + +diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c +index dbcfe39..37e8abe 100644 +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -27,6 +27,8 @@ + + #include "cpufreq-dt.h" + ++static DEFINE_PER_CPU(struct clk *, cpu_cores_clks); ++ + struct private_data { + struct opp_table *opp_table; + struct device *cpu_dev; +@@ -43,9 +45,60 @@ static struct freq_attr *cpufreq_dt_attr[] = { + static int set_target(struct cpufreq_policy *policy, unsigned int index) + { + struct private_data *priv = policy->driver_data; ++ struct clk *l2_clk = policy->l2_clk; ++ struct regulator *l2_regulator = policy->l2_regulator; ++ unsigned long target_freq = policy->freq_table[index].frequency * 1000; ++ int ret; ++ ++ ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq); ++ ++ if (!ret && policy->l2_rate_set) { ++ int cpu, i, tol = 0; ++ unsigned long freq, max_cpu_freq = 0; ++ unsigned long new_l2_freq = 0, new_l2_volt = 0; ++ ++ /* L2 cache supply has a single regulator shared by cores and each core ++ * registers itself as a regulator consumer. N of consumers = N of cores. ++ * Regulator driver sets the regulator output to highest voltage requested ++ * among all consumers. We set the regulator voltage per core and let the ++ * driver set the highest voltage requested - either core can be running ++ * at max and l2 cache should get the needed voltage. ++ */ ++ if (policy->l2_volt_set) { ++ for (i = 2; i >= 0; i--) { ++ if (target_freq >= policy->l2_cpufreq[i]) { ++ tol = policy->l2_volt[i] * policy->l2_volt_tol / 100; ++ new_l2_volt = policy->l2_volt[i]; ++ ++ regulator_set_voltage_tol(l2_regulator,new_l2_volt,tol); ++ ++ break; ++ } ++ } ++ } ++ ++ /* Unlike regulators, clock rate is adjusted on any request. Let's pick ++ * the highest core frequency and set the L2 cache frequency, so we ++ * don't pull the L2 cache frequency down prematurely if either cores is ++ * running at max. ++ */ ++ for_each_possible_cpu(cpu) { ++ freq = clk_get_rate(per_cpu(cpu_cores_clks, cpu)); ++ max_cpu_freq = max(max_cpu_freq, freq); ++ } ++ ++ for (i = 2; i >= 0; i--) { ++ if (max_cpu_freq >= policy->l2_cpufreq[i]) { ++ new_l2_freq = policy->l2_rate[i]; ++ ++ clk_set_rate(l2_clk, new_l2_freq); ++ ++ break; ++ } ++ } ++ } + +- return dev_pm_opp_set_rate(priv->cpu_dev, +- policy->freq_table[index].frequency * 1000); ++ return ret; + } + + /* +@@ -148,18 +201,24 @@ static int cpufreq_init(struct cpufreq_policy *policy) + struct private_data *priv; + struct device *cpu_dev; + struct clk *cpu_clk; ++ struct clk *l2_clk = NULL; ++ struct regulator *l2_regulator = NULL; ++ struct device_node *np, *cache; + unsigned int transition_latency; + bool fallback = false; + const char *name; +- int ret; +- ++ int ret, cpu; ++ + cpu_dev = get_cpu_device(policy->cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", policy->cpu); + return -ENODEV; + } + +- cpu_clk = clk_get(cpu_dev, NULL); ++ for_each_possible_cpu(cpu) ++ per_cpu(cpu_cores_clks, cpu) = clk_get(get_cpu_device(cpu), NULL); ++ ++ cpu_clk = per_cpu(cpu_cores_clks, policy->cpu); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); +@@ -279,6 +338,58 @@ static int cpufreq_init(struct cpufreq_policy *policy) + policy->cpuinfo.transition_latency = transition_latency; + policy->dvfs_possible_from_any_cpu = true; + ++ l2_clk = devm_clk_get(cpu_dev, "l2"); ++ if (!IS_ERR(l2_clk)) { ++ policy->l2_clk = l2_clk; ++ ++ np = of_node_get(priv->cpu_dev->of_node); ++ if (np) { ++ of_property_read_u32(np, "voltage-tolerance", &policy->l2_volt_tol); ++ cache = of_find_next_cache_node(np); ++ } ++ } ++ ++ policy->l2_rate_set = false; ++ policy->l2_volt_set = false; ++ ++ if (cache) { ++ struct device_node *vdd; ++ ++ of_property_read_u32_array(cache, "l2-rates", policy->l2_rate, 3); ++ if (policy->l2_rate[0] && policy->l2_rate[1] && policy->l2_rate[2]) { ++ policy->l2_rate_set = true; ++ of_property_read_u32_array(cache, "l2-cpufreq", policy->l2_cpufreq, 3); ++ of_property_read_u32_array(cache, "l2-volt", policy->l2_volt, 3); ++ } else ++ pr_warn("L2: failed to parse L2 rates\n"); ++ ++ if (!policy->l2_cpufreq[0] && !policy->l2_cpufreq[1] && ++ !policy->l2_cpufreq[2] && policy->l2_rate_set) { ++ int i; ++ ++ pr_warn("L2: failed to parse target cpu freq, using defaults\n"); ++ for (i = 0; i < 3; i++) ++ policy->l2_cpufreq[i] = policy->l2_rate[i]; ++ } ++ ++ if (policy->l2_volt[0] && policy->l2_volt[1] && policy->l2_volt[2] && ++ policy->l2_volt_tol && policy->l2_rate_set) ++ vdd = of_parse_phandle(cache, "l2-supply", 0); ++ ++ if (vdd) { ++ l2_regulator = devm_regulator_get(cpu_dev, vdd->name); ++ if (!IS_ERR(l2_regulator)) { ++ policy->l2_regulator = l2_regulator; ++ policy->l2_volt_set = true; ++ } else { ++ pr_warn("failed to get l2 supply\n"); ++ l2_regulator = NULL; ++ } ++ ++ of_node_put(vdd); ++ } ++ } ++ + return 0; + + out_free_cpufreq_table: +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +index 537ff84..638169f 100644 +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -73,6 +73,16 @@ struct cpufreq_policy { + unsigned int cpu; /* cpu managing this policy, must be online */ + + struct clk *clk; ++ ++ struct clk *l2_clk; /* L2 clock */ ++ struct regulator *l2_regulator; /* L2 supply */ ++ unsigned int l2_rate[3]; /* L2 bus clock rate */ ++ bool l2_rate_set; ++ unsigned int l2_cpufreq[3]; /* L2 target CPU frequency */ ++ unsigned int l2_volt[3]; /* L2 voltage array */ ++ bool l2_volt_set; ++ unsigned int l2_volt_tol; /* L2 voltage tolerance */ ++ + struct cpufreq_cpuinfo cpuinfo;/* see above */ + + unsigned int min; /* in kHz */ +-- +Working Copy 3.0.6 + diff --git a/target/linux/ipq806x/patches-4.14/0053-regulator-add-smb208-support.patch b/target/linux/ipq806x/patches-4.14/0053-regulator-add-smb208-support.patch new file mode 100644 index 000000000..0d2862c60 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0053-regulator-add-smb208-support.patch @@ -0,0 +1,55 @@ +From ef10381ca4d01848ebedb4afb2c78feb8052f103 Mon Sep 17 00:00:00 2001 +From: Adrian Panella +Date: Thu, 9 Mar 2017 08:26:54 +0100 +Subject: [PATCH 53/69] regulator: add smb208 support + +Signed-off-by: Adrian Panella +--- + Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 4 ++++ + drivers/regulator/qcom_rpm-regulator.c | 9 +++++++++ + 2 files changed, 13 insertions(+) + +--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt ++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +@@ -61,6 +61,7 @@ Regulator nodes are identified by their + "qcom,rpm-pm8901-regulators" + "qcom,rpm-pm8921-regulators" + "qcom,rpm-pm8018-regulators" ++ "qcom,rpm-smb208-regulators" + + - vdd_l0_l1_lvs-supply: + - vdd_l2_l11_l12-supply: +@@ -171,6 +172,9 @@ pm8018: + s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, + l12, l14, lvs1 + ++smb208: ++ s1a, s1b, s2a, s2b ++ + The content of each sub-node is defined by the standard binding for regulators - + see regulator.txt - with additional custom properties described below: + +--- a/drivers/regulator/qcom_rpm-regulator.c ++++ b/drivers/regulator/qcom_rpm-regulator.c +@@ -933,12 +933,21 @@ static const struct rpm_regulator_data r + { } + }; + ++static const struct rpm_regulator_data rpm_smb208_regulators[] = { ++ { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" }, ++ { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" }, ++ { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" }, ++ { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" }, ++ { } ++}; ++ + static const struct of_device_id rpm_of_match[] = { + { .compatible = "qcom,rpm-pm8018-regulators", + .data = &rpm_pm8018_regulators }, + { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, + { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, + { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, ++ { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators }, + { } + }; + MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/target/linux/ipq806x/patches-4.14/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch b/target/linux/ipq806x/patches-4.14/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch new file mode 100644 index 000000000..5bd58c813 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch @@ -0,0 +1,29 @@ +From 04ca10340f1b4d92e849724d322a7ca225d11539 Mon Sep 17 00:00:00 2001 +From: Lina Iyer +Date: Wed, 25 Mar 2015 14:25:29 -0600 +Subject: [PATCH 59/69] ARM: cpuidle: Add cpuidle support for QCOM cpus + +Define ARM_QCOM_CPUIDLE config item to enable cpuidle support. + +Cc: Stephen Boyd +Cc: Arnd Bergmann +Cc: Kevin Hilman +Cc: Daniel Lezcano +Signed-off-by: Lina Iyer +--- + drivers/cpuidle/Kconfig.arm | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/cpuidle/Kconfig.arm ++++ b/drivers/cpuidle/Kconfig.arm +@@ -75,3 +75,10 @@ config ARM_MVEBU_V7_CPUIDLE + depends on ARCH_MVEBU && !ARM64 + help + Select this to enable cpuidle on Armada 370, 38x and XP processors. ++ ++config ARM_QCOM_CPUIDLE ++ bool "CPU Idle Driver for QCOM processors" ++ depends on ARCH_QCOM ++ select ARM_CPUIDLE ++ help ++ Select this to enable cpuidle on QCOM processors. diff --git a/target/linux/ipq806x/patches-4.14/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.14/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch new file mode 100644 index 000000000..f810f6ac4 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -0,0 +1,62 @@ +From fa71139b55e114aa8c3c4823ff8ee7d49ee810d4 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Wed, 29 Apr 2015 15:21:46 -0700 +Subject: [PATCH 60/69] HACK: arch: arm: force ZRELADDR on arch-qcom + +ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended +on most ARM architectures. This automatically calculate ZRELADDR by +masking PHYS_OFFSET with 0xf8000000. + +However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware +network accelerators, and the bootloader removes this section from the +layout passed from the ATAGS (when used). + +For newer bootloader, when DT is used, this is not a problem, we just +reserve this memory in the device tree. But if the bootloader doesn't +have DT support, then ATAGS have to be used. In this case, the ARM +decompressor will position the kernel in this low mem, which will not be +in the RAM section mapped by the bootloader, which means the kernel will +freeze in the middle of the boot process trying to map the memory. + +As a work around, this patch allows disabling AUTO_ZRELADDR when +ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders +which don't support device-tree, which is the case on certain early +IPQ806x based designs. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/Kconfig | 2 +- + arch/arm/Makefile | 2 ++ + arch/arm/mach-qcom/Makefile.boot | 1 + + 3 files changed, 4 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/mach-qcom/Makefile.boot + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -341,7 +341,7 @@ config ARCH_MULTIPLATFORM + depends on MMU + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ select AUTO_ZRELADDR if !ARCH_QCOM + select TIMER_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -255,9 +255,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac + else + MACHINE := + endif ++ifeq ($(CONFIG_ARCH_QCOM),) + ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) + MACHINE := + endif ++endif + + machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) + platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile.boot +@@ -0,0 +1 @@ ++zreladdr-y+= 0x42208000 diff --git a/target/linux/ipq806x/patches-4.14/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-4.14/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch new file mode 100644 index 000000000..a4a957545 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch @@ -0,0 +1,23 @@ +From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:31:44 +0100 +Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting + +Signed-off-by: John Crispin +--- + drivers/mtd/qcom_smem_part.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/qcom_smem_part.c ++++ b/drivers/mtd/qcom_smem_part.c +@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st + m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); + m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); + ++ /* "rootfs" conflicts with OpenWrt auto mounting */ ++ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs")) ++ m_part->name = "ubi"; ++ + /* + * The last SMEM partition may have its size marked as + * something like 0xffffffff, which means "until the end of the diff --git a/target/linux/ipq806x/patches-4.14/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.14/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch new file mode 100644 index 000000000..a3e0a1dc5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch @@ -0,0 +1,25 @@ +From a16fcf911a020e46439a3bb3e702463fc3159831 Mon Sep 17 00:00:00 2001 +From: Abhishek Sahu +Date: Wed, 18 Nov 2015 12:38:56 +0530 +Subject: [PATCH 62/69] ipq806x: gcc: Added the enable regs and mask for PRNG + +kernel got hanged while reading from /dev/hwrng at the +time of PRNG clock enable + +Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172 +Signed-off-by: Abhishek Sahu +--- + drivers/clk/qcom/gcc-ipq806x.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -1234,6 +1234,8 @@ static struct clk_rcg prng_src = { + .parent_map = gcc_pxo_pll8_map, + }, + .clkr = { ++ .enable_reg = 0x2e80, ++ .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "prng_src", + .parent_names = gcc_pxo_pll8, diff --git a/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch b/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch new file mode 100644 index 000000000..685b0c3ce --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch @@ -0,0 +1,627 @@ +From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001 +From: Rajith Cherian +Date: Tue, 14 Feb 2017 18:30:43 +0530 +Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064 + +Add TSENS driver template to support IPQ8064. +This is a base file copied from tsens-8960.c + +Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7 +Signed-off-by: Rajith Cherian + +ipq8064: tsens: TSENS driver support for IPQ8064 + +Support for IPQ8064 tsens driver. The driver works +with the thermal framework. The driver overrides the +following fucntionalities: + +1. Get current temperature. +2. Get/Set trip temperatures. +3. Enabled/Disable trip points. +4. ISR for threshold generated interrupt. +5. Notify userspace when trip points are hit. + +Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918 +Signed-off-by: Rajith Cherian +--- + .../devicetree/bindings/thermal/qcom-tsens.txt | 1 + + drivers/thermal/qcom/Makefile | 3 +- + drivers/thermal/qcom/tsens-ipq8064.c | 551 +++++++++++++++++++++ + drivers/thermal/qcom/tsens.c | 3 + + drivers/thermal/qcom/tsens.h | 2 +- + 5 files changed, 558 insertions(+), 2 deletions(-) + create mode 100644 drivers/thermal/qcom/tsens-ipq8064.c + +--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt +@@ -5,6 +5,7 @@ Required properties: + - "qcom,msm8916-tsens" : For 8916 Family of SoCs + - "qcom,msm8974-tsens" : For 8974 Family of SoCs + - "qcom,msm8996-tsens" : For 8996 Family of SoCs ++ - "qcom,ipq8064-tsens" : For IPQ8064 + + - reg: Address range of the thermal registers + - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. +--- a/drivers/thermal/qcom/Makefile ++++ b/drivers/thermal/qcom/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o +-qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o ++qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o \ ++ tsens-ipq8064.o +--- /dev/null ++++ b/drivers/thermal/qcom/tsens-ipq8064.c +@@ -0,0 +1,551 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tsens.h" ++ ++#define CAL_MDEGC 30000 ++ ++#define CONFIG_ADDR 0x3640 ++/* CONFIG_ADDR bitmasks */ ++#define CONFIG 0x9b ++#define CONFIG_MASK 0xf ++#define CONFIG_SHIFT 0 ++ ++#define STATUS_CNTL_8064 0x3660 ++#define CNTL_ADDR 0x3620 ++/* CNTL_ADDR bitmasks */ ++#define EN BIT(0) ++#define SW_RST BIT(1) ++#define SENSOR0_EN BIT(3) ++#define SLP_CLK_ENA BIT(26) ++#define MEASURE_PERIOD 1 ++#define SENSOR0_SHIFT 3 ++ ++/* INT_STATUS_ADDR bitmasks */ ++#define MIN_STATUS_MASK BIT(0) ++#define LOWER_STATUS_CLR BIT(1) ++#define UPPER_STATUS_CLR BIT(2) ++#define MAX_STATUS_MASK BIT(3) ++ ++#define THRESHOLD_ADDR 0x3624 ++/* THRESHOLD_ADDR bitmasks */ ++#define THRESHOLD_MAX_CODE 0x20000 ++#define THRESHOLD_MIN_CODE 0 ++#define THRESHOLD_MAX_LIMIT_SHIFT 24 ++#define THRESHOLD_MIN_LIMIT_SHIFT 16 ++#define THRESHOLD_UPPER_LIMIT_SHIFT 8 ++#define THRESHOLD_LOWER_LIMIT_SHIFT 0 ++#define THRESHOLD_MAX_LIMIT_MASK (THRESHOLD_MAX_CODE << \ ++ THRESHOLD_MAX_LIMIT_SHIFT) ++#define THRESHOLD_MIN_LIMIT_MASK (THRESHOLD_MAX_CODE << \ ++ THRESHOLD_MIN_LIMIT_SHIFT) ++#define THRESHOLD_UPPER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ ++ THRESHOLD_UPPER_LIMIT_SHIFT) ++#define THRESHOLD_LOWER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ ++ THRESHOLD_LOWER_LIMIT_SHIFT) ++ ++/* Initial temperature threshold values */ ++#define LOWER_LIMIT_TH 0x9d /* 95C */ ++#define UPPER_LIMIT_TH 0xa6 /* 105C */ ++#define MIN_LIMIT_TH 0x0 ++#define MAX_LIMIT_TH 0xff ++ ++#define S0_STATUS_ADDR 0x3628 ++#define STATUS_ADDR_OFFSET 2 ++#define SENSOR_STATUS_SIZE 4 ++#define INT_STATUS_ADDR 0x363c ++#define TRDY_MASK BIT(7) ++#define TIMEOUT_US 100 ++ ++#define TSENS_EN BIT(0) ++#define TSENS_SW_RST BIT(1) ++#define TSENS_ADC_CLK_SEL BIT(2) ++#define SENSOR0_EN BIT(3) ++#define SENSOR1_EN BIT(4) ++#define SENSOR2_EN BIT(5) ++#define SENSOR3_EN BIT(6) ++#define SENSOR4_EN BIT(7) ++#define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \ ++ SENSOR2_EN | SENSOR3_EN | SENSOR4_EN) ++#define TSENS_8064_SENSOR5_EN BIT(8) ++#define TSENS_8064_SENSOR6_EN BIT(9) ++#define TSENS_8064_SENSOR7_EN BIT(10) ++#define TSENS_8064_SENSOR8_EN BIT(11) ++#define TSENS_8064_SENSOR9_EN BIT(12) ++#define TSENS_8064_SENSOR10_EN BIT(13) ++#define TSENS_8064_SENSORS_EN (SENSORS_EN | \ ++ TSENS_8064_SENSOR5_EN | \ ++ TSENS_8064_SENSOR6_EN | \ ++ TSENS_8064_SENSOR7_EN | \ ++ TSENS_8064_SENSOR8_EN | \ ++ TSENS_8064_SENSOR9_EN | \ ++ TSENS_8064_SENSOR10_EN) ++ ++#define TSENS_8064_SEQ_SENSORS 5 ++#define TSENS_8064_S4_S5_OFFSET 40 ++#define TSENS_FACTOR 1 ++ ++/* Trips: from very hot to very cold */ ++enum tsens_trip_type { ++ TSENS_TRIP_STAGE3 = 0, ++ TSENS_TRIP_STAGE2, ++ TSENS_TRIP_STAGE1, ++ TSENS_TRIP_STAGE0, ++ TSENS_TRIP_NUM, ++}; ++ ++u32 tsens_8064_slope[] = { ++ 1176, 1176, 1154, 1176, ++ 1111, 1132, 1132, 1199, ++ 1132, 1199, 1132 ++ }; ++ ++/* Temperature on y axis and ADC-code on x-axis */ ++static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s) ++{ ++ int degcbeforefactor, degc; ++ ++ degcbeforefactor = (adc_code * s->slope) + s->offset; ++ ++ if (degcbeforefactor == 0) ++ degc = degcbeforefactor; ++ else if (degcbeforefactor > 0) ++ degc = (degcbeforefactor + TSENS_FACTOR/2) ++ / TSENS_FACTOR; ++ else ++ degc = (degcbeforefactor - TSENS_FACTOR/2) ++ / TSENS_FACTOR; ++ ++ return degc; ++} ++ ++static int degC_to_code(int degC, const struct tsens_sensor *s) ++{ ++ int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2)) ++ / s->slope; ++ ++ if (code > THRESHOLD_MAX_CODE) ++ code = THRESHOLD_MAX_CODE; ++ else if (code < THRESHOLD_MIN_CODE) ++ code = THRESHOLD_MIN_CODE; ++ return code; ++} ++ ++static int suspend_ipq8064(struct tsens_device *tmdev) ++{ ++ int ret; ++ unsigned int mask; ++ struct regmap *map = tmdev->map; ++ ++ ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); ++ if (ret) ++ return ret; ++ ++ mask = SLP_CLK_ENA | EN; ++ ++ ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int resume_ipq8064(struct tsens_device *tmdev) ++{ ++ int ret; ++ struct regmap *map = tmdev->map; ++ ++ ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); ++ if (ret) ++ return ret; ++ ++ ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void notify_uspace_tsens_fn(struct work_struct *work) ++{ ++ struct tsens_sensor *s = container_of(work, struct tsens_sensor, ++ notify_work); ++ ++ sysfs_notify(&s->tzd->device.kobj, NULL, "type"); ++} ++ ++static void tsens_scheduler_fn(struct work_struct *work) ++{ ++ struct tsens_device *tmdev = container_of(work, struct tsens_device, ++ tsens_work); ++ unsigned int threshold, threshold_low, code, reg, sensor, mask; ++ unsigned int sensor_addr; ++ bool upper_th_x, lower_th_x; ++ int adc_code, ret; ++ ++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); ++ if (ret) ++ return; ++ reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR; ++ ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg); ++ if (ret) ++ return; ++ ++ mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR); ++ ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold); ++ if (ret) ++ return; ++ threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) ++ >> THRESHOLD_LOWER_LIMIT_SHIFT; ++ threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) ++ >> THRESHOLD_UPPER_LIMIT_SHIFT; ++ ++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); ++ if (ret) ++ return; ++ ++ ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor); ++ if (ret) ++ return; ++ sensor &= (uint32_t) TSENS_8064_SENSORS_EN; ++ sensor >>= SENSOR0_SHIFT; ++ ++ /* Constraint: There is only 1 interrupt control register for all ++ * 11 temperature sensor. So monitoring more than 1 sensor based ++ * on interrupts will yield inconsistent result. To overcome this ++ * issue we will monitor only sensor 0 which is the master sensor. ++ */ ++ ++ /* Skip if the sensor is disabled */ ++ if (sensor & 1) { ++ ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code); ++ if (ret) ++ return; ++ upper_th_x = code >= threshold; ++ lower_th_x = code <= threshold_low; ++ if (upper_th_x) ++ mask |= UPPER_STATUS_CLR; ++ if (lower_th_x) ++ mask |= LOWER_STATUS_CLR; ++ if (upper_th_x || lower_th_x) { ++ /* Notify user space */ ++ schedule_work(&tmdev->sensor[0].notify_work); ++ regmap_read(tmdev->map, sensor_addr, &adc_code); ++ pr_debug("Trigger (%d degrees) for sensor %d\n", ++ code_to_degC(adc_code, &tmdev->sensor[0]), 0); ++ } ++ } ++ regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask); ++ ++ /* force memory to sync */ ++ mb(); ++} ++ ++static irqreturn_t tsens_isr(int irq, void *data) ++{ ++ struct tsens_device *tmdev = data; ++ ++ schedule_work(&tmdev->tsens_work); ++ return IRQ_HANDLED; ++} ++ ++static void hw_init(struct tsens_device *tmdev) ++{ ++ int ret; ++ unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; ++ unsigned int reg_status_cntl = 0; ++ ++ regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); ++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST); ++ ++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) ++ | (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT); ++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ regmap_read(tmdev->map, STATUS_CNTL_8064, ®_status_cntl); ++ reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR ++ | MIN_STATUS_MASK | MAX_STATUS_MASK; ++ regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl); ++ reg_cntl |= TSENS_EN; ++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ ++ regmap_read(tmdev->map, CONFIG_ADDR, ®_cfg); ++ reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT); ++ regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg); ++ ++ reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT) ++ | (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT) ++ | (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) ++ | (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT); ++ ++ regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr); ++ ++ ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr, ++ IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev); ++ if (ret < 0) { ++ pr_err("%s: request_irq FAIL: %d\n", __func__, ret); ++ return; ++ } ++ ++ INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); ++} ++ ++static int init_ipq8064(struct tsens_device *tmdev) ++{ ++ int ret, i; ++ u32 reg_cntl, offset = 0; ++ ++ init_common(tmdev); ++ if (!tmdev->map) ++ return -ENODEV; ++ ++ /* ++ * The status registers for each sensor are discontiguous ++ * because some SoCs have 5 sensors while others have more ++ * but the control registers stay in the same place, i.e ++ * directly after the first 5 status registers. ++ */ ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ if (i >= TSENS_8064_SEQ_SENSORS) ++ offset = TSENS_8064_S4_S5_OFFSET; ++ ++ tmdev->sensor[i].status = S0_STATUS_ADDR + offset ++ + (i << STATUS_ADDR_OFFSET); ++ tmdev->sensor[i].slope = tsens_8064_slope[i]; ++ INIT_WORK(&tmdev->sensor[i].notify_work, ++ notify_uspace_tsens_fn); ++ } ++ ++ reg_cntl = SW_RST; ++ ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); ++ if (ret) ++ return ret; ++ ++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); ++ reg_cntl &= ~SW_RST; ++ ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, ++ CONFIG_MASK, CONFIG); ++ ++ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ if (ret) ++ return ret; ++ ++ reg_cntl |= EN; ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int calibrate_ipq8064(struct tsens_device *tmdev) ++{ ++ int i; ++ char *data, *data_backup; ++ ++ ssize_t num_read = tmdev->num_sensors; ++ struct tsens_sensor *s = tmdev->sensor; ++ ++ data = qfprom_read(tmdev->dev, "calib"); ++ if (IS_ERR(data)) { ++ pr_err("Calibration not found.\n"); ++ return PTR_ERR(data); ++ } ++ ++ data_backup = qfprom_read(tmdev->dev, "calib_backup"); ++ if (IS_ERR(data_backup)) { ++ pr_err("Backup calibration not found.\n"); ++ return PTR_ERR(data_backup); ++ } ++ ++ for (i = 0; i < num_read; i++) { ++ s[i].calib_data = readb_relaxed(data + i); ++ s[i].calib_data_backup = readb_relaxed(data_backup + i); ++ ++ if (s[i].calib_data_backup) ++ s[i].calib_data = s[i].calib_data_backup; ++ if (!s[i].calib_data) { ++ pr_err("QFPROM TSENS calibration data not present\n"); ++ return -ENODEV; ++ } ++ s[i].slope = tsens_8064_slope[i]; ++ s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); ++ } ++ ++ hw_init(tmdev); ++ ++ return 0; ++} ++ ++static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp) ++{ ++ int ret; ++ u32 code, trdy; ++ const struct tsens_sensor *s = &tmdev->sensor[id]; ++ unsigned long timeout; ++ ++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); ++ do { ++ ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); ++ if (ret) ++ return ret; ++ if (!(trdy & TRDY_MASK)) ++ continue; ++ ret = regmap_read(tmdev->map, s->status, &code); ++ if (ret) ++ return ret; ++ *temp = code_to_degC(code, s); ++ return 0; ++ } while (time_before(jiffies, timeout)); ++ ++ return -ETIMEDOUT; ++} ++ ++static int set_trip_temp_ipq8064(void *data, int trip, int temp) ++{ ++ unsigned int reg_th, reg_cntl; ++ int ret, code, code_chk, hi_code, lo_code; ++ const struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ ++ code_chk = code = degC_to_code(temp, s); ++ ++ if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE) ++ return -EINVAL; ++ ++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(tmdev->map, THRESHOLD_ADDR, ®_th); ++ if (ret) ++ return ret; ++ ++ hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK) ++ >> THRESHOLD_UPPER_LIMIT_SHIFT; ++ lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK) ++ >> THRESHOLD_LOWER_LIMIT_SHIFT; ++ ++ switch (trip) { ++ case TSENS_TRIP_STAGE3: ++ code <<= THRESHOLD_MAX_LIMIT_SHIFT; ++ reg_th &= ~THRESHOLD_MAX_LIMIT_MASK; ++ break; ++ case TSENS_TRIP_STAGE2: ++ if (code_chk <= lo_code) ++ return -EINVAL; ++ code <<= THRESHOLD_UPPER_LIMIT_SHIFT; ++ reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK; ++ break; ++ case TSENS_TRIP_STAGE1: ++ if (code_chk >= hi_code) ++ return -EINVAL; ++ code <<= THRESHOLD_LOWER_LIMIT_SHIFT; ++ reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK; ++ break; ++ case TSENS_TRIP_STAGE0: ++ code <<= THRESHOLD_MIN_LIMIT_SHIFT; ++ reg_th &= ~THRESHOLD_MIN_LIMIT_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int set_trip_activate_ipq8064(void *data, int trip, ++ enum thermal_trip_activation_mode mode) ++{ ++ unsigned int reg_cntl, mask, val; ++ const struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ int ret; ++ ++ if (!tmdev || trip < 0) ++ return -EINVAL; ++ ++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); ++ if (ret) ++ return ret; ++ ++ switch (trip) { ++ case TSENS_TRIP_STAGE3: ++ mask = MAX_STATUS_MASK; ++ break; ++ case TSENS_TRIP_STAGE2: ++ mask = UPPER_STATUS_CLR; ++ break; ++ case TSENS_TRIP_STAGE1: ++ mask = LOWER_STATUS_CLR; ++ break; ++ case TSENS_TRIP_STAGE0: ++ mask = MIN_STATUS_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) ++ val = reg_cntl | mask; ++ else ++ val = reg_cntl & ~mask; ++ ++ ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val); ++ if (ret) ++ return ret; ++ ++ /* force memory to sync */ ++ mb(); ++ return 0; ++} ++ ++const struct tsens_ops ops_ipq8064 = { ++ .init = init_ipq8064, ++ .calibrate = calibrate_ipq8064, ++ .get_temp = get_temp_ipq8064, ++ .suspend = suspend_ipq8064, ++ .resume = resume_ipq8064, ++ .set_trip_temp = set_trip_temp_ipq8064, ++ .set_trip_activate = set_trip_activate_ipq8064, ++}; ++ ++const struct tsens_data data_ipq8064 = { ++ .num_sensors = 11, ++ .ops = &ops_ipq8064, ++}; +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -72,6 +72,9 @@ static const struct of_device_id tsens_t + }, { + .compatible = "qcom,msm8996-tsens", + .data = &data_8996, ++ }, { ++ .compatible = "qcom,ipq8064-tsens", ++ .data = &data_ipq8064, + }, + {} + }; +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -89,6 +89,6 @@ void compute_intercept_slope(struct tsen + int init_common(struct tsens_device *); + int get_temp_common(struct tsens_device *, int, int *); + +-extern const struct tsens_data data_8916, data_8974, data_8960, data_8996; ++extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064; + + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.14/0063-2-tsens-support-configurable-interrupts.patch b/target/linux/ipq806x/patches-4.14/0063-2-tsens-support-configurable-interrupts.patch new file mode 100644 index 000000000..3d6dd5310 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0063-2-tsens-support-configurable-interrupts.patch @@ -0,0 +1,464 @@ +From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001 +From: Rajith Cherian +Date: Wed, 1 Feb 2017 19:00:26 +0530 +Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts + +Provide support for adding configurable high and +configurable low trip temperatures. An interrupts is +also triggerred when these trip points are hit. The +interrupts can be activated or deactivated from sysfs. +This functionality is made available only if +CONFIG_THERMAL_WRITABLE_TRIPS is defined. + +Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934 +Signed-off-by: Rajith Cherian +--- + .../devicetree/bindings/thermal/qcom-tsens.txt | 4 ++ + drivers/thermal/of-thermal.c | 63 ++++++++++++++++++---- + drivers/thermal/qcom/tsens.c | 43 ++++++++++++--- + drivers/thermal/qcom/tsens.h | 11 ++++ + drivers/thermal/thermal_core.c | 44 ++++++++++++++- + include/linux/thermal.h | 14 +++++ + 6 files changed, 162 insertions(+), 17 deletions(-) + +--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt +@@ -12,11 +12,15 @@ Required properties: + - Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify + nvmem cells + ++Optional properties: ++- interrupts: Interrupt which gets triggered when threshold is hit ++ + Example: + tsens: thermal-sensor@900000 { + compatible = "qcom,msm8916-tsens"; + reg = <0x4a8000 0x2000>; + nvmem-cells = <&tsens_caldata>, <&tsens_calsel>; + nvmem-cell-names = "caldata", "calsel"; ++ interrupts = <0 178 0>; + #thermal-sensor-cells = <1>; + }; +--- a/drivers/thermal/of-thermal.c ++++ b/drivers/thermal/of-thermal.c +@@ -95,7 +95,7 @@ static int of_thermal_get_temp(struct th + { + struct __thermal_zone *data = tz->devdata; + +- if (!data->ops->get_temp) ++ if (!data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EINVAL; + + return data->ops->get_temp(data->sensor_data, temp); +@@ -106,7 +106,8 @@ static int of_thermal_set_trips(struct t + { + struct __thermal_zone *data = tz->devdata; + +- if (!data->ops || !data->ops->set_trips) ++ if (!data->ops || !data->ops->set_trips ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EINVAL; + + return data->ops->set_trips(data->sensor_data, low, high); +@@ -192,6 +193,9 @@ static int of_thermal_set_emul_temp(stru + { + struct __thermal_zone *data = tz->devdata; + ++ if (data->mode == THERMAL_DEVICE_DISABLED) ++ return -EINVAL; ++ + return data->ops->set_emul_temp(data->sensor_data, temp); + } + +@@ -200,7 +204,7 @@ static int of_thermal_get_trend(struct t + { + struct __thermal_zone *data = tz->devdata; + +- if (!data->ops->get_trend) ++ if (!data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EINVAL; + + return data->ops->get_trend(data->sensor_data, trip, trend); +@@ -286,7 +290,9 @@ static int of_thermal_set_mode(struct th + mutex_unlock(&tz->lock); + + data->mode = mode; +- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); ++ ++ if (mode == THERMAL_DEVICE_ENABLED) ++ thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + return 0; + } +@@ -296,7 +302,8 @@ static int of_thermal_get_trip_type(stru + { + struct __thermal_zone *data = tz->devdata; + +- if (trip >= data->ntrips || trip < 0) ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EDOM; + + *type = data->trips[trip].type; +@@ -304,12 +311,39 @@ static int of_thermal_get_trip_type(stru + return 0; + } + ++static int of_thermal_activate_trip_type(struct thermal_zone_device *tz, ++ int trip, enum thermal_trip_activation_mode mode) ++{ ++ struct __thermal_zone *data = tz->devdata; ++ ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) ++ return -EDOM; ++ ++ /* ++ * The configurable_hi and configurable_lo trip points can be ++ * activated and deactivated. ++ */ ++ ++ if (data->ops->set_trip_activate) { ++ int ret; ++ ++ ret = data->ops->set_trip_activate(data->sensor_data, ++ trip, mode); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, + int *temp) + { + struct __thermal_zone *data = tz->devdata; + +- if (trip >= data->ntrips || trip < 0) ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EDOM; + + *temp = data->trips[trip].temperature; +@@ -322,7 +356,8 @@ static int of_thermal_set_trip_temp(stru + { + struct __thermal_zone *data = tz->devdata; + +- if (trip >= data->ntrips || trip < 0) ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EDOM; + + if (data->ops->set_trip_temp) { +@@ -344,7 +379,8 @@ static int of_thermal_get_trip_hyst(stru + { + struct __thermal_zone *data = tz->devdata; + +- if (trip >= data->ntrips || trip < 0) ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EDOM; + + *hyst = data->trips[trip].hysteresis; +@@ -357,7 +393,8 @@ static int of_thermal_set_trip_hyst(stru + { + struct __thermal_zone *data = tz->devdata; + +- if (trip >= data->ntrips || trip < 0) ++ if (trip >= data->ntrips || trip < 0 ++ || (data->mode == THERMAL_DEVICE_DISABLED)) + return -EDOM; + + /* thermal framework should take care of data->mask & (1 << trip) */ +@@ -432,6 +469,9 @@ thermal_zone_of_add_sensor(struct device + if (ops->set_emul_temp) + tzd->ops->set_emul_temp = of_thermal_set_emul_temp; + ++ if (ops->set_trip_activate) ++ tzd->ops->set_trip_activate = of_thermal_activate_trip_type; ++ + mutex_unlock(&tzd->lock); + + return tzd; +@@ -726,7 +766,10 @@ static const char * const trip_types[] = + [THERMAL_TRIP_ACTIVE] = "active", + [THERMAL_TRIP_PASSIVE] = "passive", + [THERMAL_TRIP_HOT] = "hot", +- [THERMAL_TRIP_CRITICAL] = "critical", ++ [THERMAL_TRIP_CRITICAL] = "critical_high", ++ [THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi", ++ [THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo", ++ [THERMAL_TRIP_CRITICAL_LOW] = "critical_low", + }; + + /** +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -31,7 +31,7 @@ static int tsens_get_temp(void *data, in + + static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend) + { +- const struct tsens_sensor *s = p; ++ struct tsens_sensor *s = p; + struct tsens_device *tmdev = s->tmdev; + + if (tmdev->ops->get_trend) +@@ -40,9 +40,10 @@ static int tsens_get_trend(void *p, int + return -ENOTSUPP; + } + +-static int __maybe_unused tsens_suspend(struct device *dev) ++static int __maybe_unused tsens_suspend(void *data) + { +- struct tsens_device *tmdev = dev_get_drvdata(dev); ++ struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; + + if (tmdev->ops && tmdev->ops->suspend) + return tmdev->ops->suspend(tmdev); +@@ -50,9 +51,10 @@ static int __maybe_unused tsens_suspend + return 0; + } + +-static int __maybe_unused tsens_resume(struct device *dev) ++static int __maybe_unused tsens_resume(void *data) + { +- struct tsens_device *tmdev = dev_get_drvdata(dev); ++ struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; + + if (tmdev->ops && tmdev->ops->resume) + return tmdev->ops->resume(tmdev); +@@ -60,6 +62,30 @@ static int __maybe_unused tsens_resume(s + return 0; + } + ++static int __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp) ++{ ++ struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ ++ if (tmdev->ops && tmdev->ops->set_trip_temp) ++ return tmdev->ops->set_trip_temp(s, trip, temp); ++ ++ return 0; ++} ++ ++static int __maybe_unused tsens_activate_trip_type(void *data, int trip, ++ enum thermal_trip_activation_mode mode) ++{ ++ struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ ++ if (tmdev->ops && tmdev->ops->set_trip_activate) ++ return tmdev->ops->set_trip_activate(s, trip, mode); ++ ++ return 0; ++} ++ ++ + static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); + + static const struct of_device_id tsens_table[] = { +@@ -83,6 +109,8 @@ MODULE_DEVICE_TABLE(of, tsens_table); + static const struct thermal_zone_of_device_ops tsens_of_ops = { + .get_temp = tsens_get_temp, + .get_trend = tsens_get_trend, ++ .set_trip_temp = tsens_set_trip_temp, ++ .set_trip_activate = tsens_activate_trip_type, + }; + + static int tsens_register(struct tsens_device *tmdev) +@@ -131,7 +159,7 @@ static int tsens_probe(struct platform_d + if (id) + data = id->data; + else +- data = &data_8960; ++ return -EINVAL; + + if (data->num_sensors <= 0) { + dev_err(dev, "invalid number of sensors\n"); +@@ -146,6 +174,9 @@ static int tsens_probe(struct platform_d + tmdev->dev = dev; + tmdev->num_sensors = data->num_sensors; + tmdev->ops = data->ops; ++ ++ tmdev->tsens_irq = platform_get_irq(pdev, 0); ++ + for (i = 0; i < tmdev->num_sensors; i++) { + if (data->hw_ids) + tmdev->sensor[i].hw_id = data->hw_ids[i]; +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -24,9 +24,12 @@ struct tsens_device; + struct tsens_sensor { + struct tsens_device *tmdev; + struct thermal_zone_device *tzd; ++ struct work_struct notify_work; + int offset; + int id; + int hw_id; ++ int calib_data; ++ int calib_data_backup; + int slope; + u32 status; + }; +@@ -41,6 +44,9 @@ struct tsens_sensor { + * @suspend: Function to suspend the tsens device + * @resume: Function to resume the tsens device + * @get_trend: Function to get the thermal/temp trend ++ * @set_trip_temp: Function to set trip temp ++ * @get_trip_temp: Function to get trip temp ++ * @set_trip_activate: Function to activate trip points + */ + struct tsens_ops { + /* mandatory callbacks */ +@@ -53,6 +59,9 @@ struct tsens_ops { + int (*suspend)(struct tsens_device *); + int (*resume)(struct tsens_device *); + int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); ++ int (*set_trip_temp)(void *, int, int); ++ int (*set_trip_activate)(void *, int, ++ enum thermal_trip_activation_mode); + }; + + /** +@@ -76,11 +85,13 @@ struct tsens_context { + struct tsens_device { + struct device *dev; + u32 num_sensors; ++ u32 tsens_irq; + struct regmap *map; + struct regmap_field *status_field; + struct tsens_context ctx; + bool trdy; + const struct tsens_ops *ops; ++ struct work_struct tsens_work; + struct tsens_sensor sensor[0]; + }; + +--- a/drivers/thermal/thermal_core.c ++++ b/drivers/thermal/thermal_core.c +@@ -673,7 +673,7 @@ void thermal_zone_device_unbind_exceptio + */ + int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + int trip, +- struct thermal_cooling_device *cdev, ++ struct thermal_cooling_device *cdev, + unsigned long upper, unsigned long lower, + unsigned int weight) + { +--- a/drivers/thermal/thermal_sysfs.c ++++ b/drivers/thermal/thermal_sysfs.c +@@ -115,12 +115,48 @@ trip_point_type_show(struct device *dev, + return sprintf(buf, "passive\n"); + case THERMAL_TRIP_ACTIVE: + return sprintf(buf, "active\n"); ++ case THERMAL_TRIP_CONFIGURABLE_HI: ++ return sprintf(buf, "configurable_hi\n"); ++ case THERMAL_TRIP_CONFIGURABLE_LOW: ++ return sprintf(buf, "configurable_low\n"); ++ case THERMAL_TRIP_CRITICAL_LOW: ++ return sprintf(buf, "critical_low\n"); + default: + return sprintf(buf, "unknown\n"); + } + } + + static ssize_t ++trip_point_type_activate(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct thermal_zone_device *tz = to_thermal_zone(dev); ++ int trip, ret; ++ char *enabled = "enabled"; ++ char *disabled = "disabled"; ++ ++ if (!tz->ops->set_trip_activate) ++ return -EPERM; ++ ++ if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) ++ return -EINVAL; ++ ++ if (!strncmp(buf, enabled, strlen(enabled))) ++ ret = tz->ops->set_trip_activate(tz, trip, ++ THERMAL_TRIP_ACTIVATION_ENABLED); ++ else if (!strncmp(buf, disabled, strlen(disabled))) ++ ret = tz->ops->set_trip_activate(tz, trip, ++ THERMAL_TRIP_ACTIVATION_DISABLED); ++ else ++ ret = -EINVAL; ++ ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t + trip_point_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) + { +@@ -562,6 +598,12 @@ static int create_trip_attrs(struct ther + tz->trip_type_attrs[indx].attr.show = trip_point_type_show; + attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; + ++ if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) { ++ tz->trip_type_attrs[indx].attr.store ++ = trip_point_type_activate; ++ tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR; ++ } ++ + /* create trip temp attribute */ + snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", indx); +--- a/include/linux/thermal.h ++++ b/include/linux/thermal.h +@@ -78,11 +78,19 @@ enum thermal_device_mode { + THERMAL_DEVICE_ENABLED, + }; + ++enum thermal_trip_activation_mode { ++ THERMAL_TRIP_ACTIVATION_DISABLED = 0, ++ THERMAL_TRIP_ACTIVATION_ENABLED, ++}; ++ + enum thermal_trip_type { + THERMAL_TRIP_ACTIVE = 0, + THERMAL_TRIP_PASSIVE, + THERMAL_TRIP_HOT, + THERMAL_TRIP_CRITICAL, ++ THERMAL_TRIP_CONFIGURABLE_HI, ++ THERMAL_TRIP_CONFIGURABLE_LOW, ++ THERMAL_TRIP_CRITICAL_LOW, + }; + + enum thermal_trend { +@@ -120,6 +128,8 @@ struct thermal_zone_device_ops { + enum thermal_trip_type *); + int (*get_trip_temp) (struct thermal_zone_device *, int, int *); + int (*set_trip_temp) (struct thermal_zone_device *, int, int); ++ int (*set_trip_activate) (struct thermal_zone_device *, int, ++ enum thermal_trip_activation_mode); + int (*get_trip_hyst) (struct thermal_zone_device *, int, int *); + int (*set_trip_hyst) (struct thermal_zone_device *, int, int); + int (*get_crit_temp) (struct thermal_zone_device *, int *); +@@ -363,6 +373,8 @@ struct thermal_genl_event { + * temperature. + * @set_trip_temp: a pointer to a function that sets the trip temperature on + * hardware. ++ * @activate_trip_type: a pointer to a function to enable/disable trip ++ * temperature interrupts + */ + struct thermal_zone_of_device_ops { + int (*get_temp)(void *, int *); +@@ -370,6 +382,8 @@ struct thermal_zone_of_device_ops { + int (*set_trips)(void *, int, int); + int (*set_emul_temp)(void *, int); + int (*set_trip_temp)(void *, int, int); ++ int (*set_trip_activate)(void *, int, ++ enum thermal_trip_activation_mode); + }; + + /** diff --git a/target/linux/ipq806x/patches-4.14/0064-clk-clk-rpm-fixes.patch b/target/linux/ipq806x/patches-4.14/0064-clk-clk-rpm-fixes.patch new file mode 100644 index 000000000..167278a70 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0064-clk-clk-rpm-fixes.patch @@ -0,0 +1,93 @@ +From d30840e2b1cf79d90392e6051b0c0b6006d29d8b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:32:40 +0100 +Subject: [PATCH 64/69] clk: clk-rpm fixes + +Signed-off-by: John Crispin +--- + .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + + drivers/clk/qcom/clk-rpm.c | 35 ++++++++++++++++++++++ + include/dt-bindings/clock/qcom,rpmcc.h | 4 +++ + 3 files changed, 40 insertions(+) + +--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -13,6 +13,7 @@ Required properties : + "qcom,rpmcc-msm8916", "qcom,rpmcc" + "qcom,rpmcc-msm8974", "qcom,rpmcc" + "qcom,rpmcc-apq8064", "qcom,rpmcc" ++ "qcom,rpmcc-ipq806x", "qcom,rpmcc" + + - #clock-cells : shall contain 1 + +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a + DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); + DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); + ++/* ipq806x */ ++DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); ++DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); ++ + static struct clk_rpm *apq8064_clks[] = { + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, +@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] = + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, + }; + ++static struct clk_rpm *ipq806x_clks[] = { ++ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, ++ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, ++ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, ++ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, ++ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, ++ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, ++ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, ++ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, ++ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, ++ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, ++ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, ++ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, ++ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, ++ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, ++ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, ++ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, ++}; ++ + static const struct rpm_clk_desc rpm_clk_apq8064 = { + .clks = apq8064_clks, + .num_clks = ARRAY_SIZE(apq8064_clks), + }; + ++static const struct rpm_clk_desc rpm_clk_ipq806x = { ++ .clks = ipq806x_clks, ++ .num_clks = ARRAY_SIZE(ipq806x_clks), ++}; ++ + static const struct of_device_id rpm_clk_match_table[] = { + { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, ++ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, + { } + }; + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); +--- a/include/dt-bindings/clock/qcom,rpmcc.h ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -37,6 +37,10 @@ + #define RPM_SYS_FABRIC_A_CLK 19 + #define RPM_SFPB_CLK 20 + #define RPM_SFPB_A_CLK 21 ++#define RPM_NSS_FABRIC_0_CLK 22 ++#define RPM_NSS_FABRIC_0_A_CLK 23 ++#define RPM_NSS_FABRIC_1_CLK 24 ++#define RPM_NSS_FABRIC_1_A_CLK 25 + + /* SMD RPM clocks */ + #define RPM_SMD_XO_CLK_SRC 0 diff --git a/target/linux/ipq806x/patches-4.14/0065-arm-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.14/0065-arm-override-compiler-flags.patch new file mode 100644 index 000000000..0d2a4274c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0065-arm-override-compiler-flags.patch @@ -0,0 +1,21 @@ +From 4d8e29642661397a339ac3485f212c6360445421 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:33:32 +0100 +Subject: [PATCH 65/69] arm: override compiler flags + +Signed-off-by: John Crispin +--- + arch/arm/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -67,7 +67,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i + # macro, but instead defines a whole series of macros which makes + # testing for a specific architecture or later rather impossible. + arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m +-arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) ++arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15 + arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) + # Only override the compiler option if ARMv6. The ARMv6K extensions are + # always available in ARMv7 diff --git a/target/linux/ipq806x/patches-4.14/0066-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.14/0066-GPIO-add-named-gpio-exports.patch new file mode 100644 index 000000000..1ac024b3f --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0066-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,166 @@ +From a37b0c9113647b2120cf1a18cfc46afdb3f1fccc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 66/69] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- + drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++++ + drivers/gpio/gpiolib-sysfs.c | 10 ++++++- + include/asm-generic/gpio.h | 6 ++++ + include/linux/gpio/consumer.h | 8 +++++ + 4 files changed, 91 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -23,6 +23,8 @@ + #include + #include + #include ++#include ++#include + + #include "gpiolib.h" + +@@ -505,3 +507,69 @@ void of_gpiochip_remove(struct gpio_chip + gpiochip_remove_pin_ranges(chip); + of_node_put(chip->of_node); + } ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int __init of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++}; ++ ++static int __init of_gpio_export_init(void) ++{ ++ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe); ++} ++device_initcall(of_gpio_export_init); +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -553,7 +553,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -615,6 +615,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -636,6 +638,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -451,6 +451,7 @@ struct gpio_desc *devm_fwnode_get_gpiod_ + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -458,6 +459,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { diff --git a/target/linux/ipq806x/patches-4.14/0067-generic-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/ipq806x/patches-4.14/0067-generic-Mangle-bootloader-s-kernel-arguments.patch new file mode 100644 index 000000000..65bb2ee04 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0067-generic-Mangle-bootloader-s-kernel-arguments.patch @@ -0,0 +1,189 @@ +From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001 +From: Adrian Panella +Date: Thu, 9 Mar 2017 09:37:17 +0100 +Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments + +The command-line arguments provided by the boot loader will be +appended to a new device tree property: bootloader-args. +If there is a property "append-rootblock" in DT under /chosen +and a root= option in bootloaders command line it will be parsed +and added to DT bootargs with the form: XX. +Only command line ATAG will be processed, the rest of the ATAGs +sent by bootloader will be ignored. +This is usefull in dual boot systems, to get the current root partition +without afecting the rest of the system. + +Signed-off-by: Adrian Panella +--- + arch/arm/Kconfig | 11 +++++ + arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++- + init/main.c | 16 ++++++++ + 3 files changed, 98 insertions(+), 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1938,6 +1938,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN + The command-line arguments provided by the boot loader will be + appended to the the device tree bootargs property. + ++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ bool "Append rootblock parsing bootloader's kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to a new device tree property: bootloader-args. ++ If there is a property "append-rootblock" in DT under /chosen ++ and a root= option in bootloaders command line it will be parsed ++ and added to DT bootargs with the form: XX. ++ Only command line ATAG will be processed, the rest of the ATAGs ++ sent by bootloader will be ignored. ++ + endchoice + + config CMDLINE +--- a/arch/arm/boot/compressed/atags_to_fdt.c ++++ b/arch/arm/boot/compressed/atags_to_fdt.c +@@ -4,6 +4,8 @@ + + #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) + #define do_extend_cmdline 1 ++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#define do_extend_cmdline 1 + #else + #define do_extend_cmdline 0 + #endif +@@ -67,6 +69,59 @@ static uint32_t get_cell_size(const void + return cell_size; + } + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ ++static char *append_rootblock(char *dest, const char *str, int len, void *fdt) ++{ ++ char *ptr, *end; ++ char *root="root="; ++ int i, l; ++ const char *rootblock; ++ ++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually ++ ptr = str - 1; ++ ++ do { ++ //first find an 'r' at the begining or after a space ++ do { ++ ptr++; ++ ptr = strchr(ptr, 'r'); ++ if(!ptr) return dest; ++ ++ } while (ptr != str && *(ptr-1) != ' '); ++ ++ //then check for the rest ++ for(i = 1; i <= 4; i++) ++ if(*(ptr+i) != *(root+i)) break; ++ ++ } while (i != 5); ++ ++ end = strchr(ptr, ' '); ++ end = end ? (end - 1) : (strchr(ptr, 0) - 1); ++ ++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) ++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); ++ ptr = end + 1; ++ ++ /* if append-rootblock property is set use it to append to command line */ ++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); ++ if(rootblock != NULL) { ++ if(*dest != ' ') { ++ *dest = ' '; ++ dest++; ++ len++; ++ } ++ if (len + l + i <= COMMAND_LINE_SIZE) { ++ memcpy(dest, rootblock, l); ++ dest += l - 1; ++ memcpy(dest, ptr, i); ++ dest += i; ++ } ++ } ++ return dest; ++} ++#endif ++ + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) + { + char cmdline[COMMAND_LINE_SIZE]; +@@ -86,12 +141,21 @@ static void merge_fdt_bootargs(void *fdt + + /* and append the ATAG_CMDLINE */ + if (fdt_cmdline) { ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //save original bootloader args ++ //and append ubi.mtd with root partition number to current cmdline ++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); ++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); ++ ++#else + len = strlen(fdt_cmdline); + if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { + *ptr++ = ' '; + memcpy(ptr, fdt_cmdline, len); + ptr += len; + } ++#endif + } + *ptr = '\0'; + +@@ -148,7 +212,9 @@ int atags_to_fdt(void *atag_list, void * + else + setprop_string(fdt, "/chosen", "bootargs", + atag->u.cmdline.cmdline); +- } else if (atag->hdr.tag == ATAG_MEM) { ++ } ++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ else if (atag->hdr.tag == ATAG_MEM) { + if (memcount >= sizeof(mem_reg_property)/4) + continue; + if (!atag->u.mem.size) +@@ -187,6 +253,10 @@ int atags_to_fdt(void *atag_list, void * + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } ++#else ++ ++ } ++#endif + + return fdt_pack(fdt); + } +--- a/init/main.c ++++ b/init/main.c +@@ -95,6 +95,10 @@ + #include + #include + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#include ++#endif ++ + static int kernel_init(void *); + + extern void init_IRQ(void); +@@ -570,6 +574,18 @@ asmlinkage __visible void __init start_k + page_alloc_init(); + + pr_notice("Kernel command line: %s\n", boot_command_line); ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //Show bootloader's original command line for reference ++ if(of_chosen) { ++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); ++ if(prop) ++ pr_notice("Bootloader command line (ignored): %s\n", prop); ++ else ++ pr_notice("Bootloader command line not present\n"); ++ } ++#endif ++ + parse_early_param(); + after_dashes = parse_args("Booting kernel", + static_command_line, __start___param, diff --git a/target/linux/ipq806x/patches-4.14/0068-spi-add-gpio-cs-support.patch b/target/linux/ipq806x/patches-4.14/0068-spi-add-gpio-cs-support.patch new file mode 100644 index 000000000..27dfff18c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0068-spi-add-gpio-cs-support.patch @@ -0,0 +1,50 @@ +From b9c998eb7735df8000cf48d77f9271823e8e73da Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +Date: Thu, 9 Mar 2017 09:39:05 +0100 +Subject: [PATCH 68/69] spi: add gpio cs support + +Signed-off-by: John Crispin +--- + drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #define QUP_CONFIG 0x0000 + #define QUP_STATE 0x0004 +@@ -965,6 +966,20 @@ static void spi_qup_set_cs(struct spi_de + writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL); + } + ++static int spi_qup_setup(struct spi_device *spi) ++{ ++ if (spi->cs_gpio >= 0) { ++ if (spi->mode & SPI_CS_HIGH) ++ gpio_set_value(spi->cs_gpio, 0); ++ else ++ gpio_set_value(spi->cs_gpio, 1); ++ ++ udelay(10); ++ } ++ ++ return 0; ++} ++ + static int spi_qup_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -1061,6 +1076,8 @@ static int spi_qup_probe(struct platform + + if (!controller->qup_v1) + master->set_cs = spi_qup_set_cs; ++ else ++ master->setup = spi_qup_setup; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); diff --git a/target/linux/ipq806x/patches-4.14/0069-arm-boot-add-dts-files.patch b/target/linux/ipq806x/patches-4.14/0069-arm-boot-add-dts-files.patch new file mode 100644 index 000000000..1d7ffec39 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0069-arm-boot-add-dts-files.patch @@ -0,0 +1,31 @@ +From 8f68331e14dff9a101f2d0e1d6bec84a031f27ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 11:03:18 +0100 +Subject: [PATCH 69/69] arm: boot: add dts files + +Signed-off-by: John Crispin +--- + arch/arm/boot/dts/Makefile | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -699,7 +699,18 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8084-mtp.dtb \ + qcom-ipq4019-ap.dk01.1-c1.dtb \ + qcom-ipq4019-ap.dk04.1-c1.dtb \ ++ qcom-ipq4019-fritz4040.dtb \ ++ qcom-ipq4019-nbg6617.dtb \ ++ qcom-ipq4019-rt-ac58u.dtb \ + qcom-ipq8064-ap148.dtb \ ++ qcom-ipq8064-c2600.dtb \ ++ qcom-ipq8064-d7800.dtb \ ++ qcom-ipq8064-db149.dtb \ ++ qcom-ipq8064-ea8500.dtb \ ++ qcom-ipq8064-r7500.dtb \ ++ qcom-ipq8064-r7500v2.dtb \ ++ qcom-ipq8065-nbg6817.dtb \ ++ qcom-ipq8065-r7800.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-lge-nexus5-hammerhead.dtb \ diff --git a/target/linux/ipq806x/patches-4.14/0070-qcom-spm-fix-probe-order.patch b/target/linux/ipq806x/patches-4.14/0070-qcom-spm-fix-probe-order.patch new file mode 100644 index 000000000..b7e375dfb --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0070-qcom-spm-fix-probe-order.patch @@ -0,0 +1,16 @@ +Check for SCM availability before attempting to use SPM + +Signed-off-by: Felix Fietkau + +--- a/drivers/soc/qcom/spm.c ++++ b/drivers/soc/qcom/spm.c +@@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(stru + cpumask_t mask; + bool use_scm_power_down = false; + ++ if (!qcom_scm_is_available()) ++ return -EPROBE_DEFER; ++ + for (i = 0; ; i++) { + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + if (!state_node) diff --git a/target/linux/ipq806x/patches-4.14/0071-1-PCI-qcom-Fixed-IPQ806x-specific-clocks.patch b/target/linux/ipq806x/patches-4.14/0071-1-PCI-qcom-Fixed-IPQ806x-specific-clocks.patch new file mode 100644 index 000000000..1c66092e1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-1-PCI-qcom-Fixed-IPQ806x-specific-clocks.patch @@ -0,0 +1,99 @@ +From 86655aa14304ca88a8ce8847276147dbc1a83238 Mon Sep 17 00:00:00 2001 +From: Sham Muthayyan +Date: Tue, 19 Jul 2016 18:44:49 +0530 +Subject: PCI: qcom: Fixed IPQ806x specific clocks + +Change-Id: I488e1bc707d6a22b37a338f41935e3922009ba5e +Signed-off-by: Sham Muthayyan +--- + drivers/pci/dwct/pcie-qcom.c | 38 +++++++++++++++++++++++++++++++++----- + 1 file changed, 33 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index ce7ba5b..9548b05 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -91,6 +91,8 @@ struct qcom_pcie_resources_2_1_0 { + struct clk *iface_clk; + struct clk *core_clk; + struct clk *phy_clk; ++ struct clk *aux_clk; ++ struct clk *ref_clk; + struct reset_control *pci_reset; + struct reset_control *axi_reset; + struct reset_control *ahb_reset; +@@ -249,6 +251,14 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) + if (IS_ERR(res->phy_clk)) + return PTR_ERR(res->phy_clk); + ++ res->aux_clk = devm_clk_get(dev, "aux"); ++ if (IS_ERR(res->aux_clk)) ++ return PTR_ERR(res->aux_clk); ++ ++ res->ref_clk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(res->ref_clk)) ++ return PTR_ERR(res->ref_clk); ++ + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); +@@ -281,6 +291,8 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) + clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->core_clk); + clk_disable_unprepare(res->phy_clk); ++ clk_disable_unprepare(res->aux_clk); ++ clk_disable_unprepare(res->ref_clk); + regulator_disable(res->vdda); + regulator_disable(res->vdda_phy); + regulator_disable(res->vdda_refclk); +@@ -324,18 +336,30 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + goto err_assert_ahb; + } + ++ ret = clk_prepare_enable(res->core_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable core clock\n"); ++ goto err_clk_core; ++ } ++ + ret = clk_prepare_enable(res->phy_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable phy clock\n"); + goto err_clk_phy; + } + +- ret = clk_prepare_enable(res->core_clk); ++ ret = clk_prepare_enable(res->aux_clk); + if (ret) { +- dev_err(dev, "cannot prepare/enable core clock\n"); +- goto err_clk_core; ++ dev_err(dev, "cannot prepare/enable aux clock\n"); ++ goto err_clk_aux; + } + ++ ret = clk_prepare_enable(res->ref_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable ref clock\n"); ++ goto err_clk_ref; ++ } ++ + ret = reset_control_deassert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot deassert ahb reset\n"); +@@ -389,10 +413,14 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + return 0; + + err_deassert_ahb: +- clk_disable_unprepare(res->core_clk); +-err_clk_core: ++ clk_disable_unprepare(res->ref_clk); ++err_clk_ref: ++ clk_disable_unprepare(res->aux_clk); ++err_clk_aux: + clk_disable_unprepare(res->phy_clk); + err_clk_phy: ++ clk_disable_unprepare(res->core_clk); ++err_clk_core: + clk_disable_unprepare(res->iface_clk); + err_assert_ahb: + regulator_disable(res->vdda_phy); diff --git a/target/linux/ipq806x/patches-4.14/0071-2-PCI-qcom-Fixed-IPQ806x-PCIE-reset-changes.patch b/target/linux/ipq806x/patches-4.14/0071-2-PCI-qcom-Fixed-IPQ806x-PCIE-reset-changes.patch new file mode 100644 index 000000000..7facc89cc --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-2-PCI-qcom-Fixed-IPQ806x-PCIE-reset-changes.patch @@ -0,0 +1,92 @@ +From 490d103232287eb51c92c49a4ef8865fd0a9d59e Mon Sep 17 00:00:00 2001 +From: Sham Muthayyan +Date: Tue, 19 Jul 2016 18:58:18 +0530 +Subject: PCI: qcom: Fixed IPQ806x PCIE reset changes + +Change-Id: Ia6590e960b9754b1e8b7a51f318788cd63e9e321 +Signed-off-by: Sham Muthayyan +--- + drivers/pci/dwc/pcie-qcom.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index 9548b05..3e4148e 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -98,6 +98,7 @@ struct qcom_pcie_resources_2_1_0 { + struct reset_control *ahb_reset; + struct reset_control *por_reset; + struct reset_control *phy_reset; ++ struct reset_control *ext_reset; + struct regulator *vdda; + struct regulator *vdda_phy; + struct regulator *vdda_refclk; +@@ -276,7 +277,14 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) + return PTR_ERR(res->por_reset); + + res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); +- return PTR_ERR_OR_ZERO(res->phy_reset); ++ if (IS_ERR(res->phy_reset)) ++ return PTR_ERR(res->phy_reset); ++ ++ res->ext_reset = devm_reset_control_get_exclusive(dev, "ext"); ++ if (IS_ERR(res->ext_reset)) ++ return PTR_ERR(res->ext_reset); ++ ++ return 0; + } + + static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) +@@ -288,6 +296,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) + reset_control_assert(res->ahb_reset); + reset_control_assert(res->por_reset); + reset_control_assert(res->pci_reset); ++ reset_control_assert(res->ext_reset); + clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->core_clk); + clk_disable_unprepare(res->phy_clk); +@@ -306,6 +315,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + u32 val; + int ret; + ++ ret = reset_control_assert(res->ahb_reset); ++ if (ret) { ++ dev_err(dev, "cannot assert ahb reset\n"); ++ return ret; ++ } ++ + ret = regulator_enable(res->vdda); + if (ret) { + dev_err(dev, "cannot enable vdda regulator\n"); +@@ -324,16 +339,16 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + goto err_vdda_phy; + } + +- ret = reset_control_assert(res->ahb_reset); ++ ret = reset_control_deassert(res->ext_reset); + if (ret) { +- dev_err(dev, "cannot assert ahb reset\n"); +- goto err_assert_ahb; ++ dev_err(dev, "cannot assert ext reset\n"); ++ goto err_reset_ext; + } + + ret = clk_prepare_enable(res->iface_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable iface clock\n"); +- goto err_assert_ahb; ++ goto err_iface; + } + + ret = clk_prepare_enable(res->core_clk); +@@ -422,7 +437,9 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + clk_disable_unprepare(res->core_clk); + err_clk_core: + clk_disable_unprepare(res->iface_clk); +-err_assert_ahb: ++err_iface: ++ reset_control_assert(res->ext_reset); ++err_reset_ext: + regulator_disable(res->vdda_phy); + err_vdda_phy: + regulator_disable(res->vdda_refclk); diff --git a/target/linux/ipq806x/patches-4.14/0071-3-PCI-qcom-Fixed-IPQ806x-PCIE-init-changes.patch b/target/linux/ipq806x/patches-4.14/0071-3-PCI-qcom-Fixed-IPQ806x-PCIE-init-changes.patch new file mode 100644 index 000000000..caa26b475 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-3-PCI-qcom-Fixed-IPQ806x-PCIE-init-changes.patch @@ -0,0 +1,121 @@ +From eddd13215d0f2b549ebc5f0e8796d5b1231f90a0 Mon Sep 17 00:00:00 2001 +From: Sham Muthayyan +Date: Tue, 19 Jul 2016 19:58:22 +0530 +Subject: PCI: qcom: Fixed IPQ806x PCIE init changes + +Change-Id: Ic319b1aec27a47809284759f8fcb6a8815b7cf7e +Signed-off-by: Sham Muthayyan +--- + drivers/pci/dwc/pcie-qcom.c | 62 +++++++++++++++++++++++++++++++++++++------- + 1 file changed, 53 insertions(+), 9 deletions(-) + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index 3e4148e..5110776 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -52,7 +52,13 @@ + #define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 + + #define PCIE20_PARF_PHY_CTRL 0x40 ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16) ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16) ++ + #define PCIE20_PARF_PHY_REFCLK 0x4C ++#define REF_SSP_EN BIT(16) ++#define REF_USE_PAD BIT(12) ++ + #define PCIE20_PARF_DBI_BASE_ADDR 0x168 + #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C + #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 +@@ -84,6 +90,19 @@ + + #define PERST_DELAY_US 1000 + ++/* PARF registers */ ++#define PCIE20_PARF_PCS_DEEMPH 0x34 ++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16) ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8) ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0) ++ ++#define PCIE20_PARF_PCS_SWING 0x38 ++#define PCS_SWING_TX_SWING_FULL(x) (x << 8) ++#define PCS_SWING_TX_SWING_LOW(x) (x << 0) ++ ++#define PCIE20_PARF_CONFIG_BITS 0x50 ++#define PHY_RX0_EQ(x) (x << 24) ++ + #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 + #define SLV_ADDR_SPACE_SZ 0x10000000 + +@@ -102,6 +121,7 @@ struct qcom_pcie_resources_2_1_0 { + struct regulator *vdda; + struct regulator *vdda_phy; + struct regulator *vdda_refclk; ++ uint8_t phy_tx0_term_offset; + }; + + struct qcom_pcie_resources_1_0_0 { +@@ -179,6 +199,16 @@ struct qcom_pcie { + + #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) + ++static inline void ++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) ++{ ++ u32 val = readl(addr); ++ ++ val &= ~clear_mask; ++ val |= set_mask; ++ writel(val, addr); ++} ++ + static void qcom_ep_reset_assert(struct qcom_pcie *pcie) + { + gpiod_set_value_cansleep(pcie->reset, 1); +@@ -284,6 +314,10 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) + if (IS_ERR(res->ext_reset)) + return PTR_ERR(res->ext_reset); + ++ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset", ++ &res->phy_tx0_term_offset)) ++ res->phy_tx0_term_offset = 0; ++ + return 0; + } + +@@ -381,15 +415,27 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + goto err_deassert_ahb; + } + +- /* enable PCIe clocks and resets */ +- val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); +- val &= ~BIT(0); +- writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* Set Tx termination offset */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, ++ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK, ++ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset)); ++ ++ /* PARF programming */ ++ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | ++ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | ++ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), ++ pcie->parf + PCIE20_PARF_PCS_DEEMPH); ++ writel(PCS_SWING_TX_SWING_FULL(0x78) | ++ PCS_SWING_TX_SWING_LOW(0x78), ++ pcie->parf + PCIE20_PARF_PCS_SWING); ++ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS); ++ ++ /* Enable reference clock */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, ++ REF_USE_PAD, REF_SSP_EN); + +- /* enable external reference clock */ +- val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); +- val |= BIT(16); +- writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); + + ret = reset_control_deassert(res->phy_reset); + if (ret) { diff --git a/target/linux/ipq806x/patches-4.14/0071-5-PCI-qcom-Programming-the-PCIE-iATU-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.14/0071-5-PCI-qcom-Programming-the-PCIE-iATU-for-IPQ806x.patch new file mode 100644 index 000000000..e5917a418 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-5-PCI-qcom-Programming-the-PCIE-iATU-for-IPQ806x.patch @@ -0,0 +1,118 @@ +From d27c303e828d7e42f339a459d2abfe30c51698e9 Mon Sep 17 00:00:00 2001 +From: Sham Muthayyan +Date: Tue, 26 Jul 2016 12:28:31 +0530 +Subject: PCI: qcom: Programming the PCIE iATU for IPQ806x + +Resolved PCIE EP detection errors caused due to missing iATU programming. + +Change-Id: Ie95c0f8cb940abc0192a8a3c4e825ddba54b72fe +Signed-off-by: Sham Muthayyan +--- + drivers/pci/dwc/pcie-qcom.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 77 insertions(+) + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index 5110776..487aa34 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -83,6 +83,30 @@ + #define PCIE20_CAP_LINK_1 (PCIE20_CAP + 0x14) + #define PCIE_CAP_LINK1_VAL 0x2FD7F + ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c ++ ++#define PCIE20_PLR_IATU_VIEWPORT 0x900 ++#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31) ++#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0) ++ ++#define PCIE20_PLR_IATU_CTRL1 0x904 ++#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0) ++#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0) ++ ++#define PCIE20_PLR_IATU_CTRL2 0x908 ++#define PCIE20_PLR_IATU_ENABLE BIT(31) ++ ++#define PCIE20_PLR_IATU_LBAR 0x90C ++#define PCIE20_PLR_IATU_UBAR 0x910 ++#define PCIE20_PLR_IATU_LAR 0x914 ++#define PCIE20_PLR_IATU_LTAR 0x918 ++#define PCIE20_PLR_IATU_UTAR 0x91c ++ ++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 ++ + #define PCIE20_PARF_Q2A_FLUSH 0x1AC + + #define PCIE20_MISC_CONTROL_1_REG 0x8BC +@@ -252,6 +276,59 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) + writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); + } + ++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) ++{ ++ struct dw_pcie *pci = pcie->pci; ++ struct pcie_port *pp = &pci->pp; ++ ++ /* ++ * program and enable address translation region 0 (device config ++ * address space); region type config; ++ * axi config address range to device config address range ++ */ ++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | ++ PCIE20_PLR_IATU_REGION_INDEX(0), ++ pci->dbi_base + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_CFG0, pci->dbi_base + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pci->dbi_base + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->cfg0_base, pci->dbi_base + PCIE20_PLR_IATU_LBAR); ++ writel((pp->cfg0_base >> 32), pci->dbi_base + PCIE20_PLR_IATU_UBAR); ++ writel((pp->cfg0_base + pp->cfg0_size - 1), ++ pci->dbi_base + PCIE20_PLR_IATU_LAR); ++ writel(busdev, pci->dbi_base + PCIE20_PLR_IATU_LTAR); ++ writel(0, pci->dbi_base + PCIE20_PLR_IATU_UTAR); ++} ++ ++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) ++{ ++ struct dw_pcie *pci = pcie->pci; ++ struct pcie_port *pp = &pci->pp; ++ ++ /* ++ * program and enable address translation region 2 (device resource ++ * address space); region type memory; ++ * axi device bar address range to device bar address range ++ */ ++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | ++ PCIE20_PLR_IATU_REGION_INDEX(2), ++ pci->dbi_base + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_MEM, pci->dbi_base + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pci->dbi_base + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->mem_base, pci->dbi_base + PCIE20_PLR_IATU_LBAR); ++ writel((pp->mem_base >> 32), pci->dbi_base + PCIE20_PLR_IATU_UBAR); ++ writel(pp->mem_base + pp->mem_size - 1, ++ pci->dbi_base + PCIE20_PLR_IATU_LAR); ++ writel(pp->mem_bus_addr, pci->dbi_base + PCIE20_PLR_IATU_LTAR); ++ writel(upper_32_bits(pp->mem_bus_addr), ++ pci->dbi_base + PCIE20_PLR_IATU_UTAR); ++ ++ /* 256B PCIE buffer setting */ ++ writel(0x1, pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); ++ writel(0x1, pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); ++} ++ + static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) + { + struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; +@@ -471,6 +548,9 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + writel(CFG_BRIDGE_SB_INIT, + pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); + ++ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); ++ qcom_pcie_prog_viewport_mem2_outbound(pcie); ++ + return 0; + + err_deassert_ahb: diff --git a/target/linux/ipq806x/patches-4.14/0071-6-PCI-qcom-Force-GEN1-support.patch b/target/linux/ipq806x/patches-4.14/0071-6-PCI-qcom-Force-GEN1-support.patch new file mode 100644 index 000000000..7a5d9e79e --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-6-PCI-qcom-Force-GEN1-support.patch @@ -0,0 +1,64 @@ +From 4910cfd150342ec7b038892262923c725a9c4001 Mon Sep 17 00:00:00 2001 +From: Sham Muthayyan +Date: Wed, 7 Sep 2016 16:44:28 +0530 +Subject: PCI: qcom: Force GEN1 support + +Change-Id: Ica54ddb737d7b851469deab1745f54bf431bd3f0 +Signed-off-by: Sham Muthayyan +--- + drivers/pci/dwc/pcie-qcom.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index 487aa34..5baa48f 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -85,6 +85,8 @@ + + #define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) + ++#define PCIE20_LNK_CONTROL2_LINK_STATUS2 0xA0 ++ + #define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 + #define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c + +@@ -219,6 +221,7 @@ struct qcom_pcie { + struct phy *phy; + struct gpio_desc *reset; + struct qcom_pcie_ops *ops; ++ uint32_t force_gen1; + }; + + #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) +@@ -548,6 +551,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) + writel(CFG_BRIDGE_SB_INIT, + pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); + ++ if (pcie->force_gen1) { ++ writel_relaxed((readl_relaxed( ++ pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2) | 1), ++ pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); ++ } ++ + qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); + qcom_pcie_prog_viewport_mem2_outbound(pcie); + +@@ -1390,6 +1399,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) + struct dw_pcie *pci; + struct qcom_pcie *pcie; + int ret; ++ uint32_t force_gen1 = 0; ++ struct device_node *np = pdev->dev.of_node; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) +@@ -1411,6 +1422,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) + if (IS_ERR(pcie->reset)) + return PTR_ERR(pcie->reset); + ++ of_property_read_u32(np, "force_gen1", &force_gen1); ++ pcie->force_gen1 = force_gen1; ++ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); + pcie->parf = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->parf)) diff --git a/target/linux/ipq806x/patches-4.14/0071-7-pcie-Set-PCIE-MRRS-and-MPS-to-256B.patch b/target/linux/ipq806x/patches-4.14/0071-7-pcie-Set-PCIE-MRRS-and-MPS-to-256B.patch new file mode 100644 index 000000000..0bf83b092 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-7-pcie-Set-PCIE-MRRS-and-MPS-to-256B.patch @@ -0,0 +1,71 @@ +From edff8f777c6321ca89bb950a382f409c4a126e28 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Thu, 15 Dec 2016 17:38:18 +0530 +Subject: pcie: Set PCIE MRRS and MPS to 256B + +Set Max Read Request Size and Max Payload Size to 256 bytes, +per chip team recommendation. + +Change-Id: I097004be2ced1b3096ffc10c318aae0b2bb155e8 +Signed-off-by: Gokul Sriram Palanisamy +--- + drivers/pci/dwc/pcie-qcom.c | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +(limited to 'drivers/pci/host/pcie-qcom.c') + +diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c +index 5baa48f..7c694bb 100644 +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -132,6 +132,14 @@ + #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 + #define SLV_ADDR_SPACE_SZ 0x10000000 + ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++#define __mask(a, b) (((1 << ((a) + 1)) - 1) & ~((1 << (b)) - 1)) ++#define PCIE20_DEV_CAS 0x78 ++#define PCIE20_MRRS_MASK __mask(14, 12) ++#define PCIE20_MRRS(x) __set(x, 14, 12) ++#define PCIE20_MPS_MASK __mask(7, 5) ++#define PCIE20_MPS(x) __set(x, 7, 5) ++ + struct qcom_pcie_resources_2_1_0 { + struct clk *iface_clk; + struct clk *core_clk; +@@ -1481,6 +1489,35 @@ static int qcom_pcie_probe(struct platform_device *pdev) + return 0; + } + ++static void qcom_pcie_fixup_final(struct pci_dev *dev) ++{ ++ int cap, err; ++ u16 ctl, reg_val; ++ ++ cap = pci_pcie_cap(dev); ++ if (!cap) ++ return; ++ ++ err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl); ++ ++ if (err) ++ return; ++ ++ reg_val = ctl; ++ ++ if (((reg_val & PCIE20_MRRS_MASK) >> 12) > 1) ++ reg_val = (reg_val & ~(PCIE20_MRRS_MASK)) | PCIE20_MRRS(0x1); ++ ++ if (((ctl & PCIE20_MPS_MASK) >> 5) > 1) ++ reg_val = (reg_val & ~(PCIE20_MPS_MASK)) | PCIE20_MPS(0x1); ++ ++ err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, reg_val); ++ ++ if (err) ++ pr_err("pcie config write failed %d\n", err); ++} ++DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, qcom_pcie_fixup_final); ++ + static const struct of_device_id qcom_pcie_match[] = { + { .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 }, + { .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, diff --git a/target/linux/ipq806x/patches-4.14/0071-8-pcie-qcom-Fixed-pcie_phy_clk-branch-issue.patch b/target/linux/ipq806x/patches-4.14/0071-8-pcie-qcom-Fixed-pcie_phy_clk-branch-issue.patch new file mode 100644 index 000000000..6b27ebbad --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-8-pcie-qcom-Fixed-pcie_phy_clk-branch-issue.patch @@ -0,0 +1,91 @@ +From b74bab6186131eea09459eedf5d737645a3559c9 Mon Sep 17 00:00:00 2001 +From: Abhishek Sahu +Date: Thu, 22 Dec 2016 11:18:45 +0530 +Subject: pcie: qcom: Fixed pcie_phy_clk branch issue + +Following backtraces are observed in PCIe deinit operation. + + Hardware name: Qualcomm (Flattened Device Tree) + (unwind_backtrace) from [] (show_stack+0x10/0x14) + (show_stack) from [] (dump_stack+0x84/0x98) + (dump_stack) from [] (warn_slowpath_common+0x9c/0xb8) + (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) + (warn_slowpath_fmt) from [] (clk_branch_wait+0x114/0x120) + (clk_branch_wait) from [] (clk_core_disable+0xd0/0x1f4) + (clk_core_disable) from [] (clk_disable+0x24/0x30) + (clk_disable) from [] (qcom_pcie_deinit_v0+0x6c/0xb8) + (qcom_pcie_deinit_v0) from [] (qcom_pcie_host_init+0xe0/0xe8) + (qcom_pcie_host_init) from [] (dw_pcie_host_init+0x3b0/0x538) + (dw_pcie_host_init) from [] (qcom_pcie_probe+0x20c/0x2e4) + +pcie_phy_clk is generated for PCIe controller itself and the +GCC controls its branch operation. This error is coming since +the assert operations turn off the parent clock before branch +clock. Now this patch moves clk_disable_unprepare before assert +operations. + +Similarly, during probe function, the clock branch operation +should be done after dessert operation. Currently, it does not +generate any error since bootloader enables the pcie_phy_clk +but the same error is coming during probe, if bootloader +disables pcie_phy_clk. + +Change-Id: Ib29c154d10eb64363d9cc982ce5fd8107af5627d +Signed-off-by: Abhishek Sahu +--- + drivers/pci/dwc/pcie-qcom.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -352,6 +352,7 @@ static void qcom_pcie_deinit_v0(struct q + { + struct qcom_pcie_resources_v0 *res = &pcie->res.v0; + ++ clk_disable_unprepare(res->phy_clk); + reset_control_assert(res->pci_reset); + reset_control_assert(res->axi_reset); + reset_control_assert(res->ahb_reset); +@@ -360,7 +361,6 @@ static void qcom_pcie_deinit_v0(struct q + reset_control_assert(res->ext_reset); + clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->core_clk); +- clk_disable_unprepare(res->phy_clk); + clk_disable_unprepare(res->aux_clk); + clk_disable_unprepare(res->ref_clk); + regulator_disable(res->vdda); +@@ -416,12 +416,6 @@ static int qcom_pcie_init_v0(struct qcom + goto err_clk_core; + } + +- ret = clk_prepare_enable(res->phy_clk); +- if (ret) { +- dev_err(dev, "cannot prepare/enable phy clock\n"); +- goto err_clk_phy; +- } +- + ret = clk_prepare_enable(res->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clock\n"); +@@ -486,6 +480,12 @@ static int qcom_pcie_init_v0(struct qcom + return ret; + } + ++ ret = clk_prepare_enable(res->phy_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable phy clock\n"); ++ goto err_deassert_ahb; ++ } ++ + /* wait for clock acquisition */ + usleep_range(1000, 1500); + if (pcie->force_gen1) { +@@ -504,8 +504,6 @@ err_deassert_ahb: + err_clk_ref: + clk_disable_unprepare(res->aux_clk); + err_clk_aux: +- clk_disable_unprepare(res->phy_clk); +-err_clk_phy: + clk_disable_unprepare(res->core_clk); + err_clk_core: + clk_disable_unprepare(res->iface_clk); diff --git a/target/linux/ipq806x/patches-4.14/0071-9-pcie-qcom-change-duplicate-pci-reset-to-phy-reset.patch b/target/linux/ipq806x/patches-4.14/0071-9-pcie-qcom-change-duplicate-pci-reset-to-phy-reset.patch new file mode 100644 index 000000000..f0df1cd3c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0071-9-pcie-qcom-change-duplicate-pci-reset-to-phy-reset.patch @@ -0,0 +1,25 @@ +From 1a9c48123bd09f75562b6a2ee0f0a7b2d533cd45 Mon Sep 17 00:00:00 2001 +From: Abhishek Sahu +Date: Thu, 22 Dec 2016 11:50:49 +0530 +Subject: pcie: qcom: change duplicate pci reset to phy reset + +The deinit issues reset_control_assert for pci twice and +does not contain phy reset. + +Change-Id: Iba849963c7e5f9a2a1063f0e2e89635df70b8a99 +Signed-off-by: Abhishek Sahu +--- + drivers/pci/dwc/pcie-qcom.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pci/dwc/pcie-qcom.c ++++ b/drivers/pci/dwc/pcie-qcom.c +@@ -353,7 +353,7 @@ static void qcom_pcie_deinit_v0(struct q + struct qcom_pcie_resources_v0 *res = &pcie->res.v0; + + clk_disable_unprepare(res->phy_clk); +- reset_control_assert(res->pci_reset); ++ reset_control_assert(res->phy_reset); + reset_control_assert(res->axi_reset); + reset_control_assert(res->ahb_reset); + reset_control_assert(res->por_reset); diff --git a/target/linux/ipq806x/patches-4.14/0072-firmware-scm-add-ipq806x-compatible-string.patch b/target/linux/ipq806x/patches-4.14/0072-firmware-scm-add-ipq806x-compatible-string.patch new file mode 100644 index 000000000..9129c1bed --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0072-firmware-scm-add-ipq806x-compatible-string.patch @@ -0,0 +1,26 @@ +From 626cf45df06382116f3ff41e82dddfa9100ca24d Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Fri, 22 Dec 2017 23:15:12 +0300 +Subject: [PATCH] firmware: scm: add ipq806x compatible string + +--- + drivers/firmware/qcom_scm.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c +index bb16510..5f9be0d 100644 +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -413,6 +413,9 @@ static const struct of_device_id qcom_scm_dt_match[] = { + { .compatible = "qcom,scm-apq8064", + /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ + }, ++ { .compatible = "qcom,scm-ipq806x", ++ .data = NULL, /* no clocks */ ++ }, + { .compatible = "qcom,scm-msm8660", + .data = (void *) SCM_HAS_CORE_CLK, + }, +-- +Working Copy 3.0.6 + diff --git a/target/linux/ipq806x/patches-4.14/0073-pinctrl-qom-use-scm_call-to-route-GPIO-irq-to-Apps.patch b/target/linux/ipq806x/patches-4.14/0073-pinctrl-qom-use-scm_call-to-route-GPIO-irq-to-Apps.patch new file mode 100644 index 000000000..927a49d14 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/0073-pinctrl-qom-use-scm_call-to-route-GPIO-irq-to-Apps.patch @@ -0,0 +1,168 @@ +From 2034addc7e193dc81d7ca60d8884832751b76758 Mon Sep 17 00:00:00 2001 +From: Ajay Kishore +Date: Tue, 24 Jan 2017 14:14:16 +0530 +Subject: pinctrl: qcom: use scm_call to route GPIO irq to Apps + +For IPQ806x targets, TZ protects the registers that are used to +configure the routing of interrupts to a target processor. +To resolve this, this patch uses scm call to route GPIO interrupts +to application processor. Also the scm call interface is changed. + +Change-Id: Ib6c06829d04bc8c20483c36e63da92e26cdef9ce +Signed-off-by: Ajay Kishore +--- + drivers/firmware/qcom_scm-32.c | 17 +++++++++++++++++ + drivers/firmware/qcom_scm-64.c | 9 +++++++++ + drivers/firmware/qcom_scm.c | 13 +++++++++++++ + drivers/firmware/qcom_scm.h | 8 ++++++++ + drivers/pinctrl/qcom/pinctrl-msm.c | 34 ++++++++++++++++++++++++++++------ + include/linux/qcom_scm.h | 3 ++- + 6 files changed, 77 insertions(+), 7 deletions(-) + +--- a/drivers/firmware/qcom_scm-32.c ++++ b/drivers/firmware/qcom_scm-32.c +@@ -596,3 +596,21 @@ int __qcom_scm_iommu_secure_ptbl_init(st + { + return -ENODEV; + } ++ ++int __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1) ++{ ++ s32 ret; ++ ++ ret = qcom_scm_call_atomic1(svc_id, cmd_id, arg1); ++ ++ return ret; ++} ++ ++int __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2) ++{ ++ s32 ret; ++ ++ ret = qcom_scm_call_atomic2(svc_id, cmd_id, arg1, arg2); ++ ++ return ret; ++ } +--- a/drivers/firmware/qcom_scm-64.c ++++ b/drivers/firmware/qcom_scm-64.c +@@ -439,3 +439,12 @@ int __qcom_scm_iommu_secure_ptbl_init(st + + return ret; + } ++int __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1) ++{ ++ return -ENOTSUPP; ++} ++ ++int __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2) ++{ ++ return -ENOTSUPP; ++} +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -467,3 +467,16 @@ static int __init qcom_scm_init(void) + return platform_driver_register(&qcom_scm_driver); + } + subsys_initcall(qcom_scm_init); ++ ++int qcom_scm_pinmux_read(u32 arg1) ++{ ++ return __qcom_scm_pinmux_read(SCM_SVC_IO_ACCESS, SCM_IO_READ, arg1); ++} ++EXPORT_SYMBOL(qcom_scm_pinmux_read); ++ ++int qcom_scm_pinmux_write(u32 arg1, u32 arg2) ++{ ++ return __qcom_scm_pinmux_write(SCM_SVC_IO_ACCESS, SCM_IO_WRITE, ++ arg1, arg2); ++} ++EXPORT_SYMBOL(qcom_scm_pinmux_write); +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -58,6 +58,13 @@ extern int __qcom_scm_pas_auth_and_rese + extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); + extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset); + ++#define SCM_IO_READ 1 ++#define SCM_IO_WRITE 2 ++#define SCM_SVC_IO_ACCESS 0x5 ++ ++s32 __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1); ++s32 __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2); ++ + /* common error codes */ + #define QCOM_SCM_V2_EBUSY -12 + #define QCOM_SCM_ENOMEM -5 +--- a/drivers/pinctrl/qcom/pinctrl-msm.c ++++ b/drivers/pinctrl/qcom/pinctrl-msm.c +@@ -30,7 +30,8 @@ + #include + #include + #include +- ++#include ++#include + #include "../core.h" + #include "../pinconf.h" + #include "pinctrl-msm.h" +@@ -663,6 +664,9 @@ static int msm_gpio_irq_set_type(struct + const struct msm_pingroup *g; + unsigned long flags; + u32 val; ++ u32 addr; ++ int ret; ++ const __be32 *reg; + + g = &pctrl->soc->groups[d->hwirq]; + +@@ -676,11 +680,30 @@ static int msm_gpio_irq_set_type(struct + else + clear_bit(d->hwirq, pctrl->dual_edge_irqs); + ++ ret = of_device_is_compatible(pctrl->dev->of_node, ++ "qcom,ipq8064-pinctrl"); + /* Route interrupts to application cpu */ +- val = readl(pctrl->regs + g->intr_target_reg); +- val &= ~(7 << g->intr_target_bit); +- val |= g->intr_target_kpss_val << g->intr_target_bit; +- writel(val, pctrl->regs + g->intr_target_reg); ++ if (!ret) { ++ val = readl(pctrl->regs + g->intr_target_reg); ++ val &= ~(7 << g->intr_target_bit); ++ val |= g->intr_target_kpss_val << g->intr_target_bit; ++ writel(val, pctrl->regs + g->intr_target_reg); ++ } else { ++ reg = of_get_property(pctrl->dev->of_node, "reg", NULL); ++ if (reg) { ++ addr = be32_to_cpup(reg) + g->intr_target_reg; ++ val = qcom_scm_pinmux_read(addr); ++ __iormb(); ++ ++ val &= ~(7 << g->intr_target_bit); ++ val |= g->intr_target_kpss_val << g->intr_target_bit; ++ ++ __iowmb(); ++ ret = qcom_scm_pinmux_write(addr, val); ++ if (ret) ++ pr_err("\n Routing interrupts to Apps proc failed"); ++ } ++ } + + /* Update configuration for gpio. + * RAW_STATUS_EN is left on for all gpio irqs. Due to the +@@ -954,4 +977,3 @@ int msm_pinctrl_remove(struct platform_d + return 0; + } + EXPORT_SYMBOL(msm_pinctrl_remove); +- +--- a/include/linux/qcom_scm.h ++++ b/include/linux/qcom_scm.h +@@ -40,6 +40,8 @@ extern int qcom_scm_pas_shutdown(u32 per + extern void qcom_scm_cpu_power_down(u32 flags); + extern u32 qcom_scm_get_version(void); + extern int qcom_scm_set_remote_state(u32 state, u32 id); ++extern s32 qcom_scm_pinmux_read(u32 arg1); ++extern s32 qcom_scm_pinmux_write(u32 arg1, u32 arg2); + extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); + extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); + extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); diff --git a/target/linux/ipq806x/patches-4.14/104-mtd-nand-add-Winbond-manufacturer-and-chip.patch b/target/linux/ipq806x/patches-4.14/104-mtd-nand-add-Winbond-manufacturer-and-chip.patch new file mode 100644 index 000000000..368fc7f03 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/104-mtd-nand-add-Winbond-manufacturer-and-chip.patch @@ -0,0 +1,37 @@ +From 07b6d0cdbbda8c917480eceaec668f09e4cf24a5 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Mon, 14 Nov 2016 23:49:22 +0100 +Subject: [PATCH] mtd: nand: add Winbond manufacturer and chip + +This patch adds the W25N01GV NAND to the table of +known devices. Without this patch the device gets detected: + +nand: device found, Manufacturer ID: 0xef, Chip ID: 0xaa +nand: Unknown NAND 256MiB 1,8V 8-bit +nand: 256 MiB, SLC, erase size: 64 KiB, page size: 1024, OOB size : 16 + +Whereas the u-boot identifies it as: +spi_nand: spi_nand_flash_probe SF NAND ID 00:ef:aa:21 +SF: Detected W25N01GV with page size 2 KiB, total 128 MiB + +Due to the page size discrepancy, it's impossible to attach +ubi volumes on the device. + +Signed-off-by: Christian Lamparter +--- + drivers/mtd/nand/nand_ids.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/nand/nand_ids.c ++++ b/drivers/mtd/nand/nand_ids.c +@@ -54,6 +54,10 @@ struct nand_flash_dev nand_flash_ids[] = + { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, + SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, + NAND_ECC_INFO(40, SZ_1K), 4 }, ++ {"W25N01GV 1G 3.3V 8-bit", ++ { .id = {0xef, 0xaa} }, ++ SZ_2K, SZ_128, SZ_128K, NAND_NO_SUBPAGE_WRITE, ++ 2, 64, NAND_ECC_INFO(1, SZ_512) }, + + LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), diff --git a/target/linux/ipq806x/patches-4.14/105-mtd-nor-add-mx25l25635f.patch b/target/linux/ipq806x/patches-4.14/105-mtd-nor-add-mx25l25635f.patch new file mode 100644 index 000000000..24a7fda22 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/105-mtd-nor-add-mx25l25635f.patch @@ -0,0 +1,22 @@ +Subject: mtd: spi-nor: add mx25l25635f with SECT_4K + +This patch fixes an issue with the creation of the +ubi volume on the AVM FRITZ!Box 4040. The mx25l25635f +and mx25l25635e support SECT_4K which will set the +erase size to 4K. This is used by ubi to calculate +VID header offsets. Without this, uboot and linux +disagrees about the layout and refuse to attach +the ubi volume created by the other. + +--- +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1023,7 +1023,7 @@ static const struct flash_info spi_nor_i + { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, +- { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "mx25l25635f", INFO(0xc22019, 0, 64 * 1024, 512, SECT_4K) }, + { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, diff --git a/target/linux/ipq806x/patches-4.9/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch b/target/linux/ipq806x/patches-4.14/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch similarity index 98% rename from target/linux/ipq806x/patches-4.9/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch rename to target/linux/ipq806x/patches-4.14/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch index 40f5e407b..927390e2c 100644 --- a/target/linux/ipq806x/patches-4.9/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch +++ b/target/linux/ipq806x/patches-4.14/305-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch @@ -67,7 +67,7 @@ Changes: qcom,acc = <&acc3>; qcom,saw = <&saw3>; reg = <0x3>; -@@ -212,22 +222,22 @@ +@@ -235,22 +245,22 @@ }; acc0: clock-controller@b088000 { @@ -94,7 +94,7 @@ Changes: reg = <0x0b0b8000 0x1000>, <0xb008000 0x1000>; }; -@@ -255,6 +265,12 @@ +@@ -278,6 +288,12 @@ regulator; }; diff --git a/target/linux/ipq806x/patches-4.9/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch b/target/linux/ipq806x/patches-4.14/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch similarity index 96% rename from target/linux/ipq806x/patches-4.9/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch rename to target/linux/ipq806x/patches-4.14/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch index 3c3fc98a2..af1f7d3c3 100644 --- a/target/linux/ipq806x/patches-4.9/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch +++ b/target/linux/ipq806x/patches-4.14/306-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch @@ -23,8 +23,8 @@ Changes: --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -108,5 +108,25 @@ - watchdog@b017000 { +@@ -101,5 +101,25 @@ + wifi@a800000 { status = "ok"; }; + @@ -51,9 +51,9 @@ Changes: }; --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -307,5 +307,76 @@ - compatible = "qcom,pshold"; - reg = <0x4ab000 0x4>; +@@ -414,5 +414,76 @@ + "legacy"; + status = "disabled"; }; + + usb3_ss_phy: ssphy@9a000 { diff --git a/target/linux/ipq806x/patches-4.9/307-ARM-qcom-Add-IPQ4019-SoC-support.patch b/target/linux/ipq806x/patches-4.14/307-ARM-qcom-Add-IPQ4019-SoC-support.patch similarity index 94% rename from target/linux/ipq806x/patches-4.9/307-ARM-qcom-Add-IPQ4019-SoC-support.patch rename to target/linux/ipq806x/patches-4.14/307-ARM-qcom-Add-IPQ4019-SoC-support.patch index 4cbcc0aea..1792d4f6b 100644 --- a/target/linux/ipq806x/patches-4.9/307-ARM-qcom-Add-IPQ4019-SoC-support.patch +++ b/target/linux/ipq806x/patches-4.14/307-ARM-qcom-Add-IPQ4019-SoC-support.patch @@ -13,7 +13,7 @@ Signed-off-by: Christian Lamparter --- a/arch/arm/Makefile +++ b/arch/arm/Makefile -@@ -147,6 +147,7 @@ textofs-$(CONFIG_SA1111) := 0x00208000 +@@ -149,6 +149,7 @@ textofs-$(CONFIG_SA1111) := 0x00208000 endif textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 diff --git a/target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch b/target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch new file mode 100644 index 000000000..cd9fd895c --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch @@ -0,0 +1,11026 @@ +From: Christian Lamparter +Subject: BUS: add MSM_BUS +--- a/drivers/bus/Makefile ++++ b/drivers/bus/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmst + obj-$(CONFIG_IMX_WEIM) += imx-weim.o + obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o + obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o ++obj-$(CONFIG_BUS_TOPOLOGY_ADHOC)+= msm_bus/ + + # Interconnect bus driver for OMAP SoCs. + obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o +--- a/drivers/bus/Kconfig ++++ b/drivers/bus/Kconfig +@@ -93,6 +93,8 @@ config MVEBU_MBUS + Driver needed for the MBus configuration on Marvell EBU SoCs + (Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP). + ++source "drivers/bus/msm_bus/Kconfig" ++ + config OMAP_INTERCONNECT + tristate "OMAP INTERCONNECT DRIVER" + depends on ARCH_OMAP2PLUS +--- /dev/null ++++ b/include/dt-bindings/msm/msm-bus-ids.h +@@ -0,0 +1,869 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __MSM_BUS_IDS_H ++#define __MSM_BUS_IDS_H ++ ++/* Topology related enums */ ++#define MSM_BUS_FAB_DEFAULT 0 ++#define MSM_BUS_FAB_APPSS 0 ++#define MSM_BUS_FAB_SYSTEM 1024 ++#define MSM_BUS_FAB_MMSS 2048 ++#define MSM_BUS_FAB_SYSTEM_FPB 3072 ++#define MSM_BUS_FAB_CPSS_FPB 4096 ++ ++#define MSM_BUS_FAB_BIMC 0 ++#define MSM_BUS_FAB_SYS_NOC 1024 ++#define MSM_BUS_FAB_MMSS_NOC 2048 ++#define MSM_BUS_FAB_OCMEM_NOC 3072 ++#define MSM_BUS_FAB_PERIPH_NOC 4096 ++#define MSM_BUS_FAB_CONFIG_NOC 5120 ++#define MSM_BUS_FAB_OCMEM_VNOC 6144 ++#define MSM_BUS_FAB_MMSS_AHB 2049 ++#define MSM_BUS_FAB_A0_NOC 6145 ++#define MSM_BUS_FAB_A1_NOC 6146 ++#define MSM_BUS_FAB_A2_NOC 6147 ++ ++#define MSM_BUS_MASTER_FIRST 1 ++#define MSM_BUS_MASTER_AMPSS_M0 1 ++#define MSM_BUS_MASTER_AMPSS_M1 2 ++#define MSM_BUS_APPSS_MASTER_FAB_MMSS 3 ++#define MSM_BUS_APPSS_MASTER_FAB_SYSTEM 4 ++#define MSM_BUS_SYSTEM_MASTER_FAB_APPSS 5 ++#define MSM_BUS_MASTER_SPS 6 ++#define MSM_BUS_MASTER_ADM_PORT0 7 ++#define MSM_BUS_MASTER_ADM_PORT1 8 ++#define MSM_BUS_SYSTEM_MASTER_ADM1_PORT0 9 ++#define MSM_BUS_MASTER_ADM1_PORT1 10 ++#define MSM_BUS_MASTER_LPASS_PROC 11 ++#define MSM_BUS_MASTER_MSS_PROCI 12 ++#define MSM_BUS_MASTER_MSS_PROCD 13 ++#define MSM_BUS_MASTER_MSS_MDM_PORT0 14 ++#define MSM_BUS_MASTER_LPASS 15 ++#define MSM_BUS_SYSTEM_MASTER_CPSS_FPB 16 ++#define MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB 17 ++#define MSM_BUS_SYSTEM_MASTER_MMSS_FPB 18 ++#define MSM_BUS_MASTER_ADM1_CI 19 ++#define MSM_BUS_MASTER_ADM0_CI 20 ++#define MSM_BUS_MASTER_MSS_MDM_PORT1 21 ++#define MSM_BUS_MASTER_MDP_PORT0 22 ++#define MSM_BUS_MASTER_MDP_PORT1 23 ++#define MSM_BUS_MMSS_MASTER_ADM1_PORT0 24 ++#define MSM_BUS_MASTER_ROTATOR 25 ++#define MSM_BUS_MASTER_GRAPHICS_3D 26 ++#define MSM_BUS_MASTER_JPEG_DEC 27 ++#define MSM_BUS_MASTER_GRAPHICS_2D_CORE0 28 ++#define MSM_BUS_MASTER_VFE 29 ++#define MSM_BUS_MASTER_VPE 30 ++#define MSM_BUS_MASTER_JPEG_ENC 31 ++#define MSM_BUS_MASTER_GRAPHICS_2D_CORE1 32 ++#define MSM_BUS_MMSS_MASTER_APPS_FAB 33 ++#define MSM_BUS_MASTER_HD_CODEC_PORT0 34 ++#define MSM_BUS_MASTER_HD_CODEC_PORT1 35 ++#define MSM_BUS_MASTER_SPDM 36 ++#define MSM_BUS_MASTER_RPM 37 ++#define MSM_BUS_MASTER_MSS 38 ++#define MSM_BUS_MASTER_RIVA 39 ++#define MSM_BUS_MASTER_SNOC_VMEM 40 ++#define MSM_BUS_MASTER_MSS_SW_PROC 41 ++#define MSM_BUS_MASTER_MSS_FW_PROC 42 ++#define MSM_BUS_MASTER_HMSS 43 ++#define MSM_BUS_MASTER_GSS_NAV 44 ++#define MSM_BUS_MASTER_PCIE 45 ++#define MSM_BUS_MASTER_SATA 46 ++#define MSM_BUS_MASTER_CRYPTO 47 ++#define MSM_BUS_MASTER_VIDEO_CAP 48 ++#define MSM_BUS_MASTER_GRAPHICS_3D_PORT1 49 ++#define MSM_BUS_MASTER_VIDEO_ENC 50 ++#define MSM_BUS_MASTER_VIDEO_DEC 51 ++#define MSM_BUS_MASTER_LPASS_AHB 52 ++#define MSM_BUS_MASTER_QDSS_BAM 53 ++#define MSM_BUS_MASTER_SNOC_CFG 54 ++#define MSM_BUS_MASTER_CRYPTO_CORE0 55 ++#define MSM_BUS_MASTER_CRYPTO_CORE1 56 ++#define MSM_BUS_MASTER_MSS_NAV 57 ++#define MSM_BUS_MASTER_OCMEM_DMA 58 ++#define MSM_BUS_MASTER_WCSS 59 ++#define MSM_BUS_MASTER_QDSS_ETR 60 ++#define MSM_BUS_MASTER_USB3 61 ++#define MSM_BUS_MASTER_JPEG 62 ++#define MSM_BUS_MASTER_VIDEO_P0 63 ++#define MSM_BUS_MASTER_VIDEO_P1 64 ++#define MSM_BUS_MASTER_MSS_PROC 65 ++#define MSM_BUS_MASTER_JPEG_OCMEM 66 ++#define MSM_BUS_MASTER_MDP_OCMEM 67 ++#define MSM_BUS_MASTER_VIDEO_P0_OCMEM 68 ++#define MSM_BUS_MASTER_VIDEO_P1_OCMEM 69 ++#define MSM_BUS_MASTER_VFE_OCMEM 70 ++#define MSM_BUS_MASTER_CNOC_ONOC_CFG 71 ++#define MSM_BUS_MASTER_RPM_INST 72 ++#define MSM_BUS_MASTER_RPM_DATA 73 ++#define MSM_BUS_MASTER_RPM_SYS 74 ++#define MSM_BUS_MASTER_DEHR 75 ++#define MSM_BUS_MASTER_QDSS_DAP 76 ++#define MSM_BUS_MASTER_TIC 77 ++#define MSM_BUS_MASTER_SDCC_1 78 ++#define MSM_BUS_MASTER_SDCC_3 79 ++#define MSM_BUS_MASTER_SDCC_4 80 ++#define MSM_BUS_MASTER_SDCC_2 81 ++#define MSM_BUS_MASTER_TSIF 82 ++#define MSM_BUS_MASTER_BAM_DMA 83 ++#define MSM_BUS_MASTER_BLSP_2 84 ++#define MSM_BUS_MASTER_USB_HSIC 85 ++#define MSM_BUS_MASTER_BLSP_1 86 ++#define MSM_BUS_MASTER_USB_HS 87 ++#define MSM_BUS_MASTER_PNOC_CFG 88 ++#define MSM_BUS_MASTER_V_OCMEM_GFX3D 89 ++#define MSM_BUS_MASTER_IPA 90 ++#define MSM_BUS_MASTER_QPIC 91 ++#define MSM_BUS_MASTER_MDPE 92 ++#define MSM_BUS_MASTER_USB_HS2 93 ++#define MSM_BUS_MASTER_VPU 94 ++#define MSM_BUS_MASTER_UFS 95 ++#define MSM_BUS_MASTER_BCAST 96 ++#define MSM_BUS_MASTER_CRYPTO_CORE2 97 ++#define MSM_BUS_MASTER_EMAC 98 ++#define MSM_BUS_MASTER_VPU_1 99 ++#define MSM_BUS_MASTER_PCIE_1 100 ++#define MSM_BUS_MASTER_USB3_1 101 ++#define MSM_BUS_MASTER_CNOC_MNOC_MMSS_CFG 102 ++#define MSM_BUS_MASTER_CNOC_MNOC_CFG 103 ++#define MSM_BUS_MASTER_TCU_0 104 ++#define MSM_BUS_MASTER_TCU_1 105 ++#define MSM_BUS_MASTER_CPP 106 ++#define MSM_BUS_MASTER_AUDIO 107 ++#define MSM_BUS_MASTER_PCIE_2 108 ++#define MSM_BUS_MASTER_BLSP_BAM 109 ++#define MSM_BUS_MASTER_USB2_BAM 110 ++#define MSM_BUS_MASTER_ADDS_DMA0 111 ++#define MSM_BUS_MASTER_ADDS_DMA1 112 ++#define MSM_BUS_MASTER_ADDS_DMA2 113 ++#define MSM_BUS_MASTER_ADDS_DMA3 114 ++#define MSM_BUS_MASTER_QPIC_BAM 115 ++#define MSM_BUS_MASTER_SDCC_BAM 116 ++#define MSM_BUS_MASTER_DDRC_SNOC 117 ++#define MSM_BUS_MASTER_WSS_0 118 ++#define MSM_BUS_MASTER_WSS_1 119 ++#define MSM_BUS_MASTER_ESS 120 ++#define MSM_BUS_MASTER_QDSS_BAMNDP 121 ++#define MSM_BUS_MASTER_QDSS_SNOC_CFG 122 ++#define MSM_BUS_MASTER_LAST 130 ++ ++#define MSM_BUS_SYSTEM_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB ++#define MSM_BUS_CPSS_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_CPSS_FPB ++ ++#define MSM_BUS_SNOC_MM_INT_0 10000 ++#define MSM_BUS_SNOC_MM_INT_1 10001 ++#define MSM_BUS_SNOC_MM_INT_2 10002 ++#define MSM_BUS_SNOC_MM_INT_BIMC 10003 ++#define MSM_BUS_SNOC_INT_0 10004 ++#define MSM_BUS_SNOC_INT_1 10005 ++#define MSM_BUS_SNOC_INT_BIMC 10006 ++#define MSM_BUS_SNOC_BIMC_0_MAS 10007 ++#define MSM_BUS_SNOC_BIMC_1_MAS 10008 ++#define MSM_BUS_SNOC_QDSS_INT 10009 ++#define MSM_BUS_PNOC_SNOC_MAS 10010 ++#define MSM_BUS_PNOC_SNOC_SLV 10011 ++#define MSM_BUS_PNOC_INT_0 10012 ++#define MSM_BUS_PNOC_INT_1 10013 ++#define MSM_BUS_PNOC_M_0 10014 ++#define MSM_BUS_PNOC_M_1 10015 ++#define MSM_BUS_BIMC_SNOC_MAS 10016 ++#define MSM_BUS_BIMC_SNOC_SLV 10017 ++#define MSM_BUS_PNOC_SLV_0 10018 ++#define MSM_BUS_PNOC_SLV_1 10019 ++#define MSM_BUS_PNOC_SLV_2 10020 ++#define MSM_BUS_PNOC_SLV_3 10021 ++#define MSM_BUS_PNOC_SLV_4 10022 ++#define MSM_BUS_PNOC_SLV_8 10023 ++#define MSM_BUS_PNOC_SLV_9 10024 ++#define MSM_BUS_SNOC_BIMC_0_SLV 10025 ++#define MSM_BUS_SNOC_BIMC_1_SLV 10026 ++#define MSM_BUS_MNOC_BIMC_MAS 10027 ++#define MSM_BUS_MNOC_BIMC_SLV 10028 ++#define MSM_BUS_BIMC_MNOC_MAS 10029 ++#define MSM_BUS_BIMC_MNOC_SLV 10030 ++#define MSM_BUS_SNOC_BIMC_MAS 10031 ++#define MSM_BUS_SNOC_BIMC_SLV 10032 ++#define MSM_BUS_CNOC_SNOC_MAS 10033 ++#define MSM_BUS_CNOC_SNOC_SLV 10034 ++#define MSM_BUS_SNOC_CNOC_MAS 10035 ++#define MSM_BUS_SNOC_CNOC_SLV 10036 ++#define MSM_BUS_OVNOC_SNOC_MAS 10037 ++#define MSM_BUS_OVNOC_SNOC_SLV 10038 ++#define MSM_BUS_SNOC_OVNOC_MAS 10039 ++#define MSM_BUS_SNOC_OVNOC_SLV 10040 ++#define MSM_BUS_SNOC_PNOC_MAS 10041 ++#define MSM_BUS_SNOC_PNOC_SLV 10042 ++#define MSM_BUS_BIMC_INT_APPS_EBI 10043 ++#define MSM_BUS_BIMC_INT_APPS_SNOC 10044 ++#define MSM_BUS_SNOC_BIMC_2_MAS 10045 ++#define MSM_BUS_SNOC_BIMC_2_SLV 10046 ++#define MSM_BUS_PNOC_SLV_5 10047 ++#define MSM_BUS_PNOC_SLV_6 10048 ++#define MSM_BUS_PNOC_INT_2 10049 ++#define MSM_BUS_PNOC_INT_3 10050 ++#define MSM_BUS_PNOC_INT_4 10051 ++#define MSM_BUS_PNOC_INT_5 10052 ++#define MSM_BUS_PNOC_INT_6 10053 ++#define MSM_BUS_PNOC_INT_7 10054 ++#define MSM_BUS_BIMC_SNOC_1_MAS 10055 ++#define MSM_BUS_BIMC_SNOC_1_SLV 10056 ++#define MSM_BUS_PNOC_A1NOC_MAS 10057 ++#define MSM_BUS_PNOC_A1NOC_SLV 10058 ++#define MSM_BUS_CNOC_A1NOC_MAS 10059 ++#define MSM_BUS_A0NOC_SNOC_MAS 10060 ++#define MSM_BUS_A0NOC_SNOC_SLV 10061 ++#define MSM_BUS_A1NOC_SNOC_SLV 10062 ++#define MSM_BUS_A1NOC_SNOC_MAS 10063 ++#define MSM_BUS_A2NOC_SNOC_MAS 10064 ++#define MSM_BUS_A2NOC_SNOC_SLV 10065 ++#define MSM_BUS_PNOC_SLV_7 10066 ++#define MSM_BUS_INT_LAST 10067 ++ ++#define MSM_BUS_SLAVE_FIRST 512 ++#define MSM_BUS_SLAVE_EBI_CH0 512 ++#define MSM_BUS_SLAVE_EBI_CH1 513 ++#define MSM_BUS_SLAVE_AMPSS_L2 514 ++#define MSM_BUS_APPSS_SLAVE_FAB_MMSS 515 ++#define MSM_BUS_APPSS_SLAVE_FAB_SYSTEM 516 ++#define MSM_BUS_SYSTEM_SLAVE_FAB_APPS 517 ++#define MSM_BUS_SLAVE_SPS 518 ++#define MSM_BUS_SLAVE_SYSTEM_IMEM 519 ++#define MSM_BUS_SLAVE_AMPSS 520 ++#define MSM_BUS_SLAVE_MSS 521 ++#define MSM_BUS_SLAVE_LPASS 522 ++#define MSM_BUS_SYSTEM_SLAVE_CPSS_FPB 523 ++#define MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB 524 ++#define MSM_BUS_SYSTEM_SLAVE_MMSS_FPB 525 ++#define MSM_BUS_SLAVE_CORESIGHT 526 ++#define MSM_BUS_SLAVE_RIVA 527 ++#define MSM_BUS_SLAVE_SMI 528 ++#define MSM_BUS_MMSS_SLAVE_FAB_APPS 529 ++#define MSM_BUS_MMSS_SLAVE_FAB_APPS_1 530 ++#define MSM_BUS_SLAVE_MM_IMEM 531 ++#define MSM_BUS_SLAVE_CRYPTO 532 ++#define MSM_BUS_SLAVE_SPDM 533 ++#define MSM_BUS_SLAVE_RPM 534 ++#define MSM_BUS_SLAVE_RPM_MSG_RAM 535 ++#define MSM_BUS_SLAVE_MPM 536 ++#define MSM_BUS_SLAVE_PMIC1_SSBI1_A 537 ++#define MSM_BUS_SLAVE_PMIC1_SSBI1_B 538 ++#define MSM_BUS_SLAVE_PMIC1_SSBI1_C 539 ++#define MSM_BUS_SLAVE_PMIC2_SSBI2_A 540 ++#define MSM_BUS_SLAVE_PMIC2_SSBI2_B 541 ++#define MSM_BUS_SLAVE_GSBI1_UART 542 ++#define MSM_BUS_SLAVE_GSBI2_UART 543 ++#define MSM_BUS_SLAVE_GSBI3_UART 544 ++#define MSM_BUS_SLAVE_GSBI4_UART 545 ++#define MSM_BUS_SLAVE_GSBI5_UART 546 ++#define MSM_BUS_SLAVE_GSBI6_UART 547 ++#define MSM_BUS_SLAVE_GSBI7_UART 548 ++#define MSM_BUS_SLAVE_GSBI8_UART 549 ++#define MSM_BUS_SLAVE_GSBI9_UART 550 ++#define MSM_BUS_SLAVE_GSBI10_UART 551 ++#define MSM_BUS_SLAVE_GSBI11_UART 552 ++#define MSM_BUS_SLAVE_GSBI12_UART 553 ++#define MSM_BUS_SLAVE_GSBI1_QUP 554 ++#define MSM_BUS_SLAVE_GSBI2_QUP 555 ++#define MSM_BUS_SLAVE_GSBI3_QUP 556 ++#define MSM_BUS_SLAVE_GSBI4_QUP 557 ++#define MSM_BUS_SLAVE_GSBI5_QUP 558 ++#define MSM_BUS_SLAVE_GSBI6_QUP 559 ++#define MSM_BUS_SLAVE_GSBI7_QUP 560 ++#define MSM_BUS_SLAVE_GSBI8_QUP 561 ++#define MSM_BUS_SLAVE_GSBI9_QUP 562 ++#define MSM_BUS_SLAVE_GSBI10_QUP 563 ++#define MSM_BUS_SLAVE_GSBI11_QUP 564 ++#define MSM_BUS_SLAVE_GSBI12_QUP 565 ++#define MSM_BUS_SLAVE_EBI2_NAND 566 ++#define MSM_BUS_SLAVE_EBI2_CS0 567 ++#define MSM_BUS_SLAVE_EBI2_CS1 568 ++#define MSM_BUS_SLAVE_EBI2_CS2 569 ++#define MSM_BUS_SLAVE_EBI2_CS3 570 ++#define MSM_BUS_SLAVE_EBI2_CS4 571 ++#define MSM_BUS_SLAVE_EBI2_CS5 572 ++#define MSM_BUS_SLAVE_USB_FS1 573 ++#define MSM_BUS_SLAVE_USB_FS2 574 ++#define MSM_BUS_SLAVE_TSIF 575 ++#define MSM_BUS_SLAVE_MSM_TSSC 576 ++#define MSM_BUS_SLAVE_MSM_PDM 577 ++#define MSM_BUS_SLAVE_MSM_DIMEM 578 ++#define MSM_BUS_SLAVE_MSM_TCSR 579 ++#define MSM_BUS_SLAVE_MSM_PRNG 580 ++#define MSM_BUS_SLAVE_GSS 581 ++#define MSM_BUS_SLAVE_SATA 582 ++#define MSM_BUS_SLAVE_USB3 583 ++#define MSM_BUS_SLAVE_WCSS 584 ++#define MSM_BUS_SLAVE_OCIMEM 585 ++#define MSM_BUS_SLAVE_SNOC_OCMEM 586 ++#define MSM_BUS_SLAVE_SERVICE_SNOC 587 ++#define MSM_BUS_SLAVE_QDSS_STM 588 ++#define MSM_BUS_SLAVE_CAMERA_CFG 589 ++#define MSM_BUS_SLAVE_DISPLAY_CFG 590 ++#define MSM_BUS_SLAVE_OCMEM_CFG 591 ++#define MSM_BUS_SLAVE_CPR_CFG 592 ++#define MSM_BUS_SLAVE_CPR_XPU_CFG 593 ++#define MSM_BUS_SLAVE_MISC_CFG 594 ++#define MSM_BUS_SLAVE_MISC_XPU_CFG 595 ++#define MSM_BUS_SLAVE_VENUS_CFG 596 ++#define MSM_BUS_SLAVE_MISC_VENUS_CFG 597 ++#define MSM_BUS_SLAVE_GRAPHICS_3D_CFG 598 ++#define MSM_BUS_SLAVE_MMSS_CLK_CFG 599 ++#define MSM_BUS_SLAVE_MMSS_CLK_XPU_CFG 600 ++#define MSM_BUS_SLAVE_MNOC_MPU_CFG 601 ++#define MSM_BUS_SLAVE_ONOC_MPU_CFG 602 ++#define MSM_BUS_SLAVE_SERVICE_MNOC 603 ++#define MSM_BUS_SLAVE_OCMEM 604 ++#define MSM_BUS_SLAVE_SERVICE_ONOC 605 ++#define MSM_BUS_SLAVE_SDCC_1 606 ++#define MSM_BUS_SLAVE_SDCC_3 607 ++#define MSM_BUS_SLAVE_SDCC_2 608 ++#define MSM_BUS_SLAVE_SDCC_4 609 ++#define MSM_BUS_SLAVE_BAM_DMA 610 ++#define MSM_BUS_SLAVE_BLSP_2 611 ++#define MSM_BUS_SLAVE_USB_HSIC 612 ++#define MSM_BUS_SLAVE_BLSP_1 613 ++#define MSM_BUS_SLAVE_USB_HS 614 ++#define MSM_BUS_SLAVE_PDM 615 ++#define MSM_BUS_SLAVE_PERIPH_APU_CFG 616 ++#define MSM_BUS_SLAVE_PNOC_MPU_CFG 617 ++#define MSM_BUS_SLAVE_PRNG 618 ++#define MSM_BUS_SLAVE_SERVICE_PNOC 619 ++#define MSM_BUS_SLAVE_CLK_CTL 620 ++#define MSM_BUS_SLAVE_CNOC_MSS 621 ++#define MSM_BUS_SLAVE_SECURITY 622 ++#define MSM_BUS_SLAVE_TCSR 623 ++#define MSM_BUS_SLAVE_TLMM 624 ++#define MSM_BUS_SLAVE_CRYPTO_0_CFG 625 ++#define MSM_BUS_SLAVE_CRYPTO_1_CFG 626 ++#define MSM_BUS_SLAVE_IMEM_CFG 627 ++#define MSM_BUS_SLAVE_MESSAGE_RAM 628 ++#define MSM_BUS_SLAVE_BIMC_CFG 629 ++#define MSM_BUS_SLAVE_BOOT_ROM 630 ++#define MSM_BUS_SLAVE_CNOC_MNOC_MMSS_CFG 631 ++#define MSM_BUS_SLAVE_PMIC_ARB 632 ++#define MSM_BUS_SLAVE_SPDM_WRAPPER 633 ++#define MSM_BUS_SLAVE_DEHR_CFG 634 ++#define MSM_BUS_SLAVE_QDSS_CFG 635 ++#define MSM_BUS_SLAVE_RBCPR_CFG 636 ++#define MSM_BUS_SLAVE_RBCPR_QDSS_APU_CFG 637 ++#define MSM_BUS_SLAVE_SNOC_MPU_CFG 638 ++#define MSM_BUS_SLAVE_CNOC_ONOC_CFG 639 ++#define MSM_BUS_SLAVE_CNOC_MNOC_CFG 640 ++#define MSM_BUS_SLAVE_PNOC_CFG 641 ++#define MSM_BUS_SLAVE_SNOC_CFG 642 ++#define MSM_BUS_SLAVE_EBI1_DLL_CFG 643 ++#define MSM_BUS_SLAVE_PHY_APU_CFG 644 ++#define MSM_BUS_SLAVE_EBI1_PHY_CFG 645 ++#define MSM_BUS_SLAVE_SERVICE_CNOC 646 ++#define MSM_BUS_SLAVE_IPS_CFG 647 ++#define MSM_BUS_SLAVE_QPIC 648 ++#define MSM_BUS_SLAVE_DSI_CFG 649 ++#define MSM_BUS_SLAVE_UFS_CFG 650 ++#define MSM_BUS_SLAVE_RBCPR_CX_CFG 651 ++#define MSM_BUS_SLAVE_RBCPR_MX_CFG 652 ++#define MSM_BUS_SLAVE_PCIE_CFG 653 ++#define MSM_BUS_SLAVE_USB_PHYS_CFG 654 ++#define MSM_BUS_SLAVE_VIDEO_CAP_CFG 655 ++#define MSM_BUS_SLAVE_AVSYNC_CFG 656 ++#define MSM_BUS_SLAVE_CRYPTO_2_CFG 657 ++#define MSM_BUS_SLAVE_VPU_CFG 658 ++#define MSM_BUS_SLAVE_BCAST_CFG 659 ++#define MSM_BUS_SLAVE_KLM_CFG 660 ++#define MSM_BUS_SLAVE_GENI_IR_CFG 661 ++#define MSM_BUS_SLAVE_OCMEM_GFX 662 ++#define MSM_BUS_SLAVE_CATS_128 663 ++#define MSM_BUS_SLAVE_OCMEM_64 664 ++#define MSM_BUS_SLAVE_PCIE_0 665 ++#define MSM_BUS_SLAVE_PCIE_1 666 ++#define MSM_BUS_SLAVE_PCIE_0_CFG 667 ++#define MSM_BUS_SLAVE_PCIE_1_CFG 668 ++#define MSM_BUS_SLAVE_SRVC_MNOC 669 ++#define MSM_BUS_SLAVE_USB_HS2 670 ++#define MSM_BUS_SLAVE_AUDIO 671 ++#define MSM_BUS_SLAVE_TCU 672 ++#define MSM_BUS_SLAVE_APPSS 673 ++#define MSM_BUS_SLAVE_PCIE_PARF 674 ++#define MSM_BUS_SLAVE_USB3_PHY_CFG 675 ++#define MSM_BUS_SLAVE_IPA_CFG 676 ++#define MSM_BUS_SLAVE_A0NOC_SNOC 677 ++#define MSM_BUS_SLAVE_A1NOC_SNOC 678 ++#define MSM_BUS_SLAVE_A2NOC_SNOC 679 ++#define MSM_BUS_SLAVE_HMSS_L3 680 ++#define MSM_BUS_SLAVE_PIMEM_CFG 681 ++#define MSM_BUS_SLAVE_DCC_CFG 682 ++#define MSM_BUS_SLAVE_QDSS_RBCPR_APU_CFG 683 ++#define MSM_BUS_SLAVE_PCIE_2_CFG 684 ++#define MSM_BUS_SLAVE_PCIE20_AHB2PHY 685 ++#define MSM_BUS_SLAVE_A0NOC_CFG 686 ++#define MSM_BUS_SLAVE_A1NOC_CFG 687 ++#define MSM_BUS_SLAVE_A2NOC_CFG 688 ++#define MSM_BUS_SLAVE_A1NOC_MPU_CFG 689 ++#define MSM_BUS_SLAVE_A2NOC_MPU_CFG 690 ++#define MSM_BUS_SLAVE_A0NOC_SMMU_CFG 691 ++#define MSM_BUS_SLAVE_A1NOC_SMMU_CFG 692 ++#define MSM_BUS_SLAVE_A2NOC_SMMU_CFG 693 ++#define MSM_BUS_SLAVE_LPASS_SMMU_CFG 694 ++#define MSM_BUS_SLAVE_MMAGIC_CFG 695 ++#define MSM_BUS_SLAVE_VENUS_THROTTLE_CFG 696 ++#define MSM_BUS_SLAVE_SSC_CFG 697 ++#define MSM_BUS_SLAVE_DSA_CFG 698 ++#define MSM_BUS_SLAVE_DSA_MPU_CFG 699 ++#define MSM_BUS_SLAVE_DISPLAY_THROTTLE_CFG 700 ++#define MSM_BUS_SLAVE_SMMU_CPP_CFG 701 ++#define MSM_BUS_SLAVE_SMMU_JPEG_CFG 702 ++#define MSM_BUS_SLAVE_SMMU_MDP_CFG 703 ++#define MSM_BUS_SLAVE_SMMU_ROTATOR_CFG 704 ++#define MSM_BUS_SLAVE_SMMU_VENUS_CFG 705 ++#define MSM_BUS_SLAVE_SMMU_VFE_CFG 706 ++#define MSM_BUS_SLAVE_A0NOC_MPU_CFG 707 ++#define MSM_BUS_SLAVE_VMEM_CFG 708 ++#define MSM_BUS_SLAVE_CAMERA_THROTTLE_CFG 700 ++#define MSM_BUS_SLAVE_VMEM 709 ++#define MSM_BUS_SLAVE_AHB2PHY 710 ++#define MSM_BUS_SLAVE_PIMEM 711 ++#define MSM_BUS_SLAVE_SNOC_VMEM 712 ++#define MSM_BUS_SLAVE_PCIE_2 713 ++#define MSM_BUS_SLAVE_RBCPR_MX 714 ++#define MSM_BUS_SLAVE_RBCPR_CX 715 ++#define MSM_BUS_SLAVE_PRNG_APU_CFG 716 ++#define MSM_BUS_SLAVE_PERIPH_MPU_CFG 717 ++#define MSM_BUS_SLAVE_GCNT 718 ++#define MSM_BUS_SLAVE_ADSS_CFG 719 ++#define MSM_BUS_SLAVE_ADSS_VMIDMT_CFG 720 ++#define MSM_BUS_SLAVE_QHSS_APU_CFG 721 ++#define MSM_BUS_SLAVE_MDIO 722 ++#define MSM_BUS_SLAVE_FEPHY_CFG 723 ++#define MSM_BUS_SLAVE_SRIF 724 ++#define MSM_BUS_SLAVE_LAST 730 ++#define MSM_BUS_SLAVE_DDRC_CFG 731 ++#define MSM_BUS_SLAVE_DDRC_APU_CFG 732 ++#define MSM_BUS_SLAVE_MPU0_CFG 733 ++#define MSM_BUS_SLAVE_MPU1_CFG 734 ++#define MSM_BUS_SLAVE_MPU2_CFG 734 ++#define MSM_BUS_SLAVE_ESS_VMIDMT_CFG 735 ++#define MSM_BUS_SLAVE_ESS_APU_CFG 736 ++#define MSM_BUS_SLAVE_USB2_CFG 737 ++#define MSM_BUS_SLAVE_BLSP_CFG 738 ++#define MSM_BUS_SLAVE_QPIC_CFG 739 ++#define MSM_BUS_SLAVE_SDCC_CFG 740 ++#define MSM_BUS_SLAVE_WSS0_VMIDMT_CFG 741 ++#define MSM_BUS_SLAVE_WSS0_APU_CFG 742 ++#define MSM_BUS_SLAVE_WSS1_VMIDMT_CFG 743 ++#define MSM_BUS_SLAVE_WSS1_APU_CFG 744 ++#define MSM_BUS_SLAVE_SRVC_PCNOC 745 ++#define MSM_BUS_SLAVE_SNOC_DDRC 746 ++#define MSM_BUS_SLAVE_A7SS 747 ++#define MSM_BUS_SLAVE_WSS0_CFG 748 ++#define MSM_BUS_SLAVE_WSS1_CFG 749 ++#define MSM_BUS_SLAVE_PCIE 750 ++#define MSM_BUS_SLAVE_USB3_CFG 751 ++#define MSM_BUS_SLAVE_CRYPTO_CFG 752 ++#define MSM_BUS_SLAVE_ESS_CFG 753 ++#define MSM_BUS_SLAVE_SRVC_SNOC 754 ++ ++#define MSM_BUS_SYSTEM_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB ++#define MSM_BUS_CPSS_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_CPSS_FPB ++ ++/* ++ * ID's used in RPM messages ++ */ ++#define ICBID_MASTER_APPSS_PROC 0 ++#define ICBID_MASTER_MSS_PROC 1 ++#define ICBID_MASTER_MNOC_BIMC 2 ++#define ICBID_MASTER_SNOC_BIMC 3 ++#define ICBID_MASTER_SNOC_BIMC_0 ICBID_MASTER_SNOC_BIMC ++#define ICBID_MASTER_CNOC_MNOC_MMSS_CFG 4 ++#define ICBID_MASTER_CNOC_MNOC_CFG 5 ++#define ICBID_MASTER_GFX3D 6 ++#define ICBID_MASTER_JPEG 7 ++#define ICBID_MASTER_MDP 8 ++#define ICBID_MASTER_MDP0 ICBID_MASTER_MDP ++#define ICBID_MASTER_MDPS ICBID_MASTER_MDP ++#define ICBID_MASTER_VIDEO 9 ++#define ICBID_MASTER_VIDEO_P0 ICBID_MASTER_VIDEO ++#define ICBID_MASTER_VIDEO_P1 10 ++#define ICBID_MASTER_VFE 11 ++#define ICBID_MASTER_CNOC_ONOC_CFG 12 ++#define ICBID_MASTER_JPEG_OCMEM 13 ++#define ICBID_MASTER_MDP_OCMEM 14 ++#define ICBID_MASTER_VIDEO_P0_OCMEM 15 ++#define ICBID_MASTER_VIDEO_P1_OCMEM 16 ++#define ICBID_MASTER_VFE_OCMEM 17 ++#define ICBID_MASTER_LPASS_AHB 18 ++#define ICBID_MASTER_QDSS_BAM 19 ++#define ICBID_MASTER_SNOC_CFG 20 ++#define ICBID_MASTER_BIMC_SNOC 21 ++#define ICBID_MASTER_CNOC_SNOC 22 ++#define ICBID_MASTER_CRYPTO 23 ++#define ICBID_MASTER_CRYPTO_CORE0 ICBID_MASTER_CRYPTO ++#define ICBID_MASTER_CRYPTO_CORE1 24 ++#define ICBID_MASTER_LPASS_PROC 25 ++#define ICBID_MASTER_MSS 26 ++#define ICBID_MASTER_MSS_NAV 27 ++#define ICBID_MASTER_OCMEM_DMA 28 ++#define ICBID_MASTER_PNOC_SNOC 29 ++#define ICBID_MASTER_WCSS 30 ++#define ICBID_MASTER_QDSS_ETR 31 ++#define ICBID_MASTER_USB3 32 ++#define ICBID_MASTER_USB3_0 ICBID_MASTER_USB3 ++#define ICBID_MASTER_SDCC_1 33 ++#define ICBID_MASTER_SDCC_3 34 ++#define ICBID_MASTER_SDCC_2 35 ++#define ICBID_MASTER_SDCC_4 36 ++#define ICBID_MASTER_TSIF 37 ++#define ICBID_MASTER_BAM_DMA 38 ++#define ICBID_MASTER_BLSP_2 39 ++#define ICBID_MASTER_USB_HSIC 40 ++#define ICBID_MASTER_BLSP_1 41 ++#define ICBID_MASTER_USB_HS 42 ++#define ICBID_MASTER_USB_HS1 ICBID_MASTER_USB_HS ++#define ICBID_MASTER_PNOC_CFG 43 ++#define ICBID_MASTER_SNOC_PNOC 44 ++#define ICBID_MASTER_RPM_INST 45 ++#define ICBID_MASTER_RPM_DATA 46 ++#define ICBID_MASTER_RPM_SYS 47 ++#define ICBID_MASTER_DEHR 48 ++#define ICBID_MASTER_QDSS_DAP 49 ++#define ICBID_MASTER_SPDM 50 ++#define ICBID_MASTER_TIC 51 ++#define ICBID_MASTER_SNOC_CNOC 52 ++#define ICBID_MASTER_GFX3D_OCMEM 53 ++#define ICBID_MASTER_GFX3D_GMEM ICBID_MASTER_GFX3D_OCMEM ++#define ICBID_MASTER_OVIRT_SNOC 54 ++#define ICBID_MASTER_SNOC_OVIRT 55 ++#define ICBID_MASTER_SNOC_GVIRT ICBID_MASTER_SNOC_OVIRT ++#define ICBID_MASTER_ONOC_OVIRT 56 ++#define ICBID_MASTER_USB_HS2 57 ++#define ICBID_MASTER_QPIC 58 ++#define ICBID_MASTER_IPA 59 ++#define ICBID_MASTER_DSI 60 ++#define ICBID_MASTER_MDP1 61 ++#define ICBID_MASTER_MDPE ICBID_MASTER_MDP1 ++#define ICBID_MASTER_VPU_PROC 62 ++#define ICBID_MASTER_VPU 63 ++#define ICBID_MASTER_VPU0 ICBID_MASTER_VPU ++#define ICBID_MASTER_CRYPTO_CORE2 64 ++#define ICBID_MASTER_PCIE_0 65 ++#define ICBID_MASTER_PCIE_1 66 ++#define ICBID_MASTER_SATA 67 ++#define ICBID_MASTER_UFS 68 ++#define ICBID_MASTER_USB3_1 69 ++#define ICBID_MASTER_VIDEO_OCMEM 70 ++#define ICBID_MASTER_VPU1 71 ++#define ICBID_MASTER_VCAP 72 ++#define ICBID_MASTER_EMAC 73 ++#define ICBID_MASTER_BCAST 74 ++#define ICBID_MASTER_MMSS_PROC 75 ++#define ICBID_MASTER_SNOC_BIMC_1 76 ++#define ICBID_MASTER_SNOC_PCNOC 77 ++#define ICBID_MASTER_AUDIO 78 ++#define ICBID_MASTER_MM_INT_0 79 ++#define ICBID_MASTER_MM_INT_1 80 ++#define ICBID_MASTER_MM_INT_2 81 ++#define ICBID_MASTER_MM_INT_BIMC 82 ++#define ICBID_MASTER_MSS_INT 83 ++#define ICBID_MASTER_PCNOC_CFG 84 ++#define ICBID_MASTER_PCNOC_INT_0 85 ++#define ICBID_MASTER_PCNOC_INT_1 86 ++#define ICBID_MASTER_PCNOC_M_0 87 ++#define ICBID_MASTER_PCNOC_M_1 88 ++#define ICBID_MASTER_PCNOC_S_0 89 ++#define ICBID_MASTER_PCNOC_S_1 90 ++#define ICBID_MASTER_PCNOC_S_2 91 ++#define ICBID_MASTER_PCNOC_S_3 92 ++#define ICBID_MASTER_PCNOC_S_4 93 ++#define ICBID_MASTER_PCNOC_S_6 94 ++#define ICBID_MASTER_PCNOC_S_7 95 ++#define ICBID_MASTER_PCNOC_S_8 96 ++#define ICBID_MASTER_PCNOC_S_9 97 ++#define ICBID_MASTER_QDSS_INT 98 ++#define ICBID_MASTER_SNOC_INT_0 99 ++#define ICBID_MASTER_SNOC_INT_1 100 ++#define ICBID_MASTER_SNOC_INT_BIMC 101 ++#define ICBID_MASTER_TCU_0 102 ++#define ICBID_MASTER_TCU_1 103 ++#define ICBID_MASTER_BIMC_INT_0 104 ++#define ICBID_MASTER_BIMC_INT_1 105 ++#define ICBID_MASTER_CAMERA 106 ++#define ICBID_MASTER_RICA 107 ++#define ICBID_MASTER_PCNOC_S_5 129 ++#define ICBID_MASTER_PCNOC_INT_2 124 ++#define ICBID_MASTER_PCNOC_INT_3 125 ++#define ICBID_MASTER_PCNOC_INT_4 126 ++#define ICBID_MASTER_PCNOC_INT_5 127 ++#define ICBID_MASTER_PCNOC_INT_6 128 ++#define ICBID_MASTER_PCIE_2 119 ++#define ICBID_MASTER_MASTER_CNOC_A1NOC 116 ++#define ICBID_MASTER_A0NOC_SNOC 110 ++#define ICBID_MASTER_A1NOC_SNOC 111 ++#define ICBID_MASTER_A2NOC_SNOC 112 ++#define ICBID_MASTER_PNOC_A1NOC 117 ++#define ICBID_MASTER_ROTATOR 120 ++#define ICBID_MASTER_SNOC_VMEM 114 ++#define ICBID_MASTER_VENUS_VMEM 121 ++#define ICBID_MASTER_HMSS 118 ++#define ICBID_MASTER_BIMC_SNOC_1 109 ++#define ICBID_MASTER_CNOC_A1NOC 116 ++#define ICBID_MASTER_CPP 115 ++#define ICBID_MASTER_BLSP_BAM 130 ++#define ICBID_MASTER_USB2_BAM 131 ++#define ICBID_MASTER_ADSS_DMA0 132 ++#define ICBID_MASTER_ADSS_DMA1 133 ++#define ICBID_MASTER_ADSS_DMA2 134 ++#define ICBID_MASTER_ADSS_DMA3 135 ++#define ICBID_MASTER_QPIC_BAM 136 ++#define ICBID_MASTER_SDCC_BAM 137 ++#define ICBID_MASTER_DDRC_SNOC 138 ++#define ICBID_MASTER_WSS_0 139 ++#define ICBID_MASTER_WSS_1 140 ++#define ICBID_MASTER_ESS 141 ++#define ICBID_MASTER_PCIE 142 ++#define ICBID_MASTER_QDSS_BAMNDP 143 ++#define ICBID_MASTER_QDSS_SNOC_CFG 144 ++ ++#define ICBID_SLAVE_EBI1 0 ++#define ICBID_SLAVE_APPSS_L2 1 ++#define ICBID_SLAVE_BIMC_SNOC 2 ++#define ICBID_SLAVE_CAMERA_CFG 3 ++#define ICBID_SLAVE_DISPLAY_CFG 4 ++#define ICBID_SLAVE_OCMEM_CFG 5 ++#define ICBID_SLAVE_CPR_CFG 6 ++#define ICBID_SLAVE_CPR_XPU_CFG 7 ++#define ICBID_SLAVE_MISC_CFG 8 ++#define ICBID_SLAVE_MISC_XPU_CFG 9 ++#define ICBID_SLAVE_VENUS_CFG 10 ++#define ICBID_SLAVE_GFX3D_CFG 11 ++#define ICBID_SLAVE_MMSS_CLK_CFG 12 ++#define ICBID_SLAVE_MMSS_CLK_XPU_CFG 13 ++#define ICBID_SLAVE_MNOC_MPU_CFG 14 ++#define ICBID_SLAVE_ONOC_MPU_CFG 15 ++#define ICBID_SLAVE_MNOC_BIMC 16 ++#define ICBID_SLAVE_SERVICE_MNOC 17 ++#define ICBID_SLAVE_OCMEM 18 ++#define ICBID_SLAVE_GMEM ICBID_SLAVE_OCMEM ++#define ICBID_SLAVE_SERVICE_ONOC 19 ++#define ICBID_SLAVE_APPSS 20 ++#define ICBID_SLAVE_LPASS 21 ++#define ICBID_SLAVE_USB3 22 ++#define ICBID_SLAVE_USB3_0 ICBID_SLAVE_USB3 ++#define ICBID_SLAVE_WCSS 23 ++#define ICBID_SLAVE_SNOC_BIMC 24 ++#define ICBID_SLAVE_SNOC_BIMC_0 ICBID_SLAVE_SNOC_BIMC ++#define ICBID_SLAVE_SNOC_CNOC 25 ++#define ICBID_SLAVE_IMEM 26 ++#define ICBID_SLAVE_OCIMEM ICBID_SLAVE_IMEM ++#define ICBID_SLAVE_SNOC_OVIRT 27 ++#define ICBID_SLAVE_SNOC_GVIRT ICBID_SLAVE_SNOC_OVIRT ++#define ICBID_SLAVE_SNOC_PNOC 28 ++#define ICBID_SLAVE_SNOC_PCNOC ICBID_SLAVE_SNOC_PNOC ++#define ICBID_SLAVE_SERVICE_SNOC 29 ++#define ICBID_SLAVE_QDSS_STM 30 ++#define ICBID_SLAVE_SDCC_1 31 ++#define ICBID_SLAVE_SDCC_3 32 ++#define ICBID_SLAVE_SDCC_2 33 ++#define ICBID_SLAVE_SDCC_4 34 ++#define ICBID_SLAVE_TSIF 35 ++#define ICBID_SLAVE_BAM_DMA 36 ++#define ICBID_SLAVE_BLSP_2 37 ++#define ICBID_SLAVE_USB_HSIC 38 ++#define ICBID_SLAVE_BLSP_1 39 ++#define ICBID_SLAVE_USB_HS 40 ++#define ICBID_SLAVE_USB_HS1 ICBID_SLAVE_USB_HS ++#define ICBID_SLAVE_PDM 41 ++#define ICBID_SLAVE_PERIPH_APU_CFG 42 ++#define ICBID_SLAVE_PNOC_MPU_CFG 43 ++#define ICBID_SLAVE_PRNG 44 ++#define ICBID_SLAVE_PNOC_SNOC 45 ++#define ICBID_SLAVE_PCNOC_SNOC ICBID_SLAVE_PNOC_SNOC ++#define ICBID_SLAVE_SERVICE_PNOC 46 ++#define ICBID_SLAVE_CLK_CTL 47 ++#define ICBID_SLAVE_CNOC_MSS 48 ++#define ICBID_SLAVE_PCNOC_MSS ICBID_SLAVE_CNOC_MSS ++#define ICBID_SLAVE_SECURITY 49 ++#define ICBID_SLAVE_TCSR 50 ++#define ICBID_SLAVE_TLMM 51 ++#define ICBID_SLAVE_CRYPTO_0_CFG 52 ++#define ICBID_SLAVE_CRYPTO_1_CFG 53 ++#define ICBID_SLAVE_IMEM_CFG 54 ++#define ICBID_SLAVE_MESSAGE_RAM 55 ++#define ICBID_SLAVE_BIMC_CFG 56 ++#define ICBID_SLAVE_BOOT_ROM 57 ++#define ICBID_SLAVE_CNOC_MNOC_MMSS_CFG 58 ++#define ICBID_SLAVE_PMIC_ARB 59 ++#define ICBID_SLAVE_SPDM_WRAPPER 60 ++#define ICBID_SLAVE_DEHR_CFG 61 ++#define ICBID_SLAVE_MPM 62 ++#define ICBID_SLAVE_QDSS_CFG 63 ++#define ICBID_SLAVE_RBCPR_CFG 64 ++#define ICBID_SLAVE_RBCPR_CX_CFG ICBID_SLAVE_RBCPR_CFG ++#define ICBID_SLAVE_RBCPR_QDSS_APU_CFG 65 ++#define ICBID_SLAVE_CNOC_MNOC_CFG 66 ++#define ICBID_SLAVE_SNOC_MPU_CFG 67 ++#define ICBID_SLAVE_CNOC_ONOC_CFG 68 ++#define ICBID_SLAVE_PNOC_CFG 69 ++#define ICBID_SLAVE_SNOC_CFG 70 ++#define ICBID_SLAVE_EBI1_DLL_CFG 71 ++#define ICBID_SLAVE_PHY_APU_CFG 72 ++#define ICBID_SLAVE_EBI1_PHY_CFG 73 ++#define ICBID_SLAVE_RPM 74 ++#define ICBID_SLAVE_CNOC_SNOC 75 ++#define ICBID_SLAVE_SERVICE_CNOC 76 ++#define ICBID_SLAVE_OVIRT_SNOC 77 ++#define ICBID_SLAVE_OVIRT_OCMEM 78 ++#define ICBID_SLAVE_USB_HS2 79 ++#define ICBID_SLAVE_QPIC 80 ++#define ICBID_SLAVE_IPS_CFG 81 ++#define ICBID_SLAVE_DSI_CFG 82 ++#define ICBID_SLAVE_USB3_1 83 ++#define ICBID_SLAVE_PCIE_0 84 ++#define ICBID_SLAVE_PCIE_1 85 ++#define ICBID_SLAVE_PSS_SMMU_CFG 86 ++#define ICBID_SLAVE_CRYPTO_2_CFG 87 ++#define ICBID_SLAVE_PCIE_0_CFG 88 ++#define ICBID_SLAVE_PCIE_1_CFG 89 ++#define ICBID_SLAVE_SATA_CFG 90 ++#define ICBID_SLAVE_SPSS_GENI_IR 91 ++#define ICBID_SLAVE_UFS_CFG 92 ++#define ICBID_SLAVE_AVSYNC_CFG 93 ++#define ICBID_SLAVE_VPU_CFG 94 ++#define ICBID_SLAVE_USB_PHY_CFG 95 ++#define ICBID_SLAVE_RBCPR_MX_CFG 96 ++#define ICBID_SLAVE_PCIE_PARF 97 ++#define ICBID_SLAVE_VCAP_CFG 98 ++#define ICBID_SLAVE_EMAC_CFG 99 ++#define ICBID_SLAVE_BCAST_CFG 100 ++#define ICBID_SLAVE_KLM_CFG 101 ++#define ICBID_SLAVE_DISPLAY_PWM 102 ++#define ICBID_SLAVE_GENI 103 ++#define ICBID_SLAVE_SNOC_BIMC_1 104 ++#define ICBID_SLAVE_AUDIO 105 ++#define ICBID_SLAVE_CATS_0 106 ++#define ICBID_SLAVE_CATS_1 107 ++#define ICBID_SLAVE_MM_INT_0 108 ++#define ICBID_SLAVE_MM_INT_1 109 ++#define ICBID_SLAVE_MM_INT_2 110 ++#define ICBID_SLAVE_MM_INT_BIMC 111 ++#define ICBID_SLAVE_MMU_MODEM_XPU_CFG 112 ++#define ICBID_SLAVE_MSS_INT 113 ++#define ICBID_SLAVE_PCNOC_INT_0 114 ++#define ICBID_SLAVE_PCNOC_INT_1 115 ++#define ICBID_SLAVE_PCNOC_M_0 116 ++#define ICBID_SLAVE_PCNOC_M_1 117 ++#define ICBID_SLAVE_PCNOC_S_0 118 ++#define ICBID_SLAVE_PCNOC_S_1 119 ++#define ICBID_SLAVE_PCNOC_S_2 120 ++#define ICBID_SLAVE_PCNOC_S_3 121 ++#define ICBID_SLAVE_PCNOC_S_4 122 ++#define ICBID_SLAVE_PCNOC_S_6 123 ++#define ICBID_SLAVE_PCNOC_S_7 124 ++#define ICBID_SLAVE_PCNOC_S_8 125 ++#define ICBID_SLAVE_PCNOC_S_9 126 ++#define ICBID_SLAVE_PRNG_XPU_CFG 127 ++#define ICBID_SLAVE_QDSS_INT 128 ++#define ICBID_SLAVE_RPM_XPU_CFG 129 ++#define ICBID_SLAVE_SNOC_INT_0 130 ++#define ICBID_SLAVE_SNOC_INT_1 131 ++#define ICBID_SLAVE_SNOC_INT_BIMC 132 ++#define ICBID_SLAVE_TCU 133 ++#define ICBID_SLAVE_BIMC_INT_0 134 ++#define ICBID_SLAVE_BIMC_INT_1 135 ++#define ICBID_SLAVE_RICA_CFG 136 ++#define ICBID_SLAVE_PCNOC_S_5 189 ++#define ICBID_SLAVE_PCNOC_S_7 124 ++#define ICBID_SLAVE_PCNOC_INT_2 184 ++#define ICBID_SLAVE_PCNOC_INT_3 185 ++#define ICBID_SLAVE_PCNOC_INT_4 186 ++#define ICBID_SLAVE_PCNOC_INT_5 187 ++#define ICBID_SLAVE_PCNOC_INT_6 188 ++#define ICBID_SLAVE_USB3_PHY_CFG 182 ++#define ICBID_SLAVE_IPA_CFG 183 ++ ++#define ICBID_SLAVE_A0NOC_SNOC 141 ++#define ICBID_SLAVE_A1NOC_SNOC 142 ++#define ICBID_SLAVE_A2NOC_SNOC 143 ++#define ICBID_SLAVE_BIMC_SNOC_1 138 ++#define ICBID_SLAVE_PIMEM 167 ++#define ICBID_SLAVE_PIMEM_CFG 168 ++#define ICBID_SLAVE_DCC_CFG 155 ++#define ICBID_SLAVE_QDSS_RBCPR_APU_CFG 168 ++#define ICBID_SLAVE_A0NOC_CFG 144 ++#define ICBID_SLAVE_PCIE_2_CFG 165 ++#define ICBID_SLAVE_PCIE20_AHB2PHY 163 ++#define ICBID_SLAVE_PCIE_2 164 ++#define ICBID_SLAVE_A1NOC_CFG 147 ++#define ICBID_SLAVE_A1NOC_MPU_CFG 148 ++#define ICBID_SLAVE_A1NOC_SMMU_CFG 149 ++#define ICBID_SLAVE_A2NOC_CFG 150 ++#define ICBID_SLAVE_A2NOC_MPU_CFG 151 ++#define ICBID_SLAVE_A2NOC_SMMU_CFG 152 ++#define ICBID_SLAVE_AHB2PHY 153 ++#define ICBID_SLAVE_HMSS_L3 161 ++#define ICBID_SLAVE_LPASS_SMMU_CFG 161 ++#define ICBID_SLAVE_MMAGIC_CFG 162 ++#define ICBID_SLAVE_SSC_CFG 177 ++#define ICBID_SLAVE_VENUS_THROTTLE_CFG 178 ++#define ICBID_SLAVE_DISPLAY_THROTTLE_CFG 156 ++#define ICBID_SLAVE_CAMERA_THROTTLE_CFG 154 ++#define ICBID_SLAVE_DSA_CFG 157 ++#define ICBID_SLAVE_DSA_MPU_CFG 158 ++#define ICBID_SLAVE_SMMU_CPP_CFG 171 ++#define ICBID_SLAVE_SMMU_JPEG_CFG 172 ++#define ICBID_SLAVE_SMMU_MDP_CFG 173 ++#define ICBID_SLAVE_SMMU_ROTATOR_CFG 174 ++#define ICBID_SLAVE_SMMU_VENUS_CFG 175 ++#define ICBID_SLAVE_SMMU_VFE_CFG 176 ++#define ICBID_SLAVE_A0NOC_MPU_CFG 145 ++#define ICBID_SLAVE_A0NOC_SMMU_CFG 146 ++#define ICBID_SLAVE_VMEM_CFG 180 ++#define ICBID_SLAVE_VMEM 179 ++#define ICBID_SLAVE_PNOC_A1NOC 139 ++#define ICBID_SLAVE_SNOC_VMEM 140 ++#define ICBID_SLAVE_RBCPR_MX 170 ++#define ICBID_SLAVE_RBCPR_CX 169 ++#define ICBID_SLAVE_PRNG_APU_CFG 190 ++#define ICBID_SLAVE_PERIPH_MPU_CFG 191 ++#define ICBID_SLAVE_GCNT 192 ++#define ICBID_SLAVE_ADSS_CFG 193 ++#define ICBID_SLAVE_ADSS_APU 194 ++#define ICBID_SLAVE_ADSS_VMIDMT_CFG 195 ++#define ICBID_SLAVE_QHSS_APU_CFG 196 ++#define ICBID_SLAVE_MDIO 197 ++#define ICBID_SLAVE_FEPHY_CFG 198 ++#define ICBID_SLAVE_SRIF 199 ++#define ICBID_SLAVE_DDRC_CFG 200 ++#define ICBID_SLAVE_DDRC_APU_CFG 201 ++#define ICBID_SLAVE_DDRC_MPU0_CFG 202 ++#define ICBID_SLAVE_DDRC_MPU1_CFG 203 ++#define ICBID_SLAVE_DDRC_MPU2_CFG 210 ++#define ICBID_SLAVE_ESS_VMIDMT_CFG 211 ++#define ICBID_SLAVE_ESS_APU_CFG 212 ++#define ICBID_SLAVE_USB2_CFG 213 ++#define ICBID_SLAVE_BLSP_CFG 214 ++#define ICBID_SLAVE_QPIC_CFG 215 ++#define ICBID_SLAVE_SDCC_CFG 216 ++#define ICBID_SLAVE_WSS0_VMIDMT_CFG 217 ++#define ICBID_SLAVE_WSS0_APU_CFG 218 ++#define ICBID_SLAVE_WSS1_VMIDMT_CFG 219 ++#define ICBID_SLAVE_WSS1_APU_CFG 220 ++#define ICBID_SLAVE_SRVC_PCNOC 221 ++#define ICBID_SLAVE_SNOC_DDRC 222 ++#define ICBID_SLAVE_A7SS 223 ++#define ICBID_SLAVE_WSS0_CFG 224 ++#define ICBID_SLAVE_WSS1_CFG 225 ++#define ICBID_SLAVE_PCIE 226 ++#define ICBID_SLAVE_USB3_CFG 227 ++#define ICBID_SLAVE_CRYPTO_CFG 228 ++#define ICBID_SLAVE_ESS_CFG 229 ++#define ICBID_SLAVE_SRVC_SNOC 230 ++#endif +--- /dev/null ++++ b/include/dt-bindings/msm/msm-bus-rule-ops.h +@@ -0,0 +1,32 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __MSM_BUS_RULE_OPS_H ++#define __MSM_BUS_RULE_OPS_H ++ ++#define FLD_IB 0 ++#define FLD_AB 1 ++#define FLD_CLK 2 ++ ++#define OP_LE 0 ++#define OP_LT 1 ++#define OP_GE 2 ++#define OP_GT 3 ++#define OP_NOOP 4 ++ ++#define RULE_STATE_NOT_APPLIED 0 ++#define RULE_STATE_APPLIED 1 ++ ++#define THROTTLE_ON 0 ++#define THROTTLE_OFF 1 ++ ++#endif +--- /dev/null ++++ b/drivers/bus/msm_bus/Kconfig +@@ -0,0 +1,19 @@ ++config BUS_TOPOLOGY_ADHOC ++ bool "ad-hoc bus scaling topology" ++ depends on ARCH_QCOM ++ default n ++ help ++ This option enables a driver that can handle adhoc bus topologies. ++ Adhoc bus topology driver allows one to many connections and maintains ++ directionality of connections by explicitly listing device connections ++ thus avoiding illegal routes. ++ ++config MSM_BUS_SCALING ++ bool "Bus scaling driver" ++ depends on BUS_TOPOLOGY_ADHOC ++ default n ++ help ++ This option enables bus scaling on MSM devices. Bus scaling ++ allows devices to request the clocks be set to rates sufficient ++ for the active devices needs without keeping the clocks at max ++ frequency when a slower speed is sufficient. +--- /dev/null ++++ b/drivers/bus/msm_bus/Makefile +@@ -0,0 +1,12 @@ ++# ++# Makefile for msm-bus driver specific files ++# ++obj-y += msm_bus_bimc.o msm_bus_noc.o msm_bus_core.o msm_bus_client_api.o \ ++ msm_bus_id.o ++obj-$(CONFIG_OF) += msm_bus_of.o ++ ++obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o msm_bus_rules.o ++obj-$(CONFIG_OF) += msm_bus_of_adhoc.o ++obj-$(CONFIG_CORESIGHT) += msm_buspm_coresight_adhoc.o ++ ++obj-$(CONFIG_DEBUG_FS) += msm_bus_dbg.o +--- /dev/null ++++ b/drivers/bus/msm_bus/msm-bus-board.h +@@ -0,0 +1,198 @@ ++/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __ASM_ARCH_MSM_BUS_BOARD_H ++#define __ASM_ARCH_MSM_BUS_BOARD_H ++ ++#include ++#include ++ ++enum context { ++ DUAL_CTX, ++ ACTIVE_CTX, ++ NUM_CTX ++}; ++ ++struct msm_bus_fabric_registration { ++ unsigned int id; ++ const char *name; ++ struct msm_bus_node_info *info; ++ unsigned int len; ++ int ahb; ++ const char *fabclk[NUM_CTX]; ++ const char *iface_clk; ++ unsigned int offset; ++ unsigned int haltid; ++ unsigned int rpm_enabled; ++ unsigned int nmasters; ++ unsigned int nslaves; ++ unsigned int ntieredslaves; ++ bool il_flag; ++ const struct msm_bus_board_algorithm *board_algo; ++ int hw_sel; ++ void *hw_data; ++ uint32_t qos_freq; ++ uint32_t qos_baseoffset; ++ u64 nr_lim_thresh; ++ uint32_t eff_fact; ++ uint32_t qos_delta; ++ bool virt; ++}; ++ ++struct msm_bus_device_node_registration { ++ struct msm_bus_node_device_type *info; ++ unsigned int num_devices; ++ bool virt; ++}; ++ ++enum msm_bus_bw_tier_type { ++ MSM_BUS_BW_TIER1 = 1, ++ MSM_BUS_BW_TIER2, ++ MSM_BUS_BW_COUNT, ++ MSM_BUS_BW_SIZE = 0x7FFFFFFF, ++}; ++ ++struct msm_bus_halt_vector { ++ uint32_t haltval; ++ uint32_t haltmask; ++}; ++ ++extern struct msm_bus_fabric_registration msm_bus_apps_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_sys_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_mm_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_sys_fpb_pdata; ++extern struct msm_bus_fabric_registration msm_bus_cpss_fpb_pdata; ++extern struct msm_bus_fabric_registration msm_bus_def_fab_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_8960_apps_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8960_sys_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8960_mm_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8960_sg_mm_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8960_cpss_fpb_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_8064_apps_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8064_sys_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8064_mm_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8064_sys_fpb_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8064_cpss_fpb_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_9615_sys_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_9615_def_fab_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_8930_apps_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8930_sys_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8930_mm_fabric_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8930_sys_fpb_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8930_cpss_fpb_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_8974_sys_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_mmss_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_bimc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_ocmem_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_periph_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_config_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_8974_ocmem_vnoc_pdata; ++ ++extern struct msm_bus_fabric_registration msm_bus_9625_sys_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_9625_bimc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_9625_periph_noc_pdata; ++extern struct msm_bus_fabric_registration msm_bus_9625_config_noc_pdata; ++ ++extern int msm_bus_device_match_adhoc(struct device *dev, void *id); ++ ++void msm_bus_rpm_set_mt_mask(void); ++int msm_bus_board_rpm_get_il_ids(uint16_t *id); ++int msm_bus_board_get_iid(int id); ++ ++#define NFAB_MSM8226 6 ++#define NFAB_MSM8610 5 ++ ++/* ++ * These macros specify the convention followed for allocating ++ * ids to fabrics, masters and slaves for 8x60. ++ * ++ * A node can be identified as a master/slave/fabric by using ++ * these ids. ++ */ ++#define FABRIC_ID_KEY 1024 ++#define SLAVE_ID_KEY ((FABRIC_ID_KEY) >> 1) ++#define MAX_FAB_KEY 7168 /* OR(All fabric ids) */ ++#define INT_NODE_START 10000 ++ ++#define GET_FABID(id) ((id) & MAX_FAB_KEY) ++ ++#define NODE_ID(id) ((id) & (FABRIC_ID_KEY - 1)) ++#define IS_SLAVE(id) ((NODE_ID(id)) >= SLAVE_ID_KEY ? 1 : 0) ++#define CHECK_ID(iid, id) (((iid & id) != id) ? -ENXIO : iid) ++ ++/* ++ * The following macros are used to format the data for port halt ++ * and unhalt requests. ++ */ ++#define MSM_BUS_CLK_HALT 0x1 ++#define MSM_BUS_CLK_HALT_MASK 0x1 ++#define MSM_BUS_CLK_HALT_FIELDSIZE 0x1 ++#define MSM_BUS_CLK_UNHALT 0x0 ++ ++#define MSM_BUS_MASTER_SHIFT(master, fieldsize) \ ++ ((master) * (fieldsize)) ++ ++#define MSM_BUS_SET_BITFIELD(word, fieldmask, fieldvalue) \ ++ { \ ++ (word) &= ~(fieldmask); \ ++ (word) |= (fieldvalue); \ ++ } ++ ++ ++#define MSM_BUS_MASTER_HALT(u32haltmask, u32haltval, master) \ ++ MSM_BUS_SET_BITFIELD(u32haltmask, \ ++ MSM_BUS_CLK_HALT_MASK< ++ ++ ++#endif /*__ASM_ARCH_MSM_BUS_BOARD_H */ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm-bus.h +@@ -0,0 +1,139 @@ ++/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_BUS_H ++#define _ARCH_ARM_MACH_MSM_BUS_H ++ ++#include ++#include ++#include ++ ++/* ++ * Macros for clients to convert their data to ib and ab ++ * Ws : Time window over which to transfer the data in SECONDS ++ * Bs : Size of the data block in bytes ++ * Per : Recurrence period ++ * Tb : Throughput bandwidth to prevent stalling ++ * R : Ratio of actual bandwidth used to Tb ++ * Ib : Instantaneous bandwidth ++ * Ab : Arbitrated bandwidth ++ * ++ * IB_RECURRBLOCK and AB_RECURRBLOCK: ++ * These are used if the requirement is to transfer a ++ * recurring block of data over a known time window. ++ * ++ * IB_THROUGHPUTBW and AB_THROUGHPUTBW: ++ * These are used for CPU style masters. Here the requirement ++ * is to have minimum throughput bandwidth available to avoid ++ * stalling. ++ */ ++#define IB_RECURRBLOCK(Ws, Bs) ((Ws) == 0 ? 0 : ((Bs)/(Ws))) ++#define AB_RECURRBLOCK(Ws, Per) ((Ws) == 0 ? 0 : ((Bs)/(Per))) ++#define IB_THROUGHPUTBW(Tb) (Tb) ++#define AB_THROUGHPUTBW(Tb, R) ((Tb) * (R)) ++ ++struct msm_bus_vectors { ++ int src; /* Master */ ++ int dst; /* Slave */ ++ uint64_t ab; /* Arbitrated bandwidth */ ++ uint64_t ib; /* Instantaneous bandwidth */ ++}; ++ ++struct msm_bus_paths { ++ int num_paths; ++ struct msm_bus_vectors *vectors; ++}; ++ ++struct msm_bus_scale_pdata { ++ struct msm_bus_paths *usecase; ++ int num_usecases; ++ const char *name; ++ /* ++ * If the active_only flag is set to 1, the BW request is applied ++ * only when at least one CPU is active (powered on). If the flag ++ * is set to 0, then the BW request is always applied irrespective ++ * of the CPU state. ++ */ ++ unsigned int active_only; ++}; ++ ++/* Scaling APIs */ ++ ++/* ++ * This function returns a handle to the client. This should be used to ++ * call msm_bus_scale_client_update_request. ++ * The function returns 0 if bus driver is unable to register a client ++ */ ++ ++#if (defined(CONFIG_MSM_BUS_SCALING) || defined(CONFIG_BUS_TOPOLOGY_ADHOC)) ++int __init msm_bus_fabric_init_driver(void); ++uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata); ++int msm_bus_scale_client_update_request(uint32_t cl, unsigned int index); ++void msm_bus_scale_unregister_client(uint32_t cl); ++/* AXI Port configuration APIs */ ++int msm_bus_axi_porthalt(int master_port); ++int msm_bus_axi_portunhalt(int master_port); ++ ++#else ++static inline int __init msm_bus_fabric_init_driver(void) { return 0; } ++ ++static inline uint32_t ++msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) ++{ ++ return 1; ++} ++ ++static inline int ++msm_bus_scale_client_update_request(uint32_t cl, unsigned int index) ++{ ++ return 0; ++} ++ ++static inline void ++msm_bus_scale_unregister_client(uint32_t cl) ++{ ++} ++ ++static inline int msm_bus_axi_porthalt(int master_port) ++{ ++ return 0; ++} ++ ++static inline int msm_bus_axi_portunhalt(int master_port) ++{ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_OF) && defined(CONFIG_MSM_BUS_SCALING) ++struct msm_bus_scale_pdata *msm_bus_pdata_from_node( ++ struct platform_device *pdev, struct device_node *of_node); ++struct msm_bus_scale_pdata *msm_bus_cl_get_pdata(struct platform_device *pdev); ++void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata); ++#else ++static inline struct msm_bus_scale_pdata ++*msm_bus_cl_get_pdata(struct platform_device *pdev) ++{ ++ return NULL; ++} ++ ++static inline struct msm_bus_scale_pdata *msm_bus_pdata_from_node( ++ struct platform_device *pdev, struct device_node *of_node) ++{ ++ return NULL; ++} ++ ++static inline void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata) ++{ ++} ++#endif ++#endif /*_ARCH_ARM_MACH_MSM_BUS_H*/ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_adhoc.h +@@ -0,0 +1,141 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_BUS_ADHOC_H ++#define _ARCH_ARM_MACH_MSM_BUS_ADHOC_H ++ ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm-bus.h" ++#include "msm_bus_rules.h" ++#include "msm_bus_core.h" ++ ++struct msm_bus_node_device_type; ++struct link_node { ++ uint64_t lnode_ib[NUM_CTX]; ++ uint64_t lnode_ab[NUM_CTX]; ++ int next; ++ struct device *next_dev; ++ struct list_head link; ++ uint32_t in_use; ++}; ++ ++/* New types introduced for adhoc topology */ ++struct msm_bus_noc_ops { ++ int (*qos_init)(struct msm_bus_node_device_type *dev, ++ void __iomem *qos_base, uint32_t qos_off, ++ uint32_t qos_delta, uint32_t qos_freq); ++ int (*set_bw)(struct msm_bus_node_device_type *dev, ++ void __iomem *qos_base, uint32_t qos_off, ++ uint32_t qos_delta, uint32_t qos_freq); ++ int (*limit_mport)(struct msm_bus_node_device_type *dev, ++ void __iomem *qos_base, uint32_t qos_off, ++ uint32_t qos_delta, uint32_t qos_freq, bool enable_lim, ++ uint64_t lim_bw); ++ bool (*update_bw_reg)(int mode); ++}; ++ ++struct nodebw { ++ uint64_t ab[NUM_CTX]; ++ bool dirty; ++}; ++ ++struct msm_bus_fab_device_type { ++ void __iomem *qos_base; ++ phys_addr_t pqos_base; ++ size_t qos_range; ++ uint32_t base_offset; ++ uint32_t qos_freq; ++ uint32_t qos_off; ++ uint32_t util_fact; ++ uint32_t vrail_comp; ++ struct msm_bus_noc_ops noc_ops; ++ enum msm_bus_hw_sel bus_type; ++ bool bypass_qos_prg; ++}; ++ ++struct qos_params_type { ++ int mode; ++ unsigned int prio_lvl; ++ unsigned int prio_rd; ++ unsigned int prio_wr; ++ unsigned int prio1; ++ unsigned int prio0; ++ unsigned int gp; ++ unsigned int thmp; ++ unsigned int ws; ++ int cur_mode; ++ u64 bw_buffer; ++}; ++ ++struct msm_bus_node_info_type { ++ const char *name; ++ unsigned int id; ++ int mas_rpm_id; ++ int slv_rpm_id; ++ int num_ports; ++ int num_qports; ++ int *qport; ++ struct qos_params_type qos_params; ++ unsigned int num_connections; ++ unsigned int num_blist; ++ bool is_fab_dev; ++ bool virt_dev; ++ bool is_traversed; ++ unsigned int *connections; ++ unsigned int *black_listed_connections; ++ struct device **dev_connections; ++ struct device **black_connections; ++ unsigned int bus_device_id; ++ struct device *bus_device; ++ unsigned int buswidth; ++ struct rule_update_path_info rule; ++ uint64_t lim_bw; ++ uint32_t util_fact; ++ uint32_t vrail_comp; ++}; ++ ++struct msm_bus_node_device_type { ++ struct msm_bus_node_info_type *node_info; ++ struct msm_bus_fab_device_type *fabdev; ++ int num_lnodes; ++ struct link_node *lnode_list; ++ uint64_t cur_clk_hz[NUM_CTX]; ++ struct nodebw node_ab; ++ struct list_head link; ++ unsigned int ap_owned; ++ struct nodeclk clk[NUM_CTX]; ++ struct nodeclk qos_clk; ++}; ++ ++int msm_bus_enable_limiter(struct msm_bus_node_device_type *nodedev, ++ bool throttle_en, uint64_t lim_bw); ++int msm_bus_update_clks(struct msm_bus_node_device_type *nodedev, ++ int ctx, int **dirty_nodes, int *num_dirty); ++int msm_bus_commit_data(int *dirty_nodes, int ctx, int num_dirty); ++int msm_bus_update_bw(struct msm_bus_node_device_type *nodedev, int ctx, ++ int64_t add_bw, int **dirty_nodes, int *num_dirty); ++void *msm_bus_realloc_devmem(struct device *dev, void *p, size_t old_size, ++ size_t new_size, gfp_t flags); ++ ++extern struct msm_bus_device_node_registration ++ *msm_bus_of_to_pdata(struct platform_device *pdev); ++extern void msm_bus_arb_setops_adhoc(struct msm_bus_arb_ops *arb_ops); ++extern int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev); ++extern int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev); ++extern int msm_bus_of_get_static_rules(struct platform_device *pdev, ++ struct bus_rule_type **static_rule); ++extern int msm_rules_update_path(struct list_head *input_list, ++ struct list_head *output_list); ++extern void print_all_rules(void); ++#endif /* _ARCH_ARM_MACH_MSM_BUS_ADHOC_H */ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_arb_adhoc.c +@@ -0,0 +1,998 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is Mree software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus.h" ++#include "msm_bus_core.h" ++#include "msm_bus_adhoc.h" ++ ++#define NUM_CL_HANDLES 50 ++#define NUM_LNODES 3 ++ ++struct bus_search_type { ++ struct list_head link; ++ struct list_head node_list; ++}; ++ ++struct handle_type { ++ int num_entries; ++ struct msm_bus_client **cl_list; ++}; ++ ++static struct handle_type handle_list; ++struct list_head input_list; ++struct list_head apply_list; ++ ++DEFINE_MUTEX(msm_bus_adhoc_lock); ++ ++static bool chk_bl_list(struct list_head *black_list, unsigned int id) ++{ ++ struct msm_bus_node_device_type *bus_node = NULL; ++ ++ list_for_each_entry(bus_node, black_list, link) { ++ if (bus_node->node_info->id == id) ++ return true; ++ } ++ return false; ++} ++ ++static void copy_remaining_nodes(struct list_head *edge_list, struct list_head ++ *traverse_list, struct list_head *route_list) ++{ ++ struct bus_search_type *search_node; ++ ++ if (list_empty(edge_list) && list_empty(traverse_list)) ++ return; ++ ++ search_node = kzalloc(sizeof(struct bus_search_type), GFP_KERNEL); ++ INIT_LIST_HEAD(&search_node->node_list); ++ list_splice_init(edge_list, traverse_list); ++ list_splice_init(traverse_list, &search_node->node_list); ++ list_add_tail(&search_node->link, route_list); ++} ++ ++/* ++ * Duplicate instantiaion from msm_bus_arb.c. Todo there needs to be a ++ * "util" file for these common func/macros. ++ * ++ * */ ++uint64_t msm_bus_div64(unsigned int w, uint64_t bw) ++{ ++ uint64_t *b = &bw; ++ ++ if ((bw > 0) && (bw < w)) ++ return 1; ++ ++ switch (w) { ++ case 0: ++ WARN(1, "AXI: Divide by 0 attempted\n"); ++ case 1: return bw; ++ case 2: return (bw >> 1); ++ case 4: return (bw >> 2); ++ case 8: return (bw >> 3); ++ case 16: return (bw >> 4); ++ case 32: return (bw >> 5); ++ } ++ ++ do_div(*b, w); ++ return *b; ++} ++ ++int msm_bus_device_match_adhoc(struct device *dev, void *id) ++{ ++ int ret = 0; ++ struct msm_bus_node_device_type *bnode = dev->platform_data; ++ ++ if (bnode) ++ ret = (bnode->node_info->id == *(unsigned int *)id); ++ else ++ ret = 0; ++ ++ return ret; ++} ++ ++static int gen_lnode(struct device *dev, ++ int next_hop, int prev_idx) ++{ ++ struct link_node *lnode; ++ struct msm_bus_node_device_type *cur_dev = NULL; ++ int lnode_idx = -1; ++ ++ if (!dev) ++ goto exit_gen_lnode; ++ ++ cur_dev = dev->platform_data; ++ if (!cur_dev) { ++ MSM_BUS_ERR("%s: Null device ptr", __func__); ++ goto exit_gen_lnode; ++ } ++ ++ if (!cur_dev->num_lnodes) { ++ cur_dev->lnode_list = devm_kzalloc(dev, ++ sizeof(struct link_node) * NUM_LNODES, ++ GFP_KERNEL); ++ if (!cur_dev->lnode_list) ++ goto exit_gen_lnode; ++ ++ lnode = cur_dev->lnode_list; ++ cur_dev->num_lnodes = NUM_LNODES; ++ lnode_idx = 0; ++ } else { ++ int i; ++ for (i = 0; i < cur_dev->num_lnodes; i++) { ++ if (!cur_dev->lnode_list[i].in_use) ++ break; ++ } ++ ++ if (i < cur_dev->num_lnodes) { ++ lnode = &cur_dev->lnode_list[i]; ++ lnode_idx = i; ++ } else { ++ struct link_node *realloc_list; ++ size_t cur_size = sizeof(struct link_node) * ++ cur_dev->num_lnodes; ++ ++ cur_dev->num_lnodes += NUM_LNODES; ++ realloc_list = msm_bus_realloc_devmem( ++ dev, ++ cur_dev->lnode_list, ++ cur_size, ++ sizeof(struct link_node) * ++ cur_dev->num_lnodes, GFP_KERNEL); ++ ++ if (!realloc_list) ++ goto exit_gen_lnode; ++ ++ cur_dev->lnode_list = realloc_list; ++ lnode = &cur_dev->lnode_list[i]; ++ lnode_idx = i; ++ } ++ } ++ ++ lnode->in_use = 1; ++ if (next_hop == cur_dev->node_info->id) { ++ lnode->next = -1; ++ lnode->next_dev = NULL; ++ } else { ++ lnode->next = prev_idx; ++ lnode->next_dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &next_hop, ++ msm_bus_device_match_adhoc); ++ } ++ ++ memset(lnode->lnode_ib, 0, sizeof(uint64_t) * NUM_CTX); ++ memset(lnode->lnode_ab, 0, sizeof(uint64_t) * NUM_CTX); ++ ++exit_gen_lnode: ++ return lnode_idx; ++} ++ ++static int remove_lnode(struct msm_bus_node_device_type *cur_dev, ++ int lnode_idx) ++{ ++ int ret = 0; ++ ++ if (!cur_dev) { ++ MSM_BUS_ERR("%s: Null device ptr", __func__); ++ ret = -ENODEV; ++ goto exit_remove_lnode; ++ } ++ ++ if (lnode_idx != -1) { ++ if (!cur_dev->num_lnodes || ++ (lnode_idx > (cur_dev->num_lnodes - 1))) { ++ MSM_BUS_ERR("%s: Invalid Idx %d, num_lnodes %d", ++ __func__, lnode_idx, cur_dev->num_lnodes); ++ ret = -ENODEV; ++ goto exit_remove_lnode; ++ } ++ ++ cur_dev->lnode_list[lnode_idx].next = -1; ++ cur_dev->lnode_list[lnode_idx].next_dev = NULL; ++ cur_dev->lnode_list[lnode_idx].in_use = 0; ++ } ++ ++exit_remove_lnode: ++ return ret; ++} ++ ++static int prune_path(struct list_head *route_list, int dest, int src, ++ struct list_head *black_list, int found) ++{ ++ struct bus_search_type *search_node, *temp_search_node; ++ struct msm_bus_node_device_type *bus_node; ++ struct list_head *bl_list; ++ struct list_head *temp_bl_list; ++ int search_dev_id = dest; ++ struct device *dest_dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &dest, ++ msm_bus_device_match_adhoc); ++ int lnode_hop = -1; ++ ++ if (!found) ++ goto reset_links; ++ ++ if (!dest_dev) { ++ MSM_BUS_ERR("%s: Can't find dest dev %d", __func__, dest); ++ goto exit_prune_path; ++ } ++ ++ lnode_hop = gen_lnode(dest_dev, search_dev_id, lnode_hop); ++ ++ list_for_each_entry_reverse(search_node, route_list, link) { ++ list_for_each_entry(bus_node, &search_node->node_list, link) { ++ unsigned int i; ++ for (i = 0; i < bus_node->node_info->num_connections; ++ i++) { ++ if (bus_node->node_info->connections[i] == ++ search_dev_id) { ++ dest_dev = bus_find_device( ++ &msm_bus_type, ++ NULL, ++ (void *) ++ &bus_node->node_info-> ++ id, ++ msm_bus_device_match_adhoc); ++ ++ if (!dest_dev) { ++ lnode_hop = -1; ++ goto reset_links; ++ } ++ ++ lnode_hop = gen_lnode(dest_dev, ++ search_dev_id, ++ lnode_hop); ++ search_dev_id = ++ bus_node->node_info->id; ++ break; ++ } ++ } ++ } ++ } ++reset_links: ++ list_for_each_entry_safe(search_node, temp_search_node, route_list, ++ link) { ++ list_for_each_entry(bus_node, &search_node->node_list, ++ link) ++ bus_node->node_info->is_traversed = false; ++ ++ list_del(&search_node->link); ++ kfree(search_node); ++ } ++ ++ list_for_each_safe(bl_list, temp_bl_list, black_list) ++ list_del(bl_list); ++ ++exit_prune_path: ++ return lnode_hop; ++} ++ ++static void setup_bl_list(struct msm_bus_node_device_type *node, ++ struct list_head *black_list) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < node->node_info->num_blist; i++) { ++ struct msm_bus_node_device_type *bdev; ++ bdev = node->node_info->black_connections[i]->platform_data; ++ list_add_tail(&bdev->link, black_list); ++ } ++} ++ ++static int getpath(int src, int dest) ++{ ++ struct list_head traverse_list; ++ struct list_head edge_list; ++ struct list_head route_list; ++ struct list_head black_list; ++ struct device *src_dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &src, ++ msm_bus_device_match_adhoc); ++ struct msm_bus_node_device_type *src_node; ++ struct bus_search_type *search_node; ++ int found = 0; ++ int depth_index = 0; ++ int first_hop = -1; ++ ++ INIT_LIST_HEAD(&traverse_list); ++ INIT_LIST_HEAD(&edge_list); ++ INIT_LIST_HEAD(&route_list); ++ INIT_LIST_HEAD(&black_list); ++ ++ if (!src_dev) { ++ MSM_BUS_ERR("%s: Cannot locate src dev %d", __func__, src); ++ goto exit_getpath; ++ } ++ ++ src_node = src_dev->platform_data; ++ if (!src_node) { ++ MSM_BUS_ERR("%s:Fatal, Source dev %d not found", __func__, src); ++ goto exit_getpath; ++ } ++ list_add_tail(&src_node->link, &traverse_list); ++ ++ while ((!found && !list_empty(&traverse_list))) { ++ struct msm_bus_node_device_type *bus_node = NULL; ++ /* Locate dest_id in the traverse list */ ++ list_for_each_entry(bus_node, &traverse_list, link) { ++ if (bus_node->node_info->id == dest) { ++ found = 1; ++ break; ++ } ++ } ++ ++ if (!found) { ++ unsigned int i; ++ /* Setup the new edge list */ ++ list_for_each_entry(bus_node, &traverse_list, link) { ++ /* Setup list of black-listed nodes */ ++ setup_bl_list(bus_node, &black_list); ++ ++ for (i = 0; i < bus_node->node_info-> ++ num_connections; i++) { ++ bool skip; ++ struct msm_bus_node_device_type ++ *node_conn; ++ node_conn = bus_node->node_info-> ++ dev_connections[i]-> ++ platform_data; ++ if (node_conn->node_info-> ++ is_traversed) { ++ MSM_BUS_ERR("Circ Path %d\n", ++ node_conn->node_info->id); ++ goto reset_traversed; ++ } ++ skip = chk_bl_list(&black_list, ++ bus_node->node_info-> ++ connections[i]); ++ if (!skip) { ++ list_add_tail(&node_conn->link, ++ &edge_list); ++ node_conn->node_info-> ++ is_traversed = true; ++ } ++ } ++ } ++ ++ /* Keep tabs of the previous search list */ ++ search_node = kzalloc(sizeof(struct bus_search_type), ++ GFP_KERNEL); ++ INIT_LIST_HEAD(&search_node->node_list); ++ list_splice_init(&traverse_list, ++ &search_node->node_list); ++ /* Add the previous search list to a route list */ ++ list_add_tail(&search_node->link, &route_list); ++ /* Advancing the list depth */ ++ depth_index++; ++ list_splice_init(&edge_list, &traverse_list); ++ } ++ } ++reset_traversed: ++ copy_remaining_nodes(&edge_list, &traverse_list, &route_list); ++ first_hop = prune_path(&route_list, dest, src, &black_list, found); ++ ++exit_getpath: ++ return first_hop; ++} ++ ++static uint64_t arbitrate_bus_req(struct msm_bus_node_device_type *bus_dev, ++ int ctx) ++{ ++ int i; ++ uint64_t max_ib = 0; ++ uint64_t sum_ab = 0; ++ uint64_t bw_max_hz; ++ struct msm_bus_node_device_type *fab_dev = NULL; ++ uint32_t util_fact = 0; ++ uint32_t vrail_comp = 0; ++ ++ /* Find max ib */ ++ for (i = 0; i < bus_dev->num_lnodes; i++) { ++ max_ib = max(max_ib, bus_dev->lnode_list[i].lnode_ib[ctx]); ++ sum_ab += bus_dev->lnode_list[i].lnode_ab[ctx]; ++ } ++ /* ++ * Account for Util factor and vrail comp. The new aggregation ++ * formula is: ++ * Freq_hz = max((sum(ab) * util_fact)/num_chan, max(ib)/vrail_comp) ++ * / bus-width ++ * util_fact and vrail comp are obtained from fabric/Node's dts ++ * properties. ++ * They default to 100 if absent. ++ */ ++ fab_dev = bus_dev->node_info->bus_device->platform_data; ++ /* Don't do this for virtual fabrics */ ++ if (fab_dev && fab_dev->fabdev) { ++ util_fact = bus_dev->node_info->util_fact ? ++ bus_dev->node_info->util_fact : ++ fab_dev->fabdev->util_fact; ++ vrail_comp = bus_dev->node_info->vrail_comp ? ++ bus_dev->node_info->vrail_comp : ++ fab_dev->fabdev->vrail_comp; ++ sum_ab *= util_fact; ++ sum_ab = msm_bus_div64(100, sum_ab); ++ max_ib *= 100; ++ max_ib = msm_bus_div64(vrail_comp, max_ib); ++ } ++ ++ /* Account for multiple channels if any */ ++ if (bus_dev->node_info->num_qports > 1) ++ sum_ab = msm_bus_div64(bus_dev->node_info->num_qports, ++ sum_ab); ++ ++ if (!bus_dev->node_info->buswidth) { ++ MSM_BUS_WARN("No bus width found for %d. Using default\n", ++ bus_dev->node_info->id); ++ bus_dev->node_info->buswidth = 8; ++ } ++ ++ bw_max_hz = max(max_ib, sum_ab); ++ bw_max_hz = msm_bus_div64(bus_dev->node_info->buswidth, ++ bw_max_hz); ++ ++ return bw_max_hz; ++} ++ ++static void del_inp_list(struct list_head *list) ++{ ++ struct rule_update_path_info *rule_node; ++ struct rule_update_path_info *rule_node_tmp; ++ ++ list_for_each_entry_safe(rule_node, rule_node_tmp, list, link) ++ list_del(&rule_node->link); ++} ++ ++static void del_op_list(struct list_head *list) ++{ ++ struct rule_apply_rcm_info *rule; ++ struct rule_apply_rcm_info *rule_tmp; ++ ++ list_for_each_entry_safe(rule, rule_tmp, list, link) ++ list_del(&rule->link); ++} ++ ++static int msm_bus_apply_rules(struct list_head *list, bool after_clk_commit) ++{ ++ struct rule_apply_rcm_info *rule; ++ struct device *dev = NULL; ++ struct msm_bus_node_device_type *dev_info = NULL; ++ int ret = 0; ++ bool throttle_en = false; ++ ++ list_for_each_entry(rule, list, link) { ++ if (!rule) ++ break; ++ ++ if (rule && (rule->after_clk_commit != after_clk_commit)) ++ continue; ++ ++ dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &rule->id, ++ msm_bus_device_match_adhoc); ++ ++ if (!dev) { ++ MSM_BUS_ERR("Can't find dev node for %d", rule->id); ++ continue; ++ } ++ dev_info = dev->platform_data; ++ ++ throttle_en = ((rule->throttle == THROTTLE_ON) ? true : false); ++ ret = msm_bus_enable_limiter(dev_info, throttle_en, ++ rule->lim_bw); ++ if (ret) ++ MSM_BUS_ERR("Failed to set limiter for %d", rule->id); ++ } ++ ++ return ret; ++} ++ ++static uint64_t get_node_aggab(struct msm_bus_node_device_type *bus_dev) ++{ ++ int i; ++ int ctx; ++ uint64_t max_agg_ab = 0; ++ uint64_t agg_ab = 0; ++ ++ for (ctx = 0; ctx < NUM_CTX; ctx++) { ++ for (i = 0; i < bus_dev->num_lnodes; i++) ++ agg_ab += bus_dev->lnode_list[i].lnode_ab[ctx]; ++ ++ if (bus_dev->node_info->num_qports > 1) ++ agg_ab = msm_bus_div64(bus_dev->node_info->num_qports, ++ agg_ab); ++ ++ max_agg_ab = max(max_agg_ab, agg_ab); ++ } ++ ++ return max_agg_ab; ++} ++ ++static uint64_t get_node_ib(struct msm_bus_node_device_type *bus_dev) ++{ ++ int i; ++ int ctx; ++ uint64_t max_ib = 0; ++ ++ for (ctx = 0; ctx < NUM_CTX; ctx++) { ++ for (i = 0; i < bus_dev->num_lnodes; i++) ++ max_ib = max(max_ib, ++ bus_dev->lnode_list[i].lnode_ib[ctx]); ++ } ++ return max_ib; ++} ++ ++static int update_path(int src, int dest, uint64_t req_ib, uint64_t req_bw, ++ uint64_t cur_ib, uint64_t cur_bw, int src_idx, int ctx) ++{ ++ struct device *src_dev = NULL; ++ struct device *next_dev = NULL; ++ struct link_node *lnode = NULL; ++ struct msm_bus_node_device_type *dev_info = NULL; ++ int curr_idx; ++ int ret = 0; ++ int *dirty_nodes = NULL; ++ int num_dirty = 0; ++ struct rule_update_path_info *rule_node; ++ bool rules_registered = msm_rule_are_rules_registered(); ++ ++ src_dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &src, ++ msm_bus_device_match_adhoc); ++ ++ if (!src_dev) { ++ MSM_BUS_ERR("%s: Can't find source device %d", __func__, src); ++ ret = -ENODEV; ++ goto exit_update_path; ++ } ++ ++ next_dev = src_dev; ++ ++ if (src_idx < 0) { ++ MSM_BUS_ERR("%s: Invalid lnode idx %d", __func__, src_idx); ++ ret = -ENXIO; ++ goto exit_update_path; ++ } ++ curr_idx = src_idx; ++ ++ INIT_LIST_HEAD(&input_list); ++ INIT_LIST_HEAD(&apply_list); ++ ++ while (next_dev) { ++ dev_info = next_dev->platform_data; ++ ++ if (curr_idx >= dev_info->num_lnodes) { ++ MSM_BUS_ERR("%s: Invalid lnode Idx %d num lnodes %d", ++ __func__, curr_idx, dev_info->num_lnodes); ++ ret = -ENXIO; ++ goto exit_update_path; ++ } ++ ++ lnode = &dev_info->lnode_list[curr_idx]; ++ lnode->lnode_ib[ctx] = req_ib; ++ lnode->lnode_ab[ctx] = req_bw; ++ ++ dev_info->cur_clk_hz[ctx] = arbitrate_bus_req(dev_info, ctx); ++ ++ /* Start updating the clocks at the first hop. ++ * Its ok to figure out the aggregated ++ * request at this node. ++ */ ++ if (src_dev != next_dev) { ++ ret = msm_bus_update_clks(dev_info, ctx, &dirty_nodes, ++ &num_dirty); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to update clks dev %d", ++ __func__, dev_info->node_info->id); ++ goto exit_update_path; ++ } ++ } ++ ++ ret = msm_bus_update_bw(dev_info, ctx, req_bw, &dirty_nodes, ++ &num_dirty); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to update bw dev %d", ++ __func__, dev_info->node_info->id); ++ goto exit_update_path; ++ } ++ ++ if (rules_registered) { ++ rule_node = &dev_info->node_info->rule; ++ rule_node->id = dev_info->node_info->id; ++ rule_node->ib = get_node_ib(dev_info); ++ rule_node->ab = get_node_aggab(dev_info); ++ rule_node->clk = max(dev_info->cur_clk_hz[ACTIVE_CTX], ++ dev_info->cur_clk_hz[DUAL_CTX]); ++ list_add_tail(&rule_node->link, &input_list); ++ } ++ ++ next_dev = lnode->next_dev; ++ curr_idx = lnode->next; ++ } ++ ++ if (rules_registered) { ++ msm_rules_update_path(&input_list, &apply_list); ++ msm_bus_apply_rules(&apply_list, false); ++ } ++ ++ msm_bus_commit_data(dirty_nodes, ctx, num_dirty); ++ ++ if (rules_registered) { ++ msm_bus_apply_rules(&apply_list, true); ++ del_inp_list(&input_list); ++ del_op_list(&apply_list); ++ } ++exit_update_path: ++ return ret; ++} ++ ++static int remove_path(int src, int dst, uint64_t cur_ib, uint64_t cur_ab, ++ int src_idx, int active_only) ++{ ++ struct device *src_dev = NULL; ++ struct device *next_dev = NULL; ++ struct link_node *lnode = NULL; ++ struct msm_bus_node_device_type *dev_info = NULL; ++ int ret = 0; ++ int cur_idx = src_idx; ++ int next_idx; ++ ++ /* Update the current path to zero out all request from ++ * this cient on all paths ++ */ ++ ++ ret = update_path(src, dst, 0, 0, cur_ib, cur_ab, src_idx, ++ active_only); ++ if (ret) { ++ MSM_BUS_ERR("%s: Error zeroing out path ctx %d", ++ __func__, ACTIVE_CTX); ++ goto exit_remove_path; ++ } ++ ++ src_dev = bus_find_device(&msm_bus_type, NULL, ++ (void *) &src, ++ msm_bus_device_match_adhoc); ++ if (!src_dev) { ++ MSM_BUS_ERR("%s: Can't find source device %d", __func__, src); ++ ret = -ENODEV; ++ goto exit_remove_path; ++ } ++ ++ next_dev = src_dev; ++ ++ while (next_dev) { ++ dev_info = next_dev->platform_data; ++ lnode = &dev_info->lnode_list[cur_idx]; ++ next_idx = lnode->next; ++ next_dev = lnode->next_dev; ++ remove_lnode(dev_info, cur_idx); ++ cur_idx = next_idx; ++ } ++ ++exit_remove_path: ++ return ret; ++} ++ ++static void getpath_debug(int src, int curr, int active_only) ++{ ++ struct device *dev_node; ++ struct device *dev_it; ++ unsigned int hop = 1; ++ int idx; ++ struct msm_bus_node_device_type *devinfo; ++ int i; ++ ++ dev_node = bus_find_device(&msm_bus_type, NULL, ++ (void *) &src, ++ msm_bus_device_match_adhoc); ++ ++ if (!dev_node) { ++ MSM_BUS_ERR("SRC NOT FOUND %d", src); ++ return; ++ } ++ ++ idx = curr; ++ devinfo = dev_node->platform_data; ++ dev_it = dev_node; ++ ++ MSM_BUS_ERR("Route list Src %d", src); ++ while (dev_it) { ++ struct msm_bus_node_device_type *busdev = ++ devinfo->node_info->bus_device->platform_data; ++ ++ MSM_BUS_ERR("Hop[%d] at Device %d ctx %d", hop, ++ devinfo->node_info->id, active_only); ++ ++ for (i = 0; i < NUM_CTX; i++) { ++ MSM_BUS_ERR("dev info sel ib %llu", ++ devinfo->cur_clk_hz[i]); ++ MSM_BUS_ERR("dev info sel ab %llu", ++ devinfo->node_ab.ab[i]); ++ } ++ ++ dev_it = devinfo->lnode_list[idx].next_dev; ++ idx = devinfo->lnode_list[idx].next; ++ if (dev_it) ++ devinfo = dev_it->platform_data; ++ ++ MSM_BUS_ERR("Bus Device %d", busdev->node_info->id); ++ MSM_BUS_ERR("Bus Clock %llu", busdev->clk[active_only].rate); ++ ++ if (idx < 0) ++ break; ++ hop++; ++ } ++} ++ ++static void unregister_client_adhoc(uint32_t cl) ++{ ++ int i; ++ struct msm_bus_scale_pdata *pdata; ++ int lnode, src, curr, dest; ++ uint64_t cur_clk, cur_bw; ++ struct msm_bus_client *client; ++ ++ mutex_lock(&msm_bus_adhoc_lock); ++ if (!cl) { ++ MSM_BUS_ERR("%s: Null cl handle passed unregister\n", ++ __func__); ++ goto exit_unregister_client; ++ } ++ client = handle_list.cl_list[cl]; ++ pdata = client->pdata; ++ if (!pdata) { ++ MSM_BUS_ERR("%s: Null pdata passed to unregister\n", ++ __func__); ++ goto exit_unregister_client; ++ } ++ ++ curr = client->curr; ++ if (curr >= pdata->num_usecases) { ++ MSM_BUS_ERR("Invalid index Defaulting curr to 0"); ++ curr = 0; ++ } ++ ++ MSM_BUS_DBG("%s: Unregistering client %p", __func__, client); ++ ++ for (i = 0; i < pdata->usecase->num_paths; i++) { ++ src = client->pdata->usecase[curr].vectors[i].src; ++ dest = client->pdata->usecase[curr].vectors[i].dst; ++ ++ lnode = client->src_pnode[i]; ++ cur_clk = client->pdata->usecase[curr].vectors[i].ib; ++ cur_bw = client->pdata->usecase[curr].vectors[i].ab; ++ remove_path(src, dest, cur_clk, cur_bw, lnode, ++ pdata->active_only); ++ } ++ msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_UNREGISTER, cl); ++ kfree(client->src_pnode); ++ kfree(client); ++ handle_list.cl_list[cl] = NULL; ++exit_unregister_client: ++ mutex_unlock(&msm_bus_adhoc_lock); ++ return; ++} ++ ++static int alloc_handle_lst(int size) ++{ ++ int ret = 0; ++ struct msm_bus_client **t_cl_list; ++ ++ if (!handle_list.num_entries) { ++ t_cl_list = kzalloc(sizeof(struct msm_bus_client *) ++ * NUM_CL_HANDLES, GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(t_cl_list)) { ++ ret = -ENOMEM; ++ MSM_BUS_ERR("%s: Failed to allocate handles list", ++ __func__); ++ goto exit_alloc_handle_lst; ++ } ++ handle_list.cl_list = t_cl_list; ++ handle_list.num_entries += NUM_CL_HANDLES; ++ } else { ++ t_cl_list = krealloc(handle_list.cl_list, ++ sizeof(struct msm_bus_client *) * ++ handle_list.num_entries + NUM_CL_HANDLES, ++ GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(t_cl_list)) { ++ ret = -ENOMEM; ++ MSM_BUS_ERR("%s: Failed to allocate handles list", ++ __func__); ++ goto exit_alloc_handle_lst; ++ } ++ ++ memset(&handle_list.cl_list[handle_list.num_entries], 0, ++ NUM_CL_HANDLES * sizeof(struct msm_bus_client *)); ++ handle_list.num_entries += NUM_CL_HANDLES; ++ handle_list.cl_list = t_cl_list; ++ } ++exit_alloc_handle_lst: ++ return ret; ++} ++ ++static uint32_t gen_handle(struct msm_bus_client *client) ++{ ++ uint32_t handle = 0; ++ int i; ++ int ret = 0; ++ ++ for (i = 0; i < handle_list.num_entries; i++) { ++ if (i && !handle_list.cl_list[i]) { ++ handle = i; ++ break; ++ } ++ } ++ ++ if (!handle) { ++ ret = alloc_handle_lst(NUM_CL_HANDLES); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to allocate handle list", ++ __func__); ++ goto exit_gen_handle; ++ } ++ handle = i + 1; ++ } ++ handle_list.cl_list[handle] = client; ++exit_gen_handle: ++ return handle; ++} ++ ++static uint32_t register_client_adhoc(struct msm_bus_scale_pdata *pdata) ++{ ++ int src, dest; ++ int i; ++ struct msm_bus_client *client = NULL; ++ int *lnode; ++ uint32_t handle = 0; ++ ++ mutex_lock(&msm_bus_adhoc_lock); ++ client = kzalloc(sizeof(struct msm_bus_client), GFP_KERNEL); ++ if (!client) { ++ MSM_BUS_ERR("%s: Error allocating client data", __func__); ++ goto exit_register_client; ++ } ++ client->pdata = pdata; ++ ++ lnode = kzalloc(pdata->usecase->num_paths * sizeof(int), GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(lnode)) { ++ MSM_BUS_ERR("%s: Error allocating pathnode ptr!", __func__); ++ goto exit_register_client; ++ } ++ client->src_pnode = lnode; ++ ++ for (i = 0; i < pdata->usecase->num_paths; i++) { ++ src = pdata->usecase->vectors[i].src; ++ dest = pdata->usecase->vectors[i].dst; ++ ++ if ((src < 0) || (dest < 0)) { ++ MSM_BUS_ERR("%s:Invalid src/dst.src %d dest %d", ++ __func__, src, dest); ++ goto exit_register_client; ++ } ++ ++ lnode[i] = getpath(src, dest); ++ if (lnode[i] < 0) { ++ MSM_BUS_ERR("%s:Failed to find path.src %d dest %d", ++ __func__, src, dest); ++ goto exit_register_client; ++ } ++ } ++ ++ handle = gen_handle(client); ++ msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_REGISTER, ++ handle); ++ MSM_BUS_DBG("%s:Client handle %d %s", __func__, handle, ++ client->pdata->name); ++exit_register_client: ++ mutex_unlock(&msm_bus_adhoc_lock); ++ return handle; ++} ++ ++static int update_request_adhoc(uint32_t cl, unsigned int index) ++{ ++ int i, ret = 0; ++ struct msm_bus_scale_pdata *pdata; ++ int lnode, src, curr, dest; ++ uint64_t req_clk, req_bw, curr_clk, curr_bw; ++ struct msm_bus_client *client; ++ const char *test_cl = "Null"; ++ bool log_transaction = false; ++ ++ mutex_lock(&msm_bus_adhoc_lock); ++ ++ if (!cl) { ++ MSM_BUS_ERR("%s: Invalid client handle %d", __func__, cl); ++ ret = -ENXIO; ++ goto exit_update_request; ++ } ++ ++ client = handle_list.cl_list[cl]; ++ pdata = client->pdata; ++ if (!pdata) { ++ MSM_BUS_ERR("%s: Client data Null.[client didn't register]", ++ __func__); ++ ret = -ENXIO; ++ goto exit_update_request; ++ } ++ ++ if (index >= pdata->num_usecases) { ++ MSM_BUS_ERR("Client %u passed invalid index: %d\n", ++ cl, index); ++ ret = -ENXIO; ++ goto exit_update_request; ++ } ++ ++ if (client->curr == index) { ++ MSM_BUS_DBG("%s: Not updating client request idx %d unchanged", ++ __func__, index); ++ goto exit_update_request; ++ } ++ ++ curr = client->curr; ++ client->curr = index; ++ ++ if (!strcmp(test_cl, pdata->name)) ++ log_transaction = true; ++ ++ MSM_BUS_DBG("%s: cl: %u index: %d curr: %d num_paths: %d\n", __func__, ++ cl, index, client->curr, client->pdata->usecase->num_paths); ++ ++ for (i = 0; i < pdata->usecase->num_paths; i++) { ++ src = client->pdata->usecase[index].vectors[i].src; ++ dest = client->pdata->usecase[index].vectors[i].dst; ++ ++ lnode = client->src_pnode[i]; ++ req_clk = client->pdata->usecase[index].vectors[i].ib; ++ req_bw = client->pdata->usecase[index].vectors[i].ab; ++ if (curr < 0) { ++ curr_clk = 0; ++ curr_bw = 0; ++ } else { ++ curr_clk = client->pdata->usecase[curr].vectors[i].ib; ++ curr_bw = client->pdata->usecase[curr].vectors[i].ab; ++ MSM_BUS_DBG("%s:ab: %llu ib: %llu\n", __func__, ++ curr_bw, curr_clk); ++ } ++ ++ ret = update_path(src, dest, req_clk, req_bw, ++ curr_clk, curr_bw, lnode, pdata->active_only); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Update path failed! %d ctx %d\n", ++ __func__, ret, ACTIVE_CTX); ++ goto exit_update_request; ++ } ++ ++ if (log_transaction) ++ getpath_debug(src, lnode, pdata->active_only); ++ } ++ msm_bus_dbg_client_data(client->pdata, index , cl); ++exit_update_request: ++ mutex_unlock(&msm_bus_adhoc_lock); ++ return ret; ++} ++ ++/** ++ * msm_bus_arb_setops_adhoc() : Setup the bus arbitration ops ++ * @ arb_ops: pointer to the arb ops. ++ */ ++void msm_bus_arb_setops_adhoc(struct msm_bus_arb_ops *arb_ops) ++{ ++ arb_ops->register_client = register_client_adhoc; ++ arb_ops->update_request = update_request_adhoc; ++ arb_ops->unregister_client = unregister_client_adhoc; ++} +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_bimc.c +@@ -0,0 +1,2112 @@ ++/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: BIMC: %s(): " fmt, __func__ ++ ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm_bus_core.h" ++#include "msm_bus_bimc.h" ++#include "msm_bus_adhoc.h" ++#include ++ ++enum msm_bus_bimc_slave_block { ++ SLAVE_BLOCK_RESERVED = 0, ++ SLAVE_BLOCK_SLAVE_WAY, ++ SLAVE_BLOCK_XPU, ++ SLAVE_BLOCK_ARBITER, ++ SLAVE_BLOCK_SCMO, ++}; ++ ++enum bke_sw { ++ BKE_OFF = 0, ++ BKE_ON = 1, ++}; ++ ++/* M_Generic */ ++ ++#define M_REG_BASE(b) ((b) + 0x00008000) ++ ++#define M_COMPONENT_INFO_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000000) ++enum bimc_m_component_info { ++ M_COMPONENT_INFO_RMSK = 0xffffff, ++ M_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000, ++ M_COMPONENT_INFO_INSTANCE_SHFT = 0x10, ++ M_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00, ++ M_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8, ++ M_COMPONENT_INFO_TYPE_BMSK = 0xff, ++ M_COMPONENT_INFO_TYPE_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_0_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000020) ++enum bimc_m_config_info_0 { ++ M_CONFIG_INFO_0_RMSK = 0xff00ffff, ++ M_CONFIG_INFO_0_SYNC_MODE_BMSK = 0xff000000, ++ M_CONFIG_INFO_0_SYNC_MODE_SHFT = 0x18, ++ M_CONFIG_INFO_0_CONNECTION_TYPE_BMSK = 0xff00, ++ M_CONFIG_INFO_0_CONNECTION_TYPE_SHFT = 0x8, ++ M_CONFIG_INFO_0_FUNC_BMSK = 0xff, ++ M_CONFIG_INFO_0_FUNC_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_1_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000030) ++enum bimc_m_config_info_1 { ++ M_CONFIG_INFO_1_RMSK = 0xffffffff, ++ M_CONFIG_INFO_1_SWAY_CONNECTIVITY_BMSK = 0xffffffff, ++ M_CONFIG_INFO_1_SWAY_CONNECTIVITY_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_2_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000040) ++enum bimc_m_config_info_2 { ++ M_CONFIG_INFO_2_RMSK = 0xffffffff, ++ M_CONFIG_INFO_2_M_DATA_WIDTH_BMSK = 0xffff0000, ++ M_CONFIG_INFO_2_M_DATA_WIDTH_SHFT = 0x10, ++ M_CONFIG_INFO_2_M_TID_WIDTH_BMSK = 0xff00, ++ M_CONFIG_INFO_2_M_TID_WIDTH_SHFT = 0x8, ++ M_CONFIG_INFO_2_M_MID_WIDTH_BMSK = 0xff, ++ M_CONFIG_INFO_2_M_MID_WIDTH_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_3_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000050) ++enum bimc_m_config_info_3 { ++ M_CONFIG_INFO_3_RMSK = 0xffffffff, ++ M_CONFIG_INFO_3_RCH_DEPTH_BMSK = 0xff000000, ++ M_CONFIG_INFO_3_RCH_DEPTH_SHFT = 0x18, ++ M_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff0000, ++ M_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x10, ++ M_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff00, ++ M_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x8, ++ M_CONFIG_INFO_3_ACH_DEPTH_BMSK = 0xff, ++ M_CONFIG_INFO_3_ACH_DEPTH_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_4_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000060) ++enum bimc_m_config_info_4 { ++ M_CONFIG_INFO_4_RMSK = 0xffff, ++ M_CONFIG_INFO_4_REORDER_BUF_DEPTH_BMSK = 0xff00, ++ M_CONFIG_INFO_4_REORDER_BUF_DEPTH_SHFT = 0x8, ++ M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_BMSK = 0xff, ++ M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_SHFT = 0x0, ++}; ++ ++#define M_CONFIG_INFO_5_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000070) ++enum bimc_m_config_info_5 { ++ M_CONFIG_INFO_5_RMSK = 0x111, ++ M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_BMSK = 0x100, ++ M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_SHFT = 0x8, ++ M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_BMSK = 0x10, ++ M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_SHFT = 0x4, ++ M_CONFIG_INFO_5_M2MP_PIPELINE_EN_BMSK = 0x1, ++ M_CONFIG_INFO_5_M2MP_PIPELINE_EN_SHFT = 0x0, ++}; ++ ++#define M_INT_STATUS_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000100) ++enum bimc_m_int_status { ++ M_INT_STATUS_RMSK = 0x3, ++}; ++ ++#define M_INT_CLR_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000108) ++enum bimc_m_int_clr { ++ M_INT_CLR_RMSK = 0x3, ++}; ++ ++#define M_INT_EN_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x0000010c) ++enum bimc_m_int_en { ++ M_INT_EN_RMSK = 0x3, ++}; ++ ++#define M_CLK_CTRL_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000200) ++enum bimc_m_clk_ctrl { ++ M_CLK_CTRL_RMSK = 0x3, ++ M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK = 0x2, ++ M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT = 0x1, ++ M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1, ++ M_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0, ++}; ++ ++#define M_MODE_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000210) ++enum bimc_m_mode { ++ M_MODE_RMSK = 0xf0000011, ++ M_MODE_WR_GATHER_BEATS_BMSK = 0xf0000000, ++ M_MODE_WR_GATHER_BEATS_SHFT = 0x1c, ++ M_MODE_NARROW_WR_BMSK = 0x10, ++ M_MODE_NARROW_WR_SHFT = 0x4, ++ M_MODE_ORDERING_MODEL_BMSK = 0x1, ++ M_MODE_ORDERING_MODEL_SHFT = 0x0, ++}; ++ ++#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230) ++enum bimc_m_priolvl_override { ++ M_PRIOLVL_OVERRIDE_RMSK = 0x301, ++ M_PRIOLVL_OVERRIDE_BMSK = 0x300, ++ M_PRIOLVL_OVERRIDE_SHFT = 0x8, ++ M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK = 0x1, ++ M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT = 0x0, ++}; ++ ++#define M_RD_CMD_OVERRIDE_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240) ++enum bimc_m_read_command_override { ++ M_RD_CMD_OVERRIDE_RMSK = 0x3071f7f, ++ M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000, ++ M_RD_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18, ++ M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000, ++ M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10, ++ M_RD_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000, ++ M_RD_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc, ++ M_RD_CMD_OVERRIDE_ASHARED_BMSK = 0x800, ++ M_RD_CMD_OVERRIDE_ASHARED_SHFT = 0xb, ++ M_RD_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400, ++ M_RD_CMD_OVERRIDE_AREDIRECT_SHFT = 0xa, ++ M_RD_CMD_OVERRIDE_AOOO_BMSK = 0x200, ++ M_RD_CMD_OVERRIDE_AOOO_SHFT = 0x9, ++ M_RD_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100, ++ M_RD_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6, ++ M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20, ++ M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4, ++ M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8, ++ M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT = 0x3, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK = 0x4, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT = 0x2, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK = 0x2, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT = 0x1, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK = 0x1, ++ M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT = 0x0, ++}; ++ ++#define M_WR_CMD_OVERRIDE_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250) ++enum bimc_m_write_command_override { ++ M_WR_CMD_OVERRIDE_RMSK = 0x3071f7f, ++ M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000, ++ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18, ++ M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000, ++ M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10, ++ M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000, ++ M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc, ++ M_WR_CMD_OVERRIDE_ASHARED_BMSK = 0x800, ++ M_WR_CMD_OVERRIDE_ASHARED_SHFT = 0xb, ++ M_WR_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400, ++ M_WR_CMD_OVERRIDE_AREDIRECT_SHFT = 0xa, ++ M_WR_CMD_OVERRIDE_AOOO_BMSK = 0x200, ++ M_WR_CMD_OVERRIDE_AOOO_SHFT = 0x9, ++ M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100, ++ M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6, ++ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20, ++ M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4, ++ M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8, ++ M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT = 0x3, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK = 0x4, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT = 0x2, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK = 0x2, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT = 0x1, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK = 0x1, ++ M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT = 0x0, ++}; ++ ++#define M_BKE_EN_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000300) ++enum bimc_m_bke_en { ++ M_BKE_EN_RMSK = 0x1, ++ M_BKE_EN_EN_BMSK = 0x1, ++ M_BKE_EN_EN_SHFT = 0x0, ++}; ++ ++/* Grant Period registers */ ++#define M_BKE_GP_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000304) ++enum bimc_m_bke_grant_period { ++ M_BKE_GP_RMSK = 0x3ff, ++ M_BKE_GP_GP_BMSK = 0x3ff, ++ M_BKE_GP_GP_SHFT = 0x0, ++}; ++ ++/* Grant count register. ++ * The Grant count register represents a signed 16 bit ++ * value, range 0-0x7fff ++ */ ++#define M_BKE_GC_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000308) ++enum bimc_m_bke_grant_count { ++ M_BKE_GC_RMSK = 0xffff, ++ M_BKE_GC_GC_BMSK = 0xffff, ++ M_BKE_GC_GC_SHFT = 0x0, ++}; ++ ++/* Threshold High Registers */ ++#define M_BKE_THH_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000320) ++enum bimc_m_bke_thresh_high { ++ M_BKE_THH_RMSK = 0xffff, ++ M_BKE_THH_THRESH_BMSK = 0xffff, ++ M_BKE_THH_THRESH_SHFT = 0x0, ++}; ++ ++/* Threshold Medium Registers */ ++#define M_BKE_THM_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000324) ++enum bimc_m_bke_thresh_medium { ++ M_BKE_THM_RMSK = 0xffff, ++ M_BKE_THM_THRESH_BMSK = 0xffff, ++ M_BKE_THM_THRESH_SHFT = 0x0, ++}; ++ ++/* Threshold Low Registers */ ++#define M_BKE_THL_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000328) ++enum bimc_m_bke_thresh_low { ++ M_BKE_THL_RMSK = 0xffff, ++ M_BKE_THL_THRESH_BMSK = 0xffff, ++ M_BKE_THL_THRESH_SHFT = 0x0, ++}; ++ ++#define M_BKE_HEALTH_0_CONFIG_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000340) ++enum bimc_m_bke_health_0 { ++ M_BKE_HEALTH_0_CONFIG_RMSK = 0x80000303, ++ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK = 0x80000000, ++ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT = 0x1f, ++ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK = 0x300, ++ M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT = 0x8, ++ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK = 0x3, ++ M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT = 0x0, ++}; ++ ++#define M_BKE_HEALTH_1_CONFIG_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000344) ++enum bimc_m_bke_health_1 { ++ M_BKE_HEALTH_1_CONFIG_RMSK = 0x80000303, ++ M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_BMSK = 0x80000000, ++ M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_SHFT = 0x1f, ++ M_BKE_HEALTH_1_CONFIG_AREQPRIO_BMSK = 0x300, ++ M_BKE_HEALTH_1_CONFIG_AREQPRIO_SHFT = 0x8, ++ M_BKE_HEALTH_1_CONFIG_PRIOLVL_BMSK = 0x3, ++ M_BKE_HEALTH_1_CONFIG_PRIOLVL_SHFT = 0x0, ++}; ++ ++#define M_BKE_HEALTH_2_CONFIG_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000348) ++enum bimc_m_bke_health_2 { ++ M_BKE_HEALTH_2_CONFIG_RMSK = 0x80000303, ++ M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_BMSK = 0x80000000, ++ M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_SHFT = 0x1f, ++ M_BKE_HEALTH_2_CONFIG_AREQPRIO_BMSK = 0x300, ++ M_BKE_HEALTH_2_CONFIG_AREQPRIO_SHFT = 0x8, ++ M_BKE_HEALTH_2_CONFIG_PRIOLVL_BMSK = 0x3, ++ M_BKE_HEALTH_2_CONFIG_PRIOLVL_SHFT = 0x0, ++}; ++ ++#define M_BKE_HEALTH_3_CONFIG_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x0000034c) ++enum bimc_m_bke_health_3 { ++ M_BKE_HEALTH_3_CONFIG_RMSK = 0x303, ++ M_BKE_HEALTH_3_CONFIG_AREQPRIO_BMSK = 0x300, ++ M_BKE_HEALTH_3_CONFIG_AREQPRIO_SHFT = 0x8, ++ M_BKE_HEALTH_3_CONFIG_PRIOLVL_BMSK = 0x3, ++ M_BKE_HEALTH_3_CONFIG_PRIOLVL_SHFT = 0x0, ++}; ++ ++#define M_BUF_STATUS_ADDR(b, n) \ ++ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000400) ++enum bimc_m_buf_status { ++ M_BUF_STATUS_RMSK = 0xf03f030, ++ M_BUF_STATUS_RCH_DATA_WR_FULL_BMSK = 0x8000000, ++ M_BUF_STATUS_RCH_DATA_WR_FULL_SHFT = 0x1b, ++ M_BUF_STATUS_RCH_DATA_WR_EMPTY_BMSK = 0x4000000, ++ M_BUF_STATUS_RCH_DATA_WR_EMPTY_SHFT = 0x1a, ++ M_BUF_STATUS_RCH_CTRL_WR_FULL_BMSK = 0x2000000, ++ M_BUF_STATUS_RCH_CTRL_WR_FULL_SHFT = 0x19, ++ M_BUF_STATUS_RCH_CTRL_WR_EMPTY_BMSK = 0x1000000, ++ M_BUF_STATUS_RCH_CTRL_WR_EMPTY_SHFT = 0x18, ++ M_BUF_STATUS_BCH_WR_FULL_BMSK = 0x20000, ++ M_BUF_STATUS_BCH_WR_FULL_SHFT = 0x11, ++ M_BUF_STATUS_BCH_WR_EMPTY_BMSK = 0x10000, ++ M_BUF_STATUS_BCH_WR_EMPTY_SHFT = 0x10, ++ M_BUF_STATUS_WCH_DATA_RD_FULL_BMSK = 0x8000, ++ M_BUF_STATUS_WCH_DATA_RD_FULL_SHFT = 0xf, ++ M_BUF_STATUS_WCH_DATA_RD_EMPTY_BMSK = 0x4000, ++ M_BUF_STATUS_WCH_DATA_RD_EMPTY_SHFT = 0xe, ++ M_BUF_STATUS_WCH_CTRL_RD_FULL_BMSK = 0x2000, ++ M_BUF_STATUS_WCH_CTRL_RD_FULL_SHFT = 0xd, ++ M_BUF_STATUS_WCH_CTRL_RD_EMPTY_BMSK = 0x1000, ++ M_BUF_STATUS_WCH_CTRL_RD_EMPTY_SHFT = 0xc, ++ M_BUF_STATUS_ACH_RD_FULL_BMSK = 0x20, ++ M_BUF_STATUS_ACH_RD_FULL_SHFT = 0x5, ++ M_BUF_STATUS_ACH_RD_EMPTY_BMSK = 0x10, ++ M_BUF_STATUS_ACH_RD_EMPTY_SHFT = 0x4, ++}; ++/*BIMC Generic */ ++ ++#define S_REG_BASE(b) ((b) + 0x00048000) ++ ++#define S_COMPONENT_INFO_ADDR(b, n) \ ++ (S_REG_BASE(b) + (0x8000 * (n)) + 0x00000000) ++enum bimc_s_component_info { ++ S_COMPONENT_INFO_RMSK = 0xffffff, ++ S_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000, ++ S_COMPONENT_INFO_INSTANCE_SHFT = 0x10, ++ S_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00, ++ S_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8, ++ S_COMPONENT_INFO_TYPE_BMSK = 0xff, ++ S_COMPONENT_INFO_TYPE_SHFT = 0x0, ++}; ++ ++#define S_HW_INFO_ADDR(b, n) \ ++ (S_REG_BASE(b) + (0x80000 * (n)) + 0x00000010) ++enum bimc_s_hw_info { ++ S_HW_INFO_RMSK = 0xffffffff, ++ S_HW_INFO_MAJOR_BMSK = 0xff000000, ++ S_HW_INFO_MAJOR_SHFT = 0x18, ++ S_HW_INFO_BRANCH_BMSK = 0xff0000, ++ S_HW_INFO_BRANCH_SHFT = 0x10, ++ S_HW_INFO_MINOR_BMSK = 0xff00, ++ S_HW_INFO_MINOR_SHFT = 0x8, ++ S_HW_INFO_ECO_BMSK = 0xff, ++ S_HW_INFO_ECO_SHFT = 0x0, ++}; ++ ++ ++/* S_SCMO_GENERIC */ ++ ++#define S_SCMO_REG_BASE(b) ((b) + 0x00048000) ++ ++#define S_SCMO_CONFIG_INFO_0_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000020) ++enum bimc_s_scmo_config_info_0 { ++ S_SCMO_CONFIG_INFO_0_RMSK = 0xffffffff, ++ S_SCMO_CONFIG_INFO_0_DATA_WIDTH_BMSK = 0xffff0000, ++ S_SCMO_CONFIG_INFO_0_DATA_WIDTH_SHFT = 0x10, ++ S_SCMO_CONFIG_INFO_0_TID_WIDTH_BMSK = 0xff00, ++ S_SCMO_CONFIG_INFO_0_TID_WIDTH_SHFT = 0x8, ++ S_SCMO_CONFIG_INFO_0_MID_WIDTH_BMSK = 0xff, ++ S_SCMO_CONFIG_INFO_0_MID_WIDTH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_1_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000030) ++enum bimc_s_scmo_config_info_1 { ++ S_SCMO_CONFIG_INFO_1_RMSK = 0xffffffff, ++ S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff, ++ S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_2_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000040) ++enum bimc_s_scmo_config_info_2 { ++ S_SCMO_CONFIG_INFO_2_RMSK = 0xff00ff, ++ S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_BMSK = 0xff0000, ++ S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_SHFT = 0x10, ++ S_SCMO_CONFIG_INFO_2_VMID_WIDTH_BMSK = 0xff, ++ S_SCMO_CONFIG_INFO_2_VMID_WIDTH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_3_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000050) ++enum bimc_s_scmo_config_info_3 { ++ S_SCMO_CONFIG_INFO_3_RMSK = 0xffffffff, ++ S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_BMSK = 0xff000000, ++ S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_SHFT = 0x18, ++ S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_BMSK = 0xff0000, ++ S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_SHFT = 0x10, ++ S_SCMO_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff00, ++ S_SCMO_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x8, ++ S_SCMO_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff, ++ S_SCMO_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_4_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000060) ++enum bimc_s_scmo_config_info_4 { ++ S_SCMO_CONFIG_INFO_4_RMSK = 0xffff, ++ S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_BMSK = 0xff00, ++ S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_SHFT = 0x8, ++ S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_BMSK = 0xff, ++ S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_5_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000070) ++enum bimc_s_scmo_config_info_5 { ++ S_SCMO_CONFIG_INFO_5_RMSK = 0xffff, ++ S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_BMSK = 0xff00, ++ S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_SHFT = 0x8, ++ S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_BMSK = 0xff, ++ S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CONFIG_INFO_6_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000080) ++enum bimc_s_scmo_config_info_6 { ++ S_SCMO_CONFIG_INFO_6_RMSK = 0x1111, ++ S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_BMSK = 0x1000, ++ S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_SHFT = 0xc, ++ S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_BMSK = 0x100, ++ S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_SHFT = 0x8, ++ S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_BMSK = 0x10, ++ S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_SHFT = 0x4, ++ S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_BMSK = 0x1, ++ S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_SHFT = 0x0, ++}; ++ ++#define S_SCMO_INT_STATUS_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000100) ++enum bimc_s_scmo_int_status { ++ S_SCMO_INT_STATUS_RMSK = 0x1, ++ S_SCMO_INT_STATUS_ERR_OCCURED_BMSK = 0x1, ++ S_SCMO_INT_STATUS_ERR_OCCURED_SHFT = 0x0, ++}; ++ ++#define S_SCMO_INT_CLR_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000108) ++enum bimc_s_scmo_int_clr { ++ S_SCMO_INT_CLR_RMSK = 0x1, ++ S_SCMO_INT_CLR_IRQ_CLR_BMSK = 0x1, ++ S_SCMO_INT_CLR_IRQ_CLR_SHFT = 0x0, ++}; ++ ++#define S_SCMO_INT_EN_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c) ++enum bimc_s_scmo_int_en { ++ S_SCMO_INT_EN_RMSK = 0x1, ++ S_SCMO_INT_EN_IRQ_EN_BMSK = 0x1, ++ S_SCMO_INT_EN_IRQ_EN_SHFT = 0x0, ++}; ++ ++#define S_SCMO_ESYN_ADDR_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000120) ++enum bimc_s_scmo_esyn_addr { ++ S_SCMO_ESYN_ADDR_RMSK = 0xffffffff, ++ S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_BMSK = 0xffffffff, ++ S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_SHFT = 0x0, ++}; ++ ++#define S_SCMO_ESYN_APACKET_0_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000128) ++enum bimc_s_scmo_esyn_apacket_0 { ++ S_SCMO_ESYN_APACKET_0_RMSK = 0xff1fffff, ++ S_SCMO_ESYN_APACKET_0_ERR_ATID_BMSK = 0xff000000, ++ S_SCMO_ESYN_APACKET_0_ERR_ATID_SHFT = 0x18, ++ S_SCMO_ESYN_APACKET_0_ERR_AVMID_BMSK = 0x1f0000, ++ S_SCMO_ESYN_APACKET_0_ERR_AVMID_SHFT = 0x10, ++ S_SCMO_ESYN_APACKET_0_ERR_AMID_BMSK = 0xffff, ++ S_SCMO_ESYN_APACKET_0_ERR_AMID_SHFT = 0x0, ++}; ++ ++#define S_SCMO_ESYN_APACKET_1_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000012c) ++enum bimc_s_scmo_esyn_apacket_1 { ++ S_SCMO_ESYN_APACKET_1_RMSK = 0x10ff117, ++ S_SCMO_ESYN_APACKET_1_ERR_CODE_BMSK = 0x1000000, ++ S_SCMO_ESYN_APACKET_1_ERR_CODE_SHFT = 0x18, ++ S_SCMO_ESYN_APACKET_1_ERR_ALEN_BMSK = 0xf0000, ++ S_SCMO_ESYN_APACKET_1_ERR_ALEN_SHFT = 0x10, ++ S_SCMO_ESYN_APACKET_1_ERR_ASIZE_BMSK = 0xe000, ++ S_SCMO_ESYN_APACKET_1_ERR_ASIZE_SHFT = 0xd, ++ S_SCMO_ESYN_APACKET_1_ERR_ABURST_BMSK = 0x1000, ++ S_SCMO_ESYN_APACKET_1_ERR_ABURST_SHFT = 0xc, ++ S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_BMSK = 0x100, ++ S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_SHFT = 0x8, ++ S_SCMO_ESYN_APACKET_1_ERR_APRONTS_BMSK = 0x10, ++ S_SCMO_ESYN_APACKET_1_ERR_APRONTS_SHFT = 0x4, ++ S_SCMO_ESYN_APACKET_1_ERR_AOOORD_BMSK = 0x4, ++ S_SCMO_ESYN_APACKET_1_ERR_AOOORD_SHFT = 0x2, ++ S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_BMSK = 0x2, ++ S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_SHFT = 0x1, ++ S_SCMO_ESYN_APACKET_1_ERR_AWRITE_BMSK = 0x1, ++ S_SCMO_ESYN_APACKET_1_ERR_AWRITE_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CLK_CTRL_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000200) ++enum bimc_s_scmo_clk_ctrl { ++ S_SCMO_CLK_CTRL_RMSK = 0xffff1111, ++ S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_BMSK = 0x10000, ++ S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_SHFT = 0x10, ++ S_SCMO_CLK_CTRL_RCH_CG_EN_BMSK = 0x1000, ++ S_SCMO_CLK_CTRL_RCH_CG_EN_SHFT = 0xc, ++ S_SCMO_CLK_CTRL_FLUSH_CG_EN_BMSK = 0x100, ++ S_SCMO_CLK_CTRL_FLUSH_CG_EN_SHFT = 0x8, ++ S_SCMO_CLK_CTRL_WCH_CG_EN_BMSK = 0x10, ++ S_SCMO_CLK_CTRL_WCH_CG_EN_SHFT = 0x4, ++ S_SCMO_CLK_CTRL_ACH_CG_EN_BMSK = 0x1, ++ S_SCMO_CLK_CTRL_ACH_CG_EN_SHFT = 0x0, ++}; ++ ++#define S_SCMO_SLV_INTERLEAVE_CFG_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000400) ++enum bimc_s_scmo_slv_interleave_cfg { ++ S_SCMO_SLV_INTERLEAVE_CFG_RMSK = 0xff, ++ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_BMSK = 0x10, ++ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_SHFT = 0x4, ++ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_BMSK = 0x1, ++ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_SHFT = 0x0, ++}; ++ ++#define S_SCMO_ADDR_BASE_CSn_ADDR(b, n, o) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000410 + 0x4 * (o)) ++enum bimc_s_scmo_addr_base_csn { ++ S_SCMO_ADDR_BASE_CSn_RMSK = 0xffff, ++ S_SCMO_ADDR_BASE_CSn_MAXn = 1, ++ S_SCMO_ADDR_BASE_CSn_ADDR_BASE_BMSK = 0xfc, ++ S_SCMO_ADDR_BASE_CSn_ADDR_BASE_SHFT = 0x2, ++}; ++ ++#define S_SCMO_ADDR_MAP_CSn_ADDR(b, n, o) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000420 + 0x4 * (o)) ++enum bimc_s_scmo_addr_map_csn { ++ S_SCMO_ADDR_MAP_CSn_RMSK = 0xffff, ++ S_SCMO_ADDR_MAP_CSn_MAXn = 1, ++ S_SCMO_ADDR_MAP_CSn_RANK_EN_BMSK = 0x8000, ++ S_SCMO_ADDR_MAP_CSn_RANK_EN_SHFT = 0xf, ++ S_SCMO_ADDR_MAP_CSn_ADDR_MODE_BMSK = 0x1000, ++ S_SCMO_ADDR_MAP_CSn_ADDR_MODE_SHFT = 0xc, ++ S_SCMO_ADDR_MAP_CSn_BANK_SIZE_BMSK = 0x100, ++ S_SCMO_ADDR_MAP_CSn_BANK_SIZE_SHFT = 0x8, ++ S_SCMO_ADDR_MAP_CSn_ROW_SIZE_BMSK = 0x30, ++ S_SCMO_ADDR_MAP_CSn_ROW_SIZE_SHFT = 0x4, ++ S_SCMO_ADDR_MAP_CSn_COL_SIZE_BMSK = 0x3, ++ S_SCMO_ADDR_MAP_CSn_COL_SIZE_SHFT = 0x0, ++}; ++ ++#define S_SCMO_ADDR_MASK_CSn_ADDR(b, n, o) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000430 + 0x4 * (0)) ++enum bimc_s_scmo_addr_mask_csn { ++ S_SCMO_ADDR_MASK_CSn_RMSK = 0xffff, ++ S_SCMO_ADDR_MASK_CSn_MAXn = 1, ++ S_SCMO_ADDR_MASK_CSn_ADDR_MASK_BMSK = 0xfc, ++ S_SCMO_ADDR_MASK_CSn_ADDR_MASK_SHFT = 0x2, ++}; ++ ++#define S_SCMO_SLV_STATUS_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000450) ++enum bimc_s_scmo_slv_status { ++ S_SCMO_SLV_STATUS_RMSK = 0xff3, ++ S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_BMSK = 0xff0, ++ S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_SHFT = 0x4, ++ S_SCMO_SLV_STATUS_SLAVE_IDLE_BMSK = 0x3, ++ S_SCMO_SLV_STATUS_SLAVE_IDLE_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CMD_BUF_CFG_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000500) ++enum bimc_s_scmo_cmd_buf_cfg { ++ S_SCMO_CMD_BUF_CFG_RMSK = 0xf1f, ++ S_SCMO_CMD_BUF_CFG_CMD_ORDERING_BMSK = 0x300, ++ S_SCMO_CMD_BUF_CFG_CMD_ORDERING_SHFT = 0x8, ++ S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_BMSK = 0x10, ++ S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_SHFT = 0x4, ++ S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_BMSK = 0x7, ++ S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SCM_CMD_BUF_STATUS_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000520) ++enum bimc_s_scm_cmd_buf_status { ++ S_SCMO_CMD_BUF_STATUS_RMSK = 0x77, ++ S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_BMSK = 0x70, ++ S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_SHFT = 0x4, ++ S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_BMSK = 0x7, ++ S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_SHFT = 0x0, ++}; ++ ++#define S_SCMO_RCH_SEL_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000540) ++enum bimc_s_scmo_rch_sel { ++ S_SCMO_RCH_SEL_RMSK = 0xffffffff, ++ S_SCMO_CMD_BUF_STATUS_RCH_PORTS_BMSK = 0xffffffff, ++ S_SCMO_CMD_BUF_STATUS_RCH_PORTS_SHFT = 0x0, ++}; ++ ++#define S_SCMO_RCH_BKPR_CFG_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000544) ++enum bimc_s_scmo_rch_bkpr_cfg { ++ S_SCMO_RCH_BKPR_CFG_RMSK = 0xffffffff, ++ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_BMSK = 0x3f000000, ++ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_SHFT = 0x18, ++ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_BMSK = 0x3f0000, ++ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_SHFT = 0x10, ++ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_BMSK = 0x3f00, ++ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_SHFT = 0x8, ++ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_BMSK = 0x3f, ++ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_SHFT = 0x0, ++}; ++ ++#define S_SCMO_RCH_STATUS_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000560) ++enum bimc_s_scmo_rch_status { ++ S_SCMO_RCH_STATUS_RMSK = 0x33333, ++ S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_BMSK = 0x20000, ++ S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_SHFT = 0x11, ++ S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_BMSK = 0x10000, ++ S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_SHFT = 0x10, ++ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_BMSK = 0x2000, ++ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_SHFT = 0xd, ++ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_BMSK = 0x1000, ++ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_SHFT = 0xc, ++ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_BMSK = 0x200, ++ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_SHFT = 0x9, ++ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_BMSK = 0x100, ++ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_SHFT = 0x8, ++ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_BMSK = 0x20, ++ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_SHFT = 0x5, ++ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_BMSK = 0x10, ++ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_SHFT = 0x4, ++ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_BMSK = 0x2, ++ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_SHFT = 0x1, ++ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_BMSK = 0x1, ++ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_SHFT = 0x0, ++}; ++ ++#define S_SCMO_WCH_BUF_CFG_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000580) ++enum bimc_s_scmo_wch_buf_cfg { ++ S_SCMO_WCH_BUF_CFG_RMSK = 0xff, ++ S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_BMSK = 0x10, ++ S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_SHFT = 0x4, ++ S_SCMO_WCH_BUF_CFG_COALESCE_EN_BMSK = 0x1, ++ S_SCMO_WCH_BUF_CFG_COALESCE_EN_SHFT = 0x0, ++}; ++ ++#define S_SCMO_WCH_STATUS_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005a0) ++enum bimc_s_scmo_wch_status { ++ S_SCMO_WCH_STATUS_RMSK = 0x333, ++ S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_BMSK = 0x200, ++ S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_SHFT = 0x9, ++ S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_BMSK = 0x100, ++ S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_SHFT = 0x8, ++ S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_BMSK = 0x20, ++ S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_SHFT = 0x5, ++ S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_BMSK = 0x10, ++ S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_SHFT = 0x4, ++ S_SCMO_WCH_STATUS_WBUF_FULL_BMSK = 0x2, ++ S_SCMO_WCH_STATUS_WBUF_FULL_SHFT = 0x1, ++ S_SCMO_WCH_STATUS_WBUF_EMPTY_BMSK = 0x1, ++ S_SCMO_WCH_STATUS_WBUF_EMPTY_SHFT = 0x0, ++}; ++ ++#define S_SCMO_FLUSH_CFG_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c0) ++enum bimc_s_scmo_flush_cfg { ++ S_SCMO_FLUSH_CFG_RMSK = 0xffffffff, ++ S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_BMSK = 0x10000000, ++ S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_SHFT = 0x1c, ++ S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_BMSK = 0x3ff0000, ++ S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_SHFT = 0x10, ++ S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_BMSK = 0xf00, ++ S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_SHFT = 0x8, ++ S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_BMSK = 0xf, ++ S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_SHFT = 0x0, ++}; ++ ++#define S_SCMO_FLUSH_CMD_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c4) ++enum bimc_s_scmo_flush_cmd { ++ S_SCMO_FLUSH_CMD_RMSK = 0xf, ++ S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_BMSK = 0x3, ++ S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CMD_OPT_CFG0_ADDR(b, n) \ ++ (S_SCM0_REG_BASE(b) + (0x8000 * (n)) + 0x00000700) ++enum bimc_s_scmo_cmd_opt_cfg0 { ++ S_SCMO_CMD_OPT_CFG0_RMSK = 0xffffff, ++ S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_BMSK = 0x100000, ++ S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_SHFT = 0x14, ++ S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_BMSK = 0x10000, ++ S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_SHFT = 0x10, ++ S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_BMSK = 0x1000, ++ S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_SHFT = 0xc, ++ S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_BMSK = 0x100, ++ S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_SHFT = 0x8, ++ S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_BMSK = 0x10, ++ S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_SHFT = 0x4, ++ S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_BMSK = 0x1, ++ S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CMD_OPT_CFG1_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000704) ++enum bimc_s_scmo_cmd_opt_cfg1 { ++ S_SCMO_CMD_OPT_CFG1_RMSK = 0xffffffff, ++ S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_BMSK = 0x1f000000, ++ S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_SHFT = 0x18, ++ S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_BMSK = 0x1f0000, ++ S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_SHFT = 0x10, ++ S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_BMSK = 0x1f00, ++ S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_SHFT = 0x8, ++ S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_BMSK = 0x1f, ++ S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CMD_OPT_CFG2_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000708) ++enum bimc_s_scmo_cmd_opt_cfg2 { ++ S_SCMO_CMD_OPT_CFG2_RMSK = 0xff, ++ S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_BMSK = 0xf, ++ S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_SHFT = 0x0, ++}; ++ ++#define S_SCMO_CMD_OPT_CFG3_ADDR(b, n) \ ++ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000070c) ++enum bimc_s_scmo_cmd_opt_cfg3 { ++ S_SCMO_CMD_OPT_CFG3_RMSK = 0xff, ++ S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_BMSK = 0xf, ++ S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_SHFT = 0x0, ++}; ++ ++/* S_SWAY_GENERIC */ ++#define S_SWAY_REG_BASE(b) ((b) + 0x00048000) ++ ++#define S_SWAY_CONFIG_INFO_0_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000020) ++enum bimc_s_sway_config_info_0 { ++ S_SWAY_CONFIG_INFO_0_RMSK = 0xff0000ff, ++ S_SWAY_CONFIG_INFO_0_SYNC_MODE_BMSK = 0xff000000, ++ S_SWAY_CONFIG_INFO_0_SYNC_MODE_SHFT = 0x18, ++ S_SWAY_CONFIG_INFO_0_FUNC_BMSK = 0xff, ++ S_SWAY_CONFIG_INFO_0_FUNC_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CONFIG_INFO_1_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000030) ++enum bimc_s_sway_config_info_1 { ++ S_SWAY_CONFIG_INFO_1_RMSK = 0xffffffff, ++ S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff, ++ S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CONFIG_INFO_2_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000040) ++enum bimc_s_sway_config_info_2 { ++ S_SWAY_CONFIG_INFO_2_RMSK = 0xffff0000, ++ S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_BMSK = 0xffff0000, ++ S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_SHFT = 0x10, ++}; ++ ++#define S_SWAY_CONFIG_INFO_3_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000050) ++enum bimc_s_sway_config_info_3 { ++ S_SWAY_CONFIG_INFO_3_RMSK = 0xffffffff, ++ S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_BMSK = 0xff000000, ++ S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_SHFT = 0x18, ++ S_SWAY_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff0000, ++ S_SWAY_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x10, ++ S_SWAY_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff, ++ S_SWAY_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CONFIG_INFO_4_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000060) ++enum bimc_s_sway_config_info_4 { ++ S_SWAY_CONFIG_INFO_4_RMSK = 0x800000ff, ++ S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_BMSK = 0x80000000, ++ S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_SHFT = 0x1f, ++ S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_BMSK = 0xff, ++ S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CONFIG_INFO_5_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000070) ++enum bimc_s_sway_config_info_5 { ++ S_SWAY_CONFIG_INFO_5_RMSK = 0x800000ff, ++ S_SWAY_CONFIG_INFO_5_QCH_EN_BMSK = 0x80000000, ++ S_SWAY_CONFIG_INFO_5_QCH_EN_SHFT = 0x1f, ++ S_SWAY_CONFIG_INFO_5_QCH_DEPTH_BMSK = 0xff, ++ S_SWAY_CONFIG_INFO_5_QCH_DEPTH_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CONFIG_INFO_6_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000080) ++enum bimc_s_sway_config_info_6 { ++ S_SWAY_CONFIG_INFO_6_RMSK = 0x1, ++ S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_BMSK = 0x1, ++ S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_SHFT = 0x0, ++}; ++ ++#define S_SWAY_INT_STATUS_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000100) ++enum bimc_s_sway_int_status { ++ S_SWAY_INT_STATUS_RMSK = 0x3, ++ S_SWAY_INT_STATUS_RFU_BMSK = 0x3, ++ S_SWAY_INT_STATUS_RFU_SHFT = 0x0, ++}; ++ ++#define S_SWAY_INT_CLR_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000108) ++enum bimc_s_sway_int_clr { ++ S_SWAY_INT_CLR_RMSK = 0x3, ++ S_SWAY_INT_CLR_RFU_BMSK = 0x3, ++ S_SWAY_INT_CLR_RFU_SHFT = 0x0, ++}; ++ ++ ++#define S_SWAY_INT_EN_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c) ++enum bimc_s_sway_int_en { ++ S_SWAY_INT_EN_RMSK = 0x3, ++ S_SWAY_INT_EN_RFU_BMSK = 0x3, ++ S_SWAY_INT_EN_RFU_SHFT = 0x0, ++}; ++ ++#define S_SWAY_CLK_CTRL_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000200) ++enum bimc_s_sway_clk_ctrl { ++ S_SWAY_CLK_CTRL_RMSK = 0x3, ++ S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK = 0x2, ++ S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT = 0x1, ++ S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1, ++ S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0, ++}; ++ ++#define S_SWAY_RCH_SEL_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000210) ++enum bimc_s_sway_rch_sel { ++ S_SWAY_RCH_SEL_RMSK = 0x7f, ++ S_SWAY_RCH_SEL_UNUSED_BMSK = 0x7f, ++ S_SWAY_RCH_SEL_UNUSED_SHFT = 0x0, ++}; ++ ++ ++#define S_SWAY_MAX_OUTSTANDING_REQS_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000220) ++enum bimc_s_sway_max_outstanding_reqs { ++ S_SWAY_MAX_OUTSTANDING_REQS_RMSK = 0xffff, ++ S_SWAY_MAX_OUTSTANDING_REQS_WRITE_BMSK = 0xff00, ++ S_SWAY_MAX_OUTSTANDING_REQS_WRITE_SHFT = 0x8, ++ S_SWAY_MAX_OUTSTANDING_REQS_READ_BMSK = 0xff, ++ S_SWAY_MAX_OUTSTANDING_REQS_READ_SHFT = 0x0, ++}; ++ ++ ++#define S_SWAY_BUF_STATUS_0_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000400) ++enum bimc_s_sway_buf_status_0 { ++ S_SWAY_BUF_STATUS_0_RMSK = 0xf0300f03, ++ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_BMSK = 0x80000000, ++ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_SHFT = 0x1f, ++ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_BMSK = 0x40000000, ++ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_SHFT = 0x1e, ++ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_BMSK = 0x20000000, ++ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_SHFT = 0x1d, ++ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_BMSK = 0x10000000, ++ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_SHFT = 0x1c, ++ S_SWAY_BUF_STATUS_0_BCH_RD_FULL_BMSK = 0x200000, ++ S_SWAY_BUF_STATUS_0_BCH_RD_FULL_SHFT = 0x15, ++ S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_BMSK = 0x100000, ++ S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_SHFT = 0x14, ++ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_BMSK = 0x800, ++ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_SHFT = 0xb, ++ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_BMSK = 0x400, ++ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_SHFT = 0xa, ++ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_BMSK = 0x200, ++ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_SHFT = 0x9, ++ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_BMSK = 0x100, ++ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_SHFT = 0x8, ++ S_SWAY_BUF_STATUS_0_ACH_WR_FULL_BMSK = 0x2, ++ S_SWAY_BUF_STATUS_0_ACH_WR_FULL_SHFT = 0x1, ++ S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_BMSK = 0x1, ++ S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_SHFT = 0x0, ++}; ++ ++#define S_SWAY_BUF_STATUS_1_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000410) ++enum bimc_s_sway_buf_status_1 { ++ S_SWAY_BUF_STATUS_1_RMSK = 0xf0, ++ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_BMSK = 0x80, ++ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_SHFT = 0x7, ++ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_BMSK = 0x40, ++ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_SHFT = 0x6, ++ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_BMSK = 0x20, ++ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_SHFT = 0x5, ++ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_BMSK = 0x10, ++ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_SHFT = 0x4, ++}; ++ ++#define S_SWAY_BUF_STATUS_2_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000420) ++enum bimc_s_sway_buf_status_2 { ++ S_SWAY_BUF_STATUS_2_RMSK = 0x30, ++ S_SWAY_BUF_STATUS_2_QCH_RD_FULL_BMSK = 0x20, ++ S_SWAY_BUF_STATUS_2_QCH_RD_FULL_SHFT = 0x5, ++ S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_BMSK = 0x10, ++ S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_SHFT = 0x4, ++}; ++ ++/* S_ARB_GENERIC */ ++ ++#define S_ARB_REG_BASE(b) ((b) + 0x00049000) ++ ++#define S_ARB_COMPONENT_INFO_ADDR(b, n) \ ++ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000000) ++enum bimc_s_arb_component_info { ++ S_ARB_COMPONENT_INFO_RMSK = 0xffffff, ++ S_ARB_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000, ++ S_ARB_COMPONENT_INFO_INSTANCE_SHFT = 0x10, ++ S_ARB_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00, ++ S_ARB_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8, ++ S_ARB_COMPONENT_INFO_TYPE_BMSK = 0xff, ++ S_ARB_COMPONENT_INFO_TYPE_SHFT = 0x0, ++}; ++ ++#define S_ARB_CONFIG_INFO_0_ADDR(b, n) \ ++ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000020) ++enum bimc_s_arb_config_info_0 { ++ S_ARB_CONFIG_INFO_0_RMSK = 0x800000ff, ++ S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_BMSK = 0x80000000, ++ S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_SHFT = 0x1f, ++ S_ARB_CONFIG_INFO_0_FUNC_BMSK = 0xff, ++ S_ARB_CONFIG_INFO_0_FUNC_SHFT = 0x0, ++}; ++ ++#define S_ARB_CONFIG_INFO_1_ADDR(b, n) \ ++ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000030) ++enum bimc_s_arb_config_info_1 { ++ S_ARB_CONFIG_INFO_1_RMSK = 0xffffffff, ++ S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff, ++ S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0, ++}; ++ ++#define S_ARB_CLK_CTRL_ADDR(b) \ ++ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000200) ++enum bimc_s_arb_clk_ctrl { ++ S_ARB_CLK_CTRL_RMSK = 0x1, ++ S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK = 0x2, ++ S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT = 0x1, ++ S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1, ++ S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0, ++ S_ARB_CLK_CTRL_CLK_GATING_EN_BMSK = 0x1, ++ S_ARB_CLK_CTRL_CLK_GATING_EN_SHFT = 0x0, ++}; ++ ++#define S_ARB_MODE_ADDR(b, n) \ ++ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000210) ++enum bimc_s_arb_mode { ++ S_ARB_MODE_RMSK = 0xf0000001, ++ S_ARB_MODE_WR_GRANTS_AHEAD_BMSK = 0xf0000000, ++ S_ARB_MODE_WR_GRANTS_AHEAD_SHFT = 0x1c, ++ S_ARB_MODE_PRIO_RR_EN_BMSK = 0x1, ++ S_ARB_MODE_PRIO_RR_EN_SHFT = 0x0, ++}; ++ ++#define BKE_HEALTH_MASK \ ++ (M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK |\ ++ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK |\ ++ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK) ++ ++#define BKE_HEALTH_VAL(limit, areq, plvl) \ ++ ((((limit) << M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT) & \ ++ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK) | \ ++ (((areq) << M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT) & \ ++ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK) | \ ++ (((plvl) << M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT) & \ ++ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK)) ++ ++#define MAX_GRANT_PERIOD \ ++ (M_BKE_GP_GP_BMSK >> \ ++ M_BKE_GP_GP_SHFT) ++ ++#define MAX_GC \ ++ (M_BKE_GC_GC_BMSK >> \ ++ (M_BKE_GC_GC_SHFT + 1)) ++ ++static int bimc_div(int64_t *a, uint32_t b) ++{ ++ if ((*a > 0) && (*a < b)) { ++ *a = 0; ++ return 1; ++ } else { ++ return do_div(*a, b); ++ } ++} ++ ++#define ENABLE(val) ((val) == 1 ? 1 : 0) ++void msm_bus_bimc_set_mas_clk_gate(struct msm_bus_bimc_info *binfo, ++ uint32_t mas_index, struct msm_bus_bimc_clk_gate *bgate) ++{ ++ uint32_t val, mask, reg_val; ++ void __iomem *addr; ++ ++ reg_val = readl_relaxed(M_CLK_CTRL_ADDR(binfo->base, ++ mas_index)) & M_CLK_CTRL_RMSK; ++ addr = M_CLK_CTRL_ADDR(binfo->base, mas_index); ++ mask = (M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK | ++ M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK); ++ val = (bgate->core_clk_gate_en << ++ M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT) | ++ bgate->port_clk_gate_en; ++ writel_relaxed(((reg_val & (~mask)) | (val & mask)), addr); ++ /* Ensure clock gating enable mask is set before exiting */ ++ wmb(); ++} ++ ++void msm_bus_bimc_arb_en(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index, bool en) ++{ ++ uint32_t reg_val, reg_mask_val, enable, val; ++ ++ reg_mask_val = (readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo-> ++ base, slv_index)) & S_ARB_CONFIG_INFO_0_FUNC_BMSK) ++ >> S_ARB_CONFIG_INFO_0_FUNC_SHFT; ++ enable = ENABLE(en); ++ val = enable << S_ARB_MODE_PRIO_RR_EN_SHFT; ++ if (reg_mask_val == BIMC_ARB_MODE_PRIORITY_RR) { ++ reg_val = readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo-> ++ base, slv_index)) & S_ARB_MODE_RMSK; ++ writel_relaxed(((reg_val & (~(S_ARB_MODE_PRIO_RR_EN_BMSK))) | ++ (val & S_ARB_MODE_PRIO_RR_EN_BMSK)), ++ S_ARB_MODE_ADDR(binfo->base, slv_index)); ++ /* Ensure arbitration mode is set before returning */ ++ wmb(); ++ } ++} ++ ++static void set_qos_mode(void __iomem *baddr, uint32_t index, uint32_t val0, ++ uint32_t val1, uint32_t val2) ++{ ++ uint32_t reg_val, val; ++ ++ reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(baddr, ++ index)) & M_PRIOLVL_OVERRIDE_RMSK; ++ val = val0 << M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT; ++ writel_relaxed(((reg_val & ~(M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)) ++ | (val & M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)), ++ M_PRIOLVL_OVERRIDE_ADDR(baddr, index)); ++ reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(baddr, index)) & ++ M_RD_CMD_OVERRIDE_RMSK; ++ val = val1 << M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT; ++ writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK ++ )) | (val & M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)), ++ M_RD_CMD_OVERRIDE_ADDR(baddr, index)); ++ reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR(baddr, index)) & ++ M_WR_CMD_OVERRIDE_RMSK; ++ val = val2 << M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT; ++ writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK ++ )) | (val & M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)), ++ M_WR_CMD_OVERRIDE_ADDR(baddr, index)); ++ /* Ensure the priority register writes go through */ ++ wmb(); ++} ++ ++static void msm_bus_bimc_set_qos_mode(void __iomem *base, ++ uint32_t mas_index, uint8_t qmode_sel) ++{ ++ uint32_t reg_val, val; ++ ++ switch (qmode_sel) { ++ case BIMC_QOS_MODE_FIXED: ++ reg_val = readl_relaxed(M_BKE_EN_ADDR(base, ++ mas_index)); ++ writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)), ++ M_BKE_EN_ADDR(base, mas_index)); ++ /* Ensure that the book-keeping register writes ++ * go through before setting QoS mode. ++ * QoS mode registers might write beyond 1K ++ * boundary in future ++ */ ++ wmb(); ++ set_qos_mode(base, mas_index, 1, 1, 1); ++ break; ++ ++ case BIMC_QOS_MODE_BYPASS: ++ reg_val = readl_relaxed(M_BKE_EN_ADDR(base, ++ mas_index)); ++ writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)), ++ M_BKE_EN_ADDR(base, mas_index)); ++ /* Ensure that the book-keeping register writes ++ * go through before setting QoS mode. ++ * QoS mode registers might write beyond 1K ++ * boundary in future ++ */ ++ wmb(); ++ set_qos_mode(base, mas_index, 0, 0, 0); ++ break; ++ ++ case BIMC_QOS_MODE_REGULATOR: ++ case BIMC_QOS_MODE_LIMITER: ++ set_qos_mode(base, mas_index, 0, 0, 0); ++ reg_val = readl_relaxed(M_BKE_EN_ADDR(base, ++ mas_index)); ++ val = 1 << M_BKE_EN_EN_SHFT; ++ /* Ensure that the book-keeping register writes ++ * go through before setting QoS mode. ++ * QoS mode registers might write beyond 1K ++ * boundary in future ++ */ ++ wmb(); ++ writel_relaxed(((reg_val & (~M_BKE_EN_EN_BMSK)) | (val & ++ M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(base, ++ mas_index)); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void set_qos_prio_rl(void __iomem *addr, uint32_t rmsk, ++ uint8_t index, struct msm_bus_bimc_qos_mode *qmode) ++{ ++ uint32_t reg_val, val0, val; ++ ++ /* Note, addr is already passed with right mas_index */ ++ reg_val = readl_relaxed(addr) & rmsk; ++ val0 = BKE_HEALTH_VAL(qmode->rl.qhealth[index].limit_commands, ++ qmode->rl.qhealth[index].areq_prio, ++ qmode->rl.qhealth[index].prio_level); ++ val = ((reg_val & (~(BKE_HEALTH_MASK))) | (val0 & BKE_HEALTH_MASK)); ++ writel_relaxed(val, addr); ++ /* Ensure that priority for regulator/limiter modes are ++ * set before returning ++ */ ++ wmb(); ++ ++} ++ ++static void msm_bus_bimc_set_qos_prio(void __iomem *base, ++ uint32_t mas_index, uint8_t qmode_sel, ++ struct msm_bus_bimc_qos_mode *qmode) ++{ ++ uint32_t reg_val, val; ++ ++ switch (qmode_sel) { ++ case BIMC_QOS_MODE_FIXED: ++ reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR( ++ base, mas_index)) & M_PRIOLVL_OVERRIDE_RMSK; ++ val = qmode->fixed.prio_level << ++ M_PRIOLVL_OVERRIDE_SHFT; ++ writel_relaxed(((reg_val & ++ ~(M_PRIOLVL_OVERRIDE_BMSK)) | (val ++ & M_PRIOLVL_OVERRIDE_BMSK)), ++ M_PRIOLVL_OVERRIDE_ADDR(base, mas_index)); ++ ++ reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR( ++ base, mas_index)) & M_RD_CMD_OVERRIDE_RMSK; ++ val = qmode->fixed.areq_prio_rd << ++ M_RD_CMD_OVERRIDE_AREQPRIO_SHFT; ++ writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_AREQPRIO_BMSK)) ++ | (val & M_RD_CMD_OVERRIDE_AREQPRIO_BMSK)), ++ M_RD_CMD_OVERRIDE_ADDR(base, mas_index)); ++ ++ reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR( ++ base, mas_index)) & M_WR_CMD_OVERRIDE_RMSK; ++ val = qmode->fixed.areq_prio_wr << ++ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT; ++ writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_AREQPRIO_BMSK)) ++ | (val & M_WR_CMD_OVERRIDE_AREQPRIO_BMSK)), ++ M_WR_CMD_OVERRIDE_ADDR(base, mas_index)); ++ /* Ensure that fixed mode register writes go through ++ * before returning ++ */ ++ wmb(); ++ break; ++ ++ case BIMC_QOS_MODE_REGULATOR: ++ case BIMC_QOS_MODE_LIMITER: ++ set_qos_prio_rl(M_BKE_HEALTH_3_CONFIG_ADDR(base, ++ mas_index), M_BKE_HEALTH_3_CONFIG_RMSK, 3, qmode); ++ set_qos_prio_rl(M_BKE_HEALTH_2_CONFIG_ADDR(base, ++ mas_index), M_BKE_HEALTH_2_CONFIG_RMSK, 2, qmode); ++ set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(base, ++ mas_index), M_BKE_HEALTH_1_CONFIG_RMSK, 1, qmode); ++ set_qos_prio_rl(M_BKE_HEALTH_0_CONFIG_ADDR(base, ++ mas_index), M_BKE_HEALTH_0_CONFIG_RMSK, 0 , qmode); ++ break; ++ case BIMC_QOS_MODE_BYPASS: ++ default: ++ break; ++ } ++} ++ ++static void set_qos_bw_regs(void __iomem *baddr, uint32_t mas_index, ++ int32_t th, int32_t tm, int32_t tl, uint32_t gp, ++ uint32_t gc) ++{ ++ int32_t reg_val, val; ++ int32_t bke_reg_val; ++ int16_t val2; ++ ++ /* Disable BKE before writing to registers as per spec */ ++ bke_reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)); ++ writel_relaxed((bke_reg_val & ~(M_BKE_EN_EN_BMSK)), ++ M_BKE_EN_ADDR(baddr, mas_index)); ++ ++ /* Write values of registers calculated */ ++ reg_val = readl_relaxed(M_BKE_GP_ADDR(baddr, mas_index)) ++ & M_BKE_GP_RMSK; ++ val = gp << M_BKE_GP_GP_SHFT; ++ writel_relaxed(((reg_val & ~(M_BKE_GP_GP_BMSK)) | (val & ++ M_BKE_GP_GP_BMSK)), M_BKE_GP_ADDR(baddr, mas_index)); ++ ++ reg_val = readl_relaxed(M_BKE_GC_ADDR(baddr, mas_index)) & ++ M_BKE_GC_RMSK; ++ val = gc << M_BKE_GC_GC_SHFT; ++ writel_relaxed(((reg_val & ~(M_BKE_GC_GC_BMSK)) | (val & ++ M_BKE_GC_GC_BMSK)), M_BKE_GC_ADDR(baddr, mas_index)); ++ ++ reg_val = readl_relaxed(M_BKE_THH_ADDR(baddr, mas_index)) & ++ M_BKE_THH_RMSK; ++ val = th << M_BKE_THH_THRESH_SHFT; ++ writel_relaxed(((reg_val & ~(M_BKE_THH_THRESH_BMSK)) | (val & ++ M_BKE_THH_THRESH_BMSK)), M_BKE_THH_ADDR(baddr, mas_index)); ++ ++ reg_val = readl_relaxed(M_BKE_THM_ADDR(baddr, mas_index)) & ++ M_BKE_THM_RMSK; ++ val2 = tm << M_BKE_THM_THRESH_SHFT; ++ writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val2 & ++ M_BKE_THM_THRESH_BMSK)), M_BKE_THM_ADDR(baddr, mas_index)); ++ ++ reg_val = readl_relaxed(M_BKE_THL_ADDR(baddr, mas_index)) & ++ M_BKE_THL_RMSK; ++ val2 = tl << M_BKE_THL_THRESH_SHFT; ++ writel_relaxed(((reg_val & ~(M_BKE_THL_THRESH_BMSK)) | ++ (val2 & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr, ++ mas_index)); ++ ++ /* Ensure that all bandwidth register writes have completed ++ * before returning ++ */ ++ wmb(); ++} ++ ++static void msm_bus_bimc_set_qos_bw(void __iomem *base, uint32_t qos_freq, ++ uint32_t mas_index, struct msm_bus_bimc_qos_bw *qbw) ++{ ++ uint32_t bke_en; ++ ++ /* Validate QOS Frequency */ ++ if (qos_freq == 0) { ++ MSM_BUS_DBG("Zero frequency\n"); ++ return; ++ } ++ ++ /* Get enable bit for BKE before programming the period */ ++ bke_en = (readl_relaxed(M_BKE_EN_ADDR(base, mas_index)) & ++ M_BKE_EN_EN_BMSK) >> M_BKE_EN_EN_SHFT; ++ ++ /* Only calculate if there's a requested bandwidth and window */ ++ if (qbw->bw && qbw->ws) { ++ int64_t th, tm, tl; ++ uint32_t gp, gc; ++ int64_t gp_nominal, gp_required, gp_calc, data, temp; ++ int64_t win = qbw->ws * qos_freq; ++ temp = win; ++ /* ++ * Calculate nominal grant period defined by requested ++ * window size. ++ * Ceil this value to max grant period. ++ */ ++ bimc_div(&temp, 1000000); ++ gp_nominal = min_t(uint64_t, MAX_GRANT_PERIOD, temp); ++ /* ++ * Calculate max window size, defined by bw request. ++ * Units: (KHz, MB/s) ++ */ ++ gp_calc = MAX_GC * qos_freq * 1000; ++ gp_required = gp_calc; ++ bimc_div(&gp_required, qbw->bw); ++ ++ /* User min of two grant periods */ ++ gp = min_t(int64_t, gp_nominal, gp_required); ++ ++ /* Calculate bandwith in grants and ceil. */ ++ temp = qbw->bw * gp; ++ data = qos_freq * 1000; ++ bimc_div(&temp, data); ++ gc = min_t(int64_t, MAX_GC, temp); ++ ++ /* Calculate thresholds */ ++ th = qbw->bw - qbw->thh; ++ tm = qbw->bw - qbw->thm; ++ tl = qbw->bw - qbw->thl; ++ ++ th = th * gp; ++ bimc_div(&th, data); ++ tm = tm * gp; ++ bimc_div(&tm, data); ++ tl = tl * gp; ++ bimc_div(&tl, data); ++ ++ MSM_BUS_DBG("BIMC: BW: mas_index: %d, th: %llu tm: %llu\n", ++ mas_index, th, tm); ++ MSM_BUS_DBG("BIMC: tl: %llu gp:%u gc: %u bke_en: %u\n", ++ tl, gp, gc, bke_en); ++ set_qos_bw_regs(base, mas_index, th, tm, tl, gp, gc); ++ } else ++ /* Clear bandwidth registers */ ++ set_qos_bw_regs(base, mas_index, 0, 0, 0, 0, 0); ++} ++ ++static int msm_bus_bimc_allocate_commit_data(struct msm_bus_fabric_registration ++ *fab_pdata, void **cdata, int ctx) ++{ ++ struct msm_bus_bimc_commit **cd = (struct msm_bus_bimc_commit **)cdata; ++ struct msm_bus_bimc_info *binfo = ++ (struct msm_bus_bimc_info *)fab_pdata->hw_data; ++ ++ MSM_BUS_DBG("Allocating BIMC commit data\n"); ++ *cd = kzalloc(sizeof(struct msm_bus_bimc_commit), GFP_KERNEL); ++ if (!*cd) { ++ MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); ++ return -ENOMEM; ++ } ++ ++ (*cd)->mas = binfo->cdata[ctx].mas; ++ (*cd)->slv = binfo->cdata[ctx].slv; ++ ++ return 0; ++} ++ ++static void *msm_bus_bimc_allocate_bimc_data(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *fab_pdata) ++{ ++ struct resource *bimc_mem; ++ struct resource *bimc_io; ++ struct msm_bus_bimc_info *binfo; ++ int i; ++ ++ MSM_BUS_DBG("Allocating BIMC data\n"); ++ binfo = kzalloc(sizeof(struct msm_bus_bimc_info), GFP_KERNEL); ++ if (!binfo) { ++ WARN(!binfo, "Couldn't alloc mem for bimc_info\n"); ++ return NULL; ++ } ++ ++ binfo->qos_freq = fab_pdata->qos_freq; ++ ++ binfo->params.nmasters = fab_pdata->nmasters; ++ binfo->params.nslaves = fab_pdata->nslaves; ++ binfo->params.bus_id = fab_pdata->id; ++ ++ for (i = 0; i < NUM_CTX; i++) { ++ binfo->cdata[i].mas = kzalloc(sizeof(struct ++ msm_bus_node_hw_info) * fab_pdata->nmasters * 2, ++ GFP_KERNEL); ++ if (!binfo->cdata[i].mas) { ++ MSM_BUS_ERR("Couldn't alloc mem for bimc master hw\n"); ++ kfree(binfo); ++ return NULL; ++ } ++ ++ binfo->cdata[i].slv = kzalloc(sizeof(struct ++ msm_bus_node_hw_info) * fab_pdata->nslaves * 2, ++ GFP_KERNEL); ++ if (!binfo->cdata[i].slv) { ++ MSM_BUS_DBG("Couldn't alloc mem for bimc slave hw\n"); ++ kfree(binfo->cdata[i].mas); ++ kfree(binfo); ++ return NULL; ++ } ++ } ++ ++ if (fab_pdata->virt) { ++ MSM_BUS_DBG("Don't get memory regions for virtual fabric\n"); ++ goto skip_mem; ++ } ++ ++ bimc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!bimc_mem) { ++ MSM_BUS_ERR("Cannot get BIMC Base address\n"); ++ kfree(binfo); ++ return NULL; ++ } ++ ++ bimc_io = request_mem_region(bimc_mem->start, ++ resource_size(bimc_mem), pdev->name); ++ if (!bimc_io) { ++ MSM_BUS_ERR("BIMC memory unavailable\n"); ++ kfree(binfo); ++ return NULL; ++ } ++ ++ binfo->base = ioremap(bimc_mem->start, resource_size(bimc_mem)); ++ if (!binfo->base) { ++ MSM_BUS_ERR("IOremap failed for BIMC!\n"); ++ release_mem_region(bimc_mem->start, resource_size(bimc_mem)); ++ kfree(binfo); ++ return NULL; ++ } ++ ++skip_mem: ++ fab_pdata->hw_data = (void *)binfo; ++ return (void *)binfo; ++} ++ ++static void free_commit_data(void *cdata) ++{ ++ struct msm_bus_bimc_commit *cd = (struct msm_bus_bimc_commit *)cdata; ++ ++ kfree(cd->mas); ++ kfree(cd->slv); ++ kfree(cd); ++} ++ ++static void bke_switch( ++ void __iomem *baddr, uint32_t mas_index, bool req, int mode) ++{ ++ uint32_t reg_val, val, cur_val; ++ ++ val = req << M_BKE_EN_EN_SHFT; ++ reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)); ++ cur_val = reg_val & M_BKE_EN_RMSK; ++ if (val == cur_val) ++ return; ++ ++ if (!req && mode == BIMC_QOS_MODE_FIXED) ++ set_qos_mode(baddr, mas_index, 1, 1, 1); ++ ++ writel_relaxed(((reg_val & ~(M_BKE_EN_EN_BMSK)) | (val & ++ M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(baddr, mas_index)); ++ /* Make sure BKE on/off goes through before changing priorities */ ++ wmb(); ++ ++ if (req) ++ set_qos_mode(baddr, mas_index, 0, 0, 0); ++} ++ ++static void bimc_set_static_qos_bw(void __iomem *base, unsigned int qos_freq, ++ int mport, struct msm_bus_bimc_qos_bw *qbw) ++{ ++ int32_t bw_mbps, thh = 0, thm, thl, gc; ++ int32_t gp; ++ u64 temp; ++ ++ if (qos_freq == 0) { ++ MSM_BUS_DBG("No QoS Frequency.\n"); ++ return; ++ } ++ ++ if (!(qbw->bw && qbw->gp)) { ++ MSM_BUS_DBG("No QoS Bandwidth or Window size\n"); ++ return; ++ } ++ ++ /* Convert bandwidth to MBPS */ ++ temp = qbw->bw; ++ bimc_div(&temp, 1000000); ++ bw_mbps = temp; ++ ++ /* Grant period in clock cycles ++ * Grant period from bandwidth structure ++ * is in nano seconds, QoS freq is in KHz. ++ * Divide by 1000 to get clock cycles. ++ */ ++ gp = (qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC); ++ ++ /* Grant count = BW in MBps * Grant period ++ * in micro seconds ++ */ ++ gc = bw_mbps * (qbw->gp / NSEC_PER_USEC); ++ gc = min(gc, MAX_GC); ++ ++ /* Medium threshold = -((Medium Threshold percentage * ++ * Grant count) / 100) ++ */ ++ thm = -((qbw->thmp * gc) / 100); ++ qbw->thm = thm; ++ ++ /* Low threshold = -(Grant count) */ ++ thl = -gc; ++ qbw->thl = thl; ++ ++ MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d", ++ __func__, gp, gc, thm, thl, thh); ++ ++ trace_bus_bke_params(gc, gp, thl, thm, thl); ++ set_qos_bw_regs(base, mport, thh, thm, thl, gp, gc); ++} ++ ++static void msm_bus_bimc_config_master( ++ struct msm_bus_fabric_registration *fab_pdata, ++ struct msm_bus_inode_info *info, ++ uint64_t req_clk, uint64_t req_bw) ++{ ++ int mode, i, ports; ++ struct msm_bus_bimc_info *binfo; ++ uint64_t bw = 0; ++ ++ binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data; ++ ports = info->node_info->num_mports; ++ ++ /** ++ * Here check the details of dual configuration. ++ * Take actions based on different modes. ++ * Check for threshold if limiter mode, etc. ++ */ ++ ++ if (req_clk <= info->node_info->th[0]) { ++ mode = info->node_info->mode; ++ bw = info->node_info->bimc_bw[0]; ++ } else if ((info->node_info->num_thresh > 1) && ++ (req_clk <= info->node_info->th[1])) { ++ mode = info->node_info->mode; ++ bw = info->node_info->bimc_bw[1]; ++ } else ++ mode = info->node_info->mode_thresh; ++ ++ switch (mode) { ++ case BIMC_QOS_MODE_BYPASS: ++ case BIMC_QOS_MODE_FIXED: ++ for (i = 0; i < ports; i++) ++ bke_switch(binfo->base, info->node_info->qport[i], ++ BKE_OFF, mode); ++ break; ++ case BIMC_QOS_MODE_REGULATOR: ++ case BIMC_QOS_MODE_LIMITER: ++ for (i = 0; i < ports; i++) { ++ /* If not in fixed mode, update bandwidth */ ++ if ((info->node_info->cur_lim_bw != bw) ++ && (mode != BIMC_QOS_MODE_FIXED)) { ++ struct msm_bus_bimc_qos_bw qbw; ++ qbw.ws = info->node_info->ws; ++ qbw.bw = bw; ++ qbw.gp = info->node_info->bimc_gp; ++ qbw.thmp = info->node_info->bimc_thmp; ++ bimc_set_static_qos_bw(binfo->base, ++ binfo->qos_freq, ++ info->node_info->qport[i], &qbw); ++ info->node_info->cur_lim_bw = bw; ++ MSM_BUS_DBG("%s: Qos is %d reqclk %llu bw %llu", ++ __func__, mode, req_clk, bw); ++ } ++ bke_switch(binfo->base, info->node_info->qport[i], ++ BKE_ON, mode); ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop, ++ struct msm_bus_inode_info *info, ++ struct msm_bus_fabric_registration *fab_pdata, ++ void *sel_cdata, int *master_tiers, ++ int64_t add_bw) ++{ ++ struct msm_bus_bimc_info *binfo; ++ struct msm_bus_bimc_qos_bw qbw; ++ int i; ++ int64_t bw; ++ int ports = info->node_info->num_mports; ++ struct msm_bus_bimc_commit *sel_cd = ++ (struct msm_bus_bimc_commit *)sel_cdata; ++ ++ MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %lld\n", ++ info->node_info->id, info->node_info->priv_id, add_bw); ++ ++ binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data; ++ ++ if (info->node_info->num_mports == 0) { ++ MSM_BUS_DBG("BIMC: Skip Master BW\n"); ++ goto skip_mas_bw; ++ } ++ ++ ports = info->node_info->num_mports; ++ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports); ++ ++ for (i = 0; i < ports; i++) { ++ sel_cd->mas[info->node_info->masterp[i]].bw += bw; ++ sel_cd->mas[info->node_info->masterp[i]].hw_id = ++ info->node_info->mas_hw_id; ++ MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %llu\n", ++ info->node_info->priv_id, ++ sel_cd->mas[info->node_info->masterp[i]].bw); ++ if (info->node_info->hw_sel == MSM_BUS_RPM) ++ sel_cd->mas[info->node_info->masterp[i]].dirty = 1; ++ else { ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No qos ports to update!\n"); ++ break; ++ } ++ if (!(info->node_info->mode == BIMC_QOS_MODE_REGULATOR) ++ || (info->node_info->mode == ++ BIMC_QOS_MODE_LIMITER)) { ++ MSM_BUS_DBG("Skip QoS reg programming\n"); ++ break; ++ } ++ ++ MSM_BUS_DBG("qport: %d\n", info->node_info->qport[i]); ++ qbw.bw = sel_cd->mas[info->node_info->masterp[i]].bw; ++ qbw.ws = info->node_info->ws; ++ /* Threshold low = 90% of bw */ ++ qbw.thl = div_s64((90 * bw), 100); ++ /* Threshold medium = bw */ ++ qbw.thm = bw; ++ /* Threshold high = 10% more than bw */ ++ qbw.thh = div_s64((110 * bw), 100); ++ /* Check if info is a shared master. ++ * If it is, mark it dirty ++ * If it isn't, then set QOS Bandwidth. ++ * Also if dual-conf is set, don't program bw regs. ++ **/ ++ if (!info->node_info->dual_conf && ++ ((info->node_info->mode == BIMC_QOS_MODE_LIMITER) || ++ (info->node_info->mode == BIMC_QOS_MODE_REGULATOR))) ++ msm_bus_bimc_set_qos_bw(binfo->base, ++ binfo->qos_freq, ++ info->node_info->qport[i], &qbw); ++ } ++ } ++ ++skip_mas_bw: ++ ports = hop->node_info->num_sports; ++ MSM_BUS_DBG("BIMC: ID: %d, Sports: %d\n", hop->node_info->priv_id, ++ ports); ++ ++ for (i = 0; i < ports; i++) { ++ sel_cd->slv[hop->node_info->slavep[i]].bw += add_bw; ++ sel_cd->slv[hop->node_info->slavep[i]].hw_id = ++ hop->node_info->slv_hw_id; ++ MSM_BUS_DBG("BIMC: Update slave_bw: ID: %d -> %llu\n", ++ hop->node_info->priv_id, ++ sel_cd->slv[hop->node_info->slavep[i]].bw); ++ MSM_BUS_DBG("BIMC: Update slave_bw: index: %d\n", ++ hop->node_info->slavep[i]); ++ /* Check if hop is a shared slave. ++ * If it is, mark it dirty ++ * If it isn't, then nothing to be done as the ++ * slaves are in bypass mode. ++ **/ ++ if (hop->node_info->hw_sel == MSM_BUS_RPM) { ++ MSM_BUS_DBG("Slave dirty: %d, slavep: %d\n", ++ hop->node_info->priv_id, ++ hop->node_info->slavep[i]); ++ sel_cd->slv[hop->node_info->slavep[i]].dirty = 1; ++ } ++ } ++} ++ ++static int msm_bus_bimc_commit(struct msm_bus_fabric_registration ++ *fab_pdata, void *hw_data, void **cdata) ++{ ++ MSM_BUS_DBG("\nReached BIMC Commit\n"); ++ msm_bus_remote_hw_commit(fab_pdata, hw_data, cdata); ++ return 0; ++} ++ ++static void msm_bus_bimc_config_limiter( ++ struct msm_bus_fabric_registration *fab_pdata, ++ struct msm_bus_inode_info *info) ++{ ++ struct msm_bus_bimc_info *binfo; ++ int mode, i, ports; ++ ++ binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data; ++ ports = info->node_info->num_mports; ++ ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ return; ++ } ++ ++ if (info->cur_lim_bw) ++ mode = BIMC_QOS_MODE_LIMITER; ++ else ++ mode = info->node_info->mode; ++ ++ switch (mode) { ++ case BIMC_QOS_MODE_BYPASS: ++ case BIMC_QOS_MODE_FIXED: ++ for (i = 0; i < ports; i++) ++ bke_switch(binfo->base, info->node_info->qport[i], ++ BKE_OFF, mode); ++ break; ++ case BIMC_QOS_MODE_REGULATOR: ++ case BIMC_QOS_MODE_LIMITER: ++ if (info->cur_lim_bw != info->cur_prg_bw) { ++ MSM_BUS_DBG("Enabled BKE throttling node %d to %llu\n", ++ info->node_info->id, info->cur_lim_bw); ++ trace_bus_bimc_config_limiter(info->node_info->id, ++ info->cur_lim_bw); ++ for (i = 0; i < ports; i++) { ++ /* If not in fixed mode, update bandwidth */ ++ struct msm_bus_bimc_qos_bw qbw; ++ ++ qbw.ws = info->node_info->ws; ++ qbw.bw = info->cur_lim_bw; ++ qbw.gp = info->node_info->bimc_gp; ++ qbw.thmp = info->node_info->bimc_thmp; ++ bimc_set_static_qos_bw(binfo->base, ++ binfo->qos_freq, ++ info->node_info->qport[i], &qbw); ++ bke_switch(binfo->base, ++ info->node_info->qport[i], ++ BKE_ON, mode); ++ info->cur_prg_bw = qbw.bw; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo, ++ struct msm_bus_inode_info *info, ++ struct msm_bus_bimc_qos_mode *qmode, int mode) ++{ ++ int i; ++ ++ switch (mode) { ++ case BIMC_QOS_MODE_FIXED: ++ qmode->fixed.prio_level = info->node_info->prio_lvl; ++ qmode->fixed.areq_prio_rd = info->node_info->prio_rd; ++ qmode->fixed.areq_prio_wr = info->node_info->prio_wr; ++ break; ++ case BIMC_QOS_MODE_LIMITER: ++ qmode->rl.qhealth[0].limit_commands = 1; ++ qmode->rl.qhealth[1].limit_commands = 0; ++ qmode->rl.qhealth[2].limit_commands = 0; ++ qmode->rl.qhealth[3].limit_commands = 0; ++ break; ++ default: ++ break; ++ } ++ ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ return; ++ } ++ ++ for (i = 0; i < info->node_info->num_mports; i++) { ++ /* If not in bypass mode, update priority */ ++ if (mode != BIMC_QOS_MODE_BYPASS) { ++ msm_bus_bimc_set_qos_prio(binfo->base, ++ info->node_info-> ++ qport[i], mode, qmode); ++ ++ /* If not in fixed mode, update bandwidth */ ++ if (mode != BIMC_QOS_MODE_FIXED) { ++ struct msm_bus_bimc_qos_bw qbw; ++ qbw.ws = info->node_info->ws; ++ qbw.bw = info->node_info->bimc_bw[0]; ++ qbw.gp = info->node_info->bimc_gp; ++ qbw.thmp = info->node_info->bimc_thmp; ++ bimc_set_static_qos_bw(binfo->base, ++ binfo->qos_freq, ++ info->node_info->qport[i], &qbw); ++ } ++ } ++ ++ /* set mode */ ++ msm_bus_bimc_set_qos_mode(binfo->base, ++ info->node_info->qport[i], ++ mode); ++ } ++} ++ ++static void init_health_regs(struct msm_bus_bimc_info *binfo, ++ struct msm_bus_inode_info *info, ++ struct msm_bus_bimc_qos_mode *qmode, ++ int mode) ++{ ++ int i; ++ ++ if (mode == BIMC_QOS_MODE_LIMITER) { ++ qmode->rl.qhealth[0].limit_commands = 1; ++ qmode->rl.qhealth[1].limit_commands = 0; ++ qmode->rl.qhealth[2].limit_commands = 0; ++ qmode->rl.qhealth[3].limit_commands = 0; ++ ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ return; ++ } ++ ++ for (i = 0; i < info->node_info->num_mports; i++) { ++ /* If not in bypass mode, update priority */ ++ if (mode != BIMC_QOS_MODE_BYPASS) ++ msm_bus_bimc_set_qos_prio(binfo->base, ++ info->node_info->qport[i], mode, qmode); ++ } ++ } ++} ++ ++ ++static int msm_bus_bimc_mas_init(struct msm_bus_bimc_info *binfo, ++ struct msm_bus_inode_info *info) ++{ ++ struct msm_bus_bimc_qos_mode *qmode; ++ qmode = kzalloc(sizeof(struct msm_bus_bimc_qos_mode), ++ GFP_KERNEL); ++ if (!qmode) { ++ MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n", ++ info->node_info->id); ++ return -ENOMEM; ++ } ++ ++ info->hw_data = (void *)qmode; ++ ++ /** ++ * If the master supports dual configuration, ++ * configure registers for both modes ++ */ ++ if (info->node_info->dual_conf) ++ bimc_init_mas_reg(binfo, info, qmode, ++ info->node_info->mode_thresh); ++ else if (info->node_info->nr_lim) ++ init_health_regs(binfo, info, qmode, BIMC_QOS_MODE_LIMITER); ++ ++ bimc_init_mas_reg(binfo, info, qmode, info->node_info->mode); ++ return 0; ++} ++ ++static void msm_bus_bimc_node_init(void *hw_data, ++ struct msm_bus_inode_info *info) ++{ ++ struct msm_bus_bimc_info *binfo = ++ (struct msm_bus_bimc_info *)hw_data; ++ ++ if (!IS_SLAVE(info->node_info->priv_id) && ++ (info->node_info->hw_sel != MSM_BUS_RPM)) ++ msm_bus_bimc_mas_init(binfo, info); ++} ++ ++static int msm_bus_bimc_port_halt(uint32_t haltid, uint8_t mport) ++{ ++ return 0; ++} ++ ++static int msm_bus_bimc_port_unhalt(uint32_t haltid, uint8_t mport) ++{ ++ return 0; ++} ++ ++static int msm_bus_bimc_limit_mport(struct msm_bus_node_device_type *info, ++ void __iomem *qos_base, uint32_t qos_off, ++ uint32_t qos_delta, uint32_t qos_freq, ++ bool enable_lim, u64 lim_bw) ++{ ++ int mode; ++ int i; ++ ++ if (ZERO_OR_NULL_PTR(info->node_info->qport)) { ++ MSM_BUS_DBG("No QoS Ports to limit\n"); ++ return 0; ++ } ++ ++ if (enable_lim && lim_bw) { ++ mode = BIMC_QOS_MODE_LIMITER; ++ ++ if (!info->node_info->lim_bw) { ++ struct msm_bus_bimc_qos_mode qmode; ++ qmode.rl.qhealth[0].limit_commands = 1; ++ qmode.rl.qhealth[1].limit_commands = 0; ++ qmode.rl.qhealth[2].limit_commands = 0; ++ qmode.rl.qhealth[3].limit_commands = 0; ++ ++ for (i = 0; i < info->node_info->num_qports; i++) { ++ /* If not in bypass mode, update priority */ ++ if (mode != BIMC_QOS_MODE_BYPASS) ++ msm_bus_bimc_set_qos_prio(qos_base, ++ info->node_info->qport[i], mode, ++ &qmode); ++ } ++ } ++ ++ for (i = 0; i < info->node_info->num_qports; i++) { ++ struct msm_bus_bimc_qos_bw qbw; ++ /* If not in fixed mode, update bandwidth */ ++ if ((info->node_info->lim_bw != lim_bw)) { ++ qbw.ws = info->node_info->qos_params.ws; ++ qbw.bw = lim_bw; ++ qbw.gp = info->node_info->qos_params.gp; ++ qbw.thmp = info->node_info->qos_params.thmp; ++ bimc_set_static_qos_bw(qos_base, qos_freq, ++ info->node_info->qport[i], &qbw); ++ } ++ bke_switch(qos_base, info->node_info->qport[i], ++ BKE_ON, mode); ++ } ++ info->node_info->lim_bw = lim_bw; ++ } else { ++ mode = info->node_info->qos_params.mode; ++ for (i = 0; i < info->node_info->num_qports; i++) { ++ bke_switch(qos_base, info->node_info->qport[i], ++ BKE_OFF, mode); ++ } ++ } ++ info->node_info->qos_params.cur_mode = mode; ++ return 0; ++} ++ ++static bool msm_bus_bimc_update_bw_reg(int mode) ++{ ++ bool ret = false; ++ ++ if ((mode == BIMC_QOS_MODE_LIMITER) ++ || (mode == BIMC_QOS_MODE_REGULATOR)) ++ ret = true; ++ ++ return ret; ++} ++ ++static int msm_bus_bimc_qos_init(struct msm_bus_node_device_type *info, ++ void __iomem *qos_base, ++ uint32_t qos_off, uint32_t qos_delta, ++ uint32_t qos_freq) ++{ ++ int i; ++ struct msm_bus_bimc_qos_mode qmode; ++ ++ switch (info->node_info->qos_params.mode) { ++ case BIMC_QOS_MODE_FIXED: ++ qmode.fixed.prio_level = info->node_info->qos_params.prio_lvl; ++ qmode.fixed.areq_prio_rd = info->node_info->qos_params.prio_rd; ++ qmode.fixed.areq_prio_wr = info->node_info->qos_params.prio_wr; ++ break; ++ case BIMC_QOS_MODE_LIMITER: ++ qmode.rl.qhealth[0].limit_commands = 1; ++ qmode.rl.qhealth[1].limit_commands = 0; ++ qmode.rl.qhealth[2].limit_commands = 0; ++ qmode.rl.qhealth[3].limit_commands = 0; ++ break; ++ default: ++ break; ++ } ++ ++ if (ZERO_OR_NULL_PTR(info->node_info->qport)) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ return 0; ++ } ++ ++ for (i = 0; i < info->node_info->num_qports; i++) { ++ /* If not in bypass mode, update priority */ ++ if (info->node_info->qos_params.mode != BIMC_QOS_MODE_BYPASS) ++ msm_bus_bimc_set_qos_prio(qos_base, info->node_info-> ++ qport[i], info->node_info->qos_params.mode, ++ &qmode); ++ ++ /* set mode */ ++ if (info->node_info->qos_params.mode == BIMC_QOS_MODE_LIMITER) ++ bke_switch(qos_base, info->node_info->qport[i], ++ BKE_OFF, BIMC_QOS_MODE_FIXED); ++ else ++ msm_bus_bimc_set_qos_mode(qos_base, ++ info->node_info->qport[i], ++ info->node_info->qos_params.mode); ++ } ++ ++ return 0; ++} ++ ++static int msm_bus_bimc_set_bw(struct msm_bus_node_device_type *dev, ++ void __iomem *qos_base, uint32_t qos_off, ++ uint32_t qos_delta, uint32_t qos_freq) ++{ ++ struct msm_bus_bimc_qos_bw qbw; ++ int i; ++ int64_t bw = 0; ++ int ret = 0; ++ struct msm_bus_node_info_type *info = dev->node_info; ++ ++ if (info && info->num_qports && ++ ((info->qos_params.mode == BIMC_QOS_MODE_LIMITER) || ++ (info->qos_params.mode == BIMC_QOS_MODE_REGULATOR))) { ++ bw = msm_bus_div64(info->num_qports, ++ dev->node_ab.ab[DUAL_CTX]); ++ ++ for (i = 0; i < info->num_qports; i++) { ++ MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %llu\n", ++ info->id, bw); ++ ++ if (!info->qport) { ++ MSM_BUS_DBG("No qos ports to update!\n"); ++ break; ++ } ++ ++ qbw.bw = bw + info->qos_params.bw_buffer; ++ trace_bus_bimc_config_limiter(info->id, bw); ++ ++ /* Default to gp of 5us */ ++ qbw.gp = (info->qos_params.gp ? ++ info->qos_params.gp : 5000); ++ /* Default to thmp of 50% */ ++ qbw.thmp = (info->qos_params.thmp ? ++ info->qos_params.thmp : 50); ++ /* ++ * If the BW vote is 0 then set the QoS mode to ++ * Fixed. ++ */ ++ if (bw) { ++ bimc_set_static_qos_bw(qos_base, qos_freq, ++ info->qport[i], &qbw); ++ bke_switch(qos_base, info->qport[i], ++ BKE_ON, info->qos_params.mode); ++ } else { ++ bke_switch(qos_base, info->qport[i], ++ BKE_OFF, BIMC_QOS_MODE_FIXED); ++ } ++ } ++ } ++ return ret; ++} ++ ++int msm_bus_bimc_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo) ++{ ++ /* Set interleaving to true by default */ ++ MSM_BUS_DBG("\nInitializing BIMC...\n"); ++ pdata->il_flag = true; ++ hw_algo->allocate_commit_data = msm_bus_bimc_allocate_commit_data; ++ hw_algo->allocate_hw_data = msm_bus_bimc_allocate_bimc_data; ++ hw_algo->node_init = msm_bus_bimc_node_init; ++ hw_algo->free_commit_data = free_commit_data; ++ hw_algo->update_bw = msm_bus_bimc_update_bw; ++ hw_algo->commit = msm_bus_bimc_commit; ++ hw_algo->port_halt = msm_bus_bimc_port_halt; ++ hw_algo->port_unhalt = msm_bus_bimc_port_unhalt; ++ hw_algo->config_master = msm_bus_bimc_config_master; ++ hw_algo->config_limiter = msm_bus_bimc_config_limiter; ++ hw_algo->update_bw_reg = msm_bus_bimc_update_bw_reg; ++ /* BIMC slaves are shared. Slave registers are set through RPM */ ++ if (!pdata->ahb) ++ pdata->rpm_enabled = 1; ++ return 0; ++} ++ ++int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev) ++{ ++ if (!bus_dev) ++ return -ENODEV; ++ else { ++ bus_dev->fabdev->noc_ops.qos_init = msm_bus_bimc_qos_init; ++ bus_dev->fabdev->noc_ops.set_bw = msm_bus_bimc_set_bw; ++ bus_dev->fabdev->noc_ops.limit_mport = msm_bus_bimc_limit_mport; ++ bus_dev->fabdev->noc_ops.update_bw_reg = ++ msm_bus_bimc_update_bw_reg; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(msm_bus_bimc_set_ops); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_bimc.h +@@ -0,0 +1,127 @@ ++/* Copyright (c) 2012-2013, 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_BUS_BIMC_H ++#define _ARCH_ARM_MACH_MSM_BUS_BIMC_H ++ ++struct msm_bus_bimc_params { ++ uint32_t bus_id; ++ uint32_t addr_width; ++ uint32_t data_width; ++ uint32_t nmasters; ++ uint32_t nslaves; ++}; ++ ++struct msm_bus_bimc_commit { ++ struct msm_bus_node_hw_info *mas; ++ struct msm_bus_node_hw_info *slv; ++}; ++ ++struct msm_bus_bimc_info { ++ void __iomem *base; ++ uint32_t base_addr; ++ uint32_t qos_freq; ++ struct msm_bus_bimc_params params; ++ struct msm_bus_bimc_commit cdata[NUM_CTX]; ++}; ++ ++struct msm_bus_bimc_node { ++ uint32_t conn_mask; ++ uint32_t data_width; ++ uint8_t slv_arb_mode; ++}; ++ ++enum msm_bus_bimc_arb_mode { ++ BIMC_ARB_MODE_RR = 0, ++ BIMC_ARB_MODE_PRIORITY_RR, ++ BIMC_ARB_MODE_TIERED_RR, ++}; ++ ++ ++enum msm_bus_bimc_interleave { ++ BIMC_INTERLEAVE_NONE = 0, ++ BIMC_INTERLEAVE_ODD, ++ BIMC_INTERLEAVE_EVEN, ++}; ++ ++struct msm_bus_bimc_slave_seg { ++ bool enable; ++ uint64_t start_addr; ++ uint64_t seg_size; ++ uint8_t interleave; ++}; ++ ++enum msm_bus_bimc_qos_mode_type { ++ BIMC_QOS_MODE_FIXED = 0, ++ BIMC_QOS_MODE_LIMITER, ++ BIMC_QOS_MODE_BYPASS, ++ BIMC_QOS_MODE_REGULATOR, ++}; ++ ++struct msm_bus_bimc_qos_health { ++ bool limit_commands; ++ uint32_t areq_prio; ++ uint32_t prio_level; ++}; ++ ++struct msm_bus_bimc_mode_fixed { ++ uint32_t prio_level; ++ uint32_t areq_prio_rd; ++ uint32_t areq_prio_wr; ++}; ++ ++struct msm_bus_bimc_mode_rl { ++ uint8_t qhealthnum; ++ struct msm_bus_bimc_qos_health qhealth[4]; ++}; ++ ++struct msm_bus_bimc_qos_mode { ++ uint8_t mode; ++ struct msm_bus_bimc_mode_fixed fixed; ++ struct msm_bus_bimc_mode_rl rl; ++}; ++ ++struct msm_bus_bimc_qos_bw { ++ uint64_t bw; /* bw is in Bytes/sec */ ++ uint32_t ws; /* Window size in nano seconds*/ ++ int64_t thh; /* Threshold high, bytes per second */ ++ int64_t thm; /* Threshold medium, bytes per second */ ++ int64_t thl; /* Threshold low, bytes per second */ ++ u32 gp; /* Grant Period in micro seconds */ ++ u32 thmp; /* Threshold medium in percentage */ ++}; ++ ++struct msm_bus_bimc_clk_gate { ++ bool core_clk_gate_en; ++ bool arb_clk_gate_en; /* For arbiter */ ++ bool port_clk_gate_en; /* For regs on BIMC core clock */ ++}; ++ ++void msm_bus_bimc_set_slave_seg(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index, uint32_t seg_index, ++ struct msm_bus_bimc_slave_seg *bsseg); ++void msm_bus_bimc_set_slave_clk_gate(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index, struct msm_bus_bimc_clk_gate *bgate); ++void msm_bus_bimc_set_mas_clk_gate(struct msm_bus_bimc_info *binfo, ++ uint32_t mas_index, struct msm_bus_bimc_clk_gate *bgate); ++void msm_bus_bimc_arb_en(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index, bool en); ++void msm_bus_bimc_get_params(struct msm_bus_bimc_info *binfo, ++ struct msm_bus_bimc_params *params); ++void msm_bus_bimc_get_mas_params(struct msm_bus_bimc_info *binfo, ++ uint32_t mas_index, struct msm_bus_bimc_node *mparams); ++void msm_bus_bimc_get_slv_params(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index, struct msm_bus_bimc_node *sparams); ++bool msm_bus_bimc_get_arb_en(struct msm_bus_bimc_info *binfo, ++ uint32_t slv_index); ++ ++#endif /*_ARCH_ARM_MACH_MSM_BUS_BIMC_H*/ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_client_api.c +@@ -0,0 +1,83 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus.h" ++#include "msm_bus_core.h" ++ ++struct msm_bus_arb_ops arb_ops; ++ ++/** ++ * msm_bus_scale_register_client() - Register the clients with the msm bus ++ * driver ++ * @pdata: Platform data of the client, containing src, dest, ab, ib. ++ * Return non-zero value in case of success, 0 in case of failure. ++ * ++ * Client data contains the vectors specifying arbitrated bandwidth (ab) ++ * and instantaneous bandwidth (ib) requested between a particular ++ * src and dest. ++ */ ++uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) ++{ ++ if (arb_ops.register_client) ++ return arb_ops.register_client(pdata); ++ else { ++ pr_err("%s: Bus driver not ready.", ++ __func__); ++ return 0; ++ } ++} ++EXPORT_SYMBOL(msm_bus_scale_register_client); ++ ++/** ++ * msm_bus_scale_client_update_request() - Update the request for bandwidth ++ * from a particular client ++ * ++ * cl: Handle to the client ++ * index: Index into the vector, to which the bw and clock values need to be ++ * updated ++ */ ++int msm_bus_scale_client_update_request(uint32_t cl, unsigned int index) ++{ ++ if (arb_ops.update_request) ++ return arb_ops.update_request(cl, index); ++ else { ++ pr_err("%s: Bus driver not ready.", ++ __func__); ++ return -EPROBE_DEFER; ++ } ++} ++EXPORT_SYMBOL(msm_bus_scale_client_update_request); ++ ++/** ++ * msm_bus_scale_unregister_client() - Unregister the client from the bus driver ++ * @cl: Handle to the client ++ */ ++void msm_bus_scale_unregister_client(uint32_t cl) ++{ ++ if (arb_ops.unregister_client) ++ arb_ops.unregister_client(cl); ++ else { ++ pr_err("%s: Bus driver not ready.", ++ __func__); ++ } ++} ++EXPORT_SYMBOL(msm_bus_scale_unregister_client); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_core.c +@@ -0,0 +1,125 @@ ++/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm-bus.h" ++#include "msm_bus_core.h" ++ ++static atomic_t num_fab = ATOMIC_INIT(0); ++ ++int msm_bus_get_num_fab(void) ++{ ++ return atomic_read(&num_fab); ++} ++ ++int msm_bus_device_match(struct device *dev, void *id) ++{ ++ struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); ++ ++ if (!fabdev) { ++ MSM_BUS_WARN("Fabric %p returning 0\n", fabdev); ++ return 0; ++ } ++ return fabdev->id == *(int *)id; ++} ++ ++static void msm_bus_release(struct device *device) ++{ ++} ++ ++struct bus_type msm_bus_type = { ++ .name = "msm-bus-type", ++}; ++EXPORT_SYMBOL(msm_bus_type); ++ ++/** ++ * msm_bus_get_fabric_device() - This function is used to search for ++ * the fabric device on the bus ++ * @fabid: Fabric id ++ * Function returns: Pointer to the fabric device ++ */ ++struct msm_bus_fabric_device *msm_bus_get_fabric_device(int fabid) ++{ ++ struct device *dev; ++ struct msm_bus_fabric_device *fabric; ++ dev = bus_find_device(&msm_bus_type, NULL, (void *)&fabid, ++ msm_bus_device_match); ++ if (!dev) ++ return NULL; ++ fabric = to_msm_bus_fabric_device(dev); ++ return fabric; ++} ++ ++/** ++ * msm_bus_fabric_device_register() - Registers a fabric on msm bus ++ * @fabdev: Fabric device to be registered ++ */ ++int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabdev) ++{ ++ int ret = 0; ++ fabdev->dev.bus = &msm_bus_type; ++ fabdev->dev.release = msm_bus_release; ++ ret = dev_set_name(&fabdev->dev, fabdev->name); ++ if (ret) { ++ MSM_BUS_ERR("error setting dev name\n"); ++ goto err; ++ } ++ ++ ret = device_register(&fabdev->dev); ++ if (ret < 0) { ++ MSM_BUS_ERR("error registering device%d %s\n", ++ ret, fabdev->name); ++ goto err; ++ } ++ atomic_inc(&num_fab); ++err: ++ return ret; ++} ++ ++/** ++ * msm_bus_fabric_device_unregister() - Unregisters the fabric ++ * devices from the msm bus ++ */ ++void msm_bus_fabric_device_unregister(struct msm_bus_fabric_device *fabdev) ++{ ++ device_unregister(&fabdev->dev); ++ atomic_dec(&num_fab); ++} ++ ++static void __exit msm_bus_exit(void) ++{ ++ bus_unregister(&msm_bus_type); ++} ++ ++static int __init msm_bus_init(void) ++{ ++ int retval = 0; ++ retval = bus_register(&msm_bus_type); ++ if (retval) ++ MSM_BUS_ERR("bus_register error! %d\n", ++ retval); ++ return retval; ++} ++postcore_initcall(msm_bus_init); ++module_exit(msm_bus_exit); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION("0.2"); ++MODULE_ALIAS("platform:msm_bus"); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_core.h +@@ -0,0 +1,375 @@ ++/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_BUS_CORE_H ++#define _ARCH_ARM_MACH_MSM_BUS_CORE_H ++ ++#include ++#include ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm-bus.h" ++ ++#define MSM_BUS_DBG(msg, ...) \ ++ pr_debug(msg, ## __VA_ARGS__) ++#define MSM_BUS_ERR(msg, ...) \ ++ pr_err(msg, ## __VA_ARGS__) ++#define MSM_BUS_WARN(msg, ...) \ ++ pr_warn(msg, ## __VA_ARGS__) ++#define MSM_FAB_ERR(msg, ...) \ ++ dev_err(&fabric->fabdev.dev, msg, ## __VA_ARGS__) ++ ++#define IS_MASTER_VALID(mas) \ ++ (((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \ ++ ? 1 : 0) ++#define IS_SLAVE_VALID(slv) \ ++ (((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0) ++ ++#define INTERLEAVED_BW(fab_pdata, bw, ports) \ ++ ((fab_pdata->il_flag) ? ((bw < 0) \ ++ ? -msm_bus_div64((ports), (-bw)) : msm_bus_div64((ports), (bw))) : (bw)) ++#define INTERLEAVED_VAL(fab_pdata, n) \ ++ ((fab_pdata->il_flag) ? (n) : 1) ++#define KBTOB(a) (a * 1000ULL) ++ ++enum msm_bus_dbg_op_type { ++ MSM_BUS_DBG_UNREGISTER = -2, ++ MSM_BUS_DBG_REGISTER, ++ MSM_BUS_DBG_OP = 1, ++}; ++ ++enum msm_bus_hw_sel { ++ MSM_BUS_RPM = 0, ++ MSM_BUS_NOC, ++ MSM_BUS_BIMC, ++}; ++ ++struct msm_bus_arb_ops { ++ uint32_t (*register_client)(struct msm_bus_scale_pdata *pdata); ++ int (*update_request)(uint32_t cl, unsigned int index); ++ void (*unregister_client)(uint32_t cl); ++}; ++ ++enum { ++ SLAVE_NODE, ++ MASTER_NODE, ++ CLK_NODE, ++ NR_LIM_NODE, ++}; ++ ++ ++extern struct bus_type msm_bus_type; ++extern struct msm_bus_arb_ops arb_ops; ++extern void msm_bus_arb_setops_legacy(struct msm_bus_arb_ops *arb_ops); ++ ++struct msm_bus_node_info { ++ unsigned int id; ++ unsigned int priv_id; ++ unsigned int mas_hw_id; ++ unsigned int slv_hw_id; ++ int gateway; ++ int *masterp; ++ int *qport; ++ int num_mports; ++ int *slavep; ++ int num_sports; ++ int *tier; ++ int num_tiers; ++ int ahb; ++ int hw_sel; ++ const char *slaveclk[NUM_CTX]; ++ const char *memclk[NUM_CTX]; ++ const char *iface_clk_node; ++ unsigned int buswidth; ++ unsigned int ws; ++ unsigned int mode; ++ unsigned int perm_mode; ++ unsigned int prio_lvl; ++ unsigned int prio_rd; ++ unsigned int prio_wr; ++ unsigned int prio1; ++ unsigned int prio0; ++ unsigned int num_thresh; ++ u64 *th; ++ u64 cur_lim_bw; ++ unsigned int mode_thresh; ++ bool dual_conf; ++ u64 *bimc_bw; ++ bool nr_lim; ++ u32 ff; ++ bool rt_mas; ++ u32 bimc_gp; ++ u32 bimc_thmp; ++ u64 floor_bw; ++ const char *name; ++}; ++ ++struct path_node { ++ uint64_t clk[NUM_CTX]; ++ uint64_t bw[NUM_CTX]; ++ uint64_t *sel_clk; ++ uint64_t *sel_bw; ++ int next; ++}; ++ ++struct msm_bus_link_info { ++ uint64_t clk[NUM_CTX]; ++ uint64_t *sel_clk; ++ uint64_t memclk; ++ int64_t bw[NUM_CTX]; ++ int64_t *sel_bw; ++ int *tier; ++ int num_tiers; ++}; ++ ++struct nodeclk { ++ struct clk *clk; ++ uint64_t rate; ++ bool dirty; ++ bool enable; ++}; ++ ++struct msm_bus_inode_info { ++ struct msm_bus_node_info *node_info; ++ uint64_t max_bw; ++ uint64_t max_clk; ++ uint64_t cur_lim_bw; ++ uint64_t cur_prg_bw; ++ struct msm_bus_link_info link_info; ++ int num_pnodes; ++ struct path_node *pnode; ++ int commit_index; ++ struct nodeclk nodeclk[NUM_CTX]; ++ struct nodeclk memclk[NUM_CTX]; ++ struct nodeclk iface_clk; ++ void *hw_data; ++}; ++ ++struct msm_bus_node_hw_info { ++ bool dirty; ++ unsigned int hw_id; ++ uint64_t bw; ++}; ++ ++struct msm_bus_hw_algorithm { ++ int (*allocate_commit_data)(struct msm_bus_fabric_registration ++ *fab_pdata, void **cdata, int ctx); ++ void *(*allocate_hw_data)(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *fab_pdata); ++ void (*node_init)(void *hw_data, struct msm_bus_inode_info *info); ++ void (*free_commit_data)(void *cdata); ++ void (*update_bw)(struct msm_bus_inode_info *hop, ++ struct msm_bus_inode_info *info, ++ struct msm_bus_fabric_registration *fab_pdata, ++ void *sel_cdata, int *master_tiers, ++ int64_t add_bw); ++ void (*fill_cdata_buffer)(int *curr, char *buf, const int max_size, ++ void *cdata, int nmasters, int nslaves, int ntslaves); ++ int (*commit)(struct msm_bus_fabric_registration ++ *fab_pdata, void *hw_data, void **cdata); ++ int (*port_unhalt)(uint32_t haltid, uint8_t mport); ++ int (*port_halt)(uint32_t haltid, uint8_t mport); ++ void (*config_master)(struct msm_bus_fabric_registration *fab_pdata, ++ struct msm_bus_inode_info *info, ++ uint64_t req_clk, uint64_t req_bw); ++ void (*config_limiter)(struct msm_bus_fabric_registration *fab_pdata, ++ struct msm_bus_inode_info *info); ++ bool (*update_bw_reg)(int mode); ++}; ++ ++struct msm_bus_fabric_device { ++ int id; ++ const char *name; ++ struct device dev; ++ const struct msm_bus_fab_algorithm *algo; ++ const struct msm_bus_board_algorithm *board_algo; ++ struct msm_bus_hw_algorithm hw_algo; ++ int visited; ++ int num_nr_lim; ++ u64 nr_lim_thresh; ++ u32 eff_fact; ++}; ++#define to_msm_bus_fabric_device(d) container_of(d, \ ++ struct msm_bus_fabric_device, d) ++ ++struct msm_bus_fabric { ++ struct msm_bus_fabric_device fabdev; ++ int ahb; ++ void *cdata[NUM_CTX]; ++ bool arb_dirty; ++ bool clk_dirty; ++ struct radix_tree_root fab_tree; ++ int num_nodes; ++ struct list_head gateways; ++ struct msm_bus_inode_info info; ++ struct msm_bus_fabric_registration *pdata; ++ void *hw_data; ++}; ++#define to_msm_bus_fabric(d) container_of(d, \ ++ struct msm_bus_fabric, d) ++ ++ ++struct msm_bus_fab_algorithm { ++ int (*update_clks)(struct msm_bus_fabric_device *fabdev, ++ struct msm_bus_inode_info *pme, int index, ++ uint64_t curr_clk, uint64_t req_clk, ++ uint64_t bwsum, int flag, int ctx, ++ unsigned int cl_active_flag); ++ int (*port_halt)(struct msm_bus_fabric_device *fabdev, int portid); ++ int (*port_unhalt)(struct msm_bus_fabric_device *fabdev, int portid); ++ int (*commit)(struct msm_bus_fabric_device *fabdev); ++ struct msm_bus_inode_info *(*find_node)(struct msm_bus_fabric_device ++ *fabdev, int id); ++ struct msm_bus_inode_info *(*find_gw_node)(struct msm_bus_fabric_device ++ *fabdev, int id); ++ struct list_head *(*get_gw_list)(struct msm_bus_fabric_device *fabdev); ++ void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct ++ msm_bus_inode_info * hop, struct msm_bus_inode_info *info, ++ int64_t add_bw, int *master_tiers, int ctx); ++ void (*config_master)(struct msm_bus_fabric_device *fabdev, ++ struct msm_bus_inode_info *info, uint64_t req_clk, ++ uint64_t req_bw); ++ void (*config_limiter)(struct msm_bus_fabric_device *fabdev, ++ struct msm_bus_inode_info *info); ++}; ++ ++struct msm_bus_board_algorithm { ++ int board_nfab; ++ void (*assign_iids)(struct msm_bus_fabric_registration *fabreg, ++ int fabid); ++ int (*get_iid)(int id); ++}; ++ ++/** ++ * Used to store the list of fabrics and other info to be ++ * maintained outside the fabric structure. ++ * Used while calculating path, and to find fabric ptrs ++ */ ++struct msm_bus_fabnodeinfo { ++ struct list_head list; ++ struct msm_bus_inode_info *info; ++}; ++ ++struct msm_bus_client { ++ int id; ++ struct msm_bus_scale_pdata *pdata; ++ int *src_pnode; ++ int curr; ++}; ++ ++uint64_t msm_bus_div64(unsigned int width, uint64_t bw); ++int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabric); ++void msm_bus_fabric_device_unregister(struct msm_bus_fabric_device *fabric); ++struct msm_bus_fabric_device *msm_bus_get_fabric_device(int fabid); ++int msm_bus_get_num_fab(void); ++ ++ ++int msm_bus_hw_fab_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo); ++void msm_bus_board_init(struct msm_bus_fabric_registration *pdata); ++void msm_bus_board_set_nfab(struct msm_bus_fabric_registration *pdata, ++ int nfab); ++#if defined(CONFIG_MSM_RPM_SMD) ++int msm_bus_rpm_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo); ++int msm_bus_remote_hw_commit(struct msm_bus_fabric_registration ++ *fab_pdata, void *hw_data, void **cdata); ++void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, ++ void *cdata, int nmasters, int nslaves, int ntslaves); ++#else ++static inline int msm_bus_rpm_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo) ++{ ++ return 0; ++} ++static inline int msm_bus_remote_hw_commit(struct msm_bus_fabric_registration ++ *fab_pdata, void *hw_data, void **cdata) ++{ ++ return 0; ++} ++static inline void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, ++ const int max_size, void *cdata, int nmasters, int nslaves, ++ int ntslaves) ++{ ++} ++#endif ++ ++int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo); ++int msm_bus_bimc_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo); ++#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) ++void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, int index, ++ uint32_t cl); ++void msm_bus_dbg_commit_data(const char *fabname, void *cdata, ++ int nmasters, int nslaves, int ntslaves, int op); ++#else ++static inline void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, ++ int index, uint32_t cl) ++{ ++} ++static inline void msm_bus_dbg_commit_data(const char *fabname, ++ void *cdata, int nmasters, int nslaves, int ntslaves, ++ int op) ++{ ++} ++#endif ++ ++#ifdef CONFIG_CORESIGHT ++int msmbus_coresight_init(struct platform_device *pdev); ++void msmbus_coresight_remove(struct platform_device *pdev); ++int msmbus_coresight_init_adhoc(struct platform_device *pdev, ++ struct device_node *of_node); ++void msmbus_coresight_remove_adhoc(struct platform_device *pdev); ++#else ++static inline int msmbus_coresight_init(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static inline void msmbus_coresight_remove(struct platform_device *pdev) ++{ ++} ++ ++static inline int msmbus_coresight_init_adhoc(struct platform_device *pdev, ++ struct device_node *of_node) ++{ ++ return 0; ++} ++ ++static inline void msmbus_coresight_remove_adhoc(struct platform_device *pdev) ++{ ++} ++#endif ++ ++ ++#ifdef CONFIG_OF ++void msm_bus_of_get_nfab(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *pdata); ++struct msm_bus_fabric_registration ++ *msm_bus_of_get_fab_data(struct platform_device *pdev); ++#else ++static inline void msm_bus_of_get_nfab(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *pdata) ++{ ++ return; ++} ++ ++static inline struct msm_bus_fabric_registration ++ *msm_bus_of_get_fab_data(struct platform_device *pdev) ++{ ++ return NULL; ++} ++#endif ++ ++#endif /*_ARCH_ARM_MACH_MSM_BUS_CORE_H*/ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_dbg.c +@@ -0,0 +1,810 @@ ++/* Copyright (c) 2010-2012, 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm-bus.h" ++#include "msm_bus_rules.h" ++#include "msm_bus_core.h" ++#include "msm_bus_adhoc.h" ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++#define MAX_BUFF_SIZE 4096 ++#define FILL_LIMIT 128 ++ ++static struct dentry *clients; ++static struct dentry *dir; ++static DEFINE_MUTEX(msm_bus_dbg_fablist_lock); ++struct msm_bus_dbg_state { ++ uint32_t cl; ++ uint8_t enable; ++ uint8_t current_index; ++} clstate; ++ ++struct msm_bus_cldata { ++ const struct msm_bus_scale_pdata *pdata; ++ int index; ++ uint32_t clid; ++ int size; ++ struct dentry *file; ++ struct list_head list; ++ char buffer[MAX_BUFF_SIZE]; ++}; ++ ++struct msm_bus_fab_list { ++ const char *name; ++ int size; ++ struct dentry *file; ++ struct list_head list; ++ char buffer[MAX_BUFF_SIZE]; ++}; ++ ++static char *rules_buf; ++ ++LIST_HEAD(fabdata_list); ++LIST_HEAD(cl_list); ++ ++/** ++ * The following structures and funtions are used for ++ * the test-client which can be created at run-time. ++ */ ++ ++static struct msm_bus_vectors init_vectors[1]; ++static struct msm_bus_vectors current_vectors[1]; ++static struct msm_bus_vectors requested_vectors[1]; ++ ++static struct msm_bus_paths shell_client_usecases[] = { ++ { ++ .num_paths = ARRAY_SIZE(init_vectors), ++ .vectors = init_vectors, ++ }, ++ { ++ .num_paths = ARRAY_SIZE(current_vectors), ++ .vectors = current_vectors, ++ }, ++ { ++ .num_paths = ARRAY_SIZE(requested_vectors), ++ .vectors = requested_vectors, ++ }, ++}; ++ ++static struct msm_bus_scale_pdata shell_client = { ++ .usecase = shell_client_usecases, ++ .num_usecases = ARRAY_SIZE(shell_client_usecases), ++ .name = "test-client", ++}; ++ ++static void msm_bus_dbg_init_vectors(void) ++{ ++ init_vectors[0].src = -1; ++ init_vectors[0].dst = -1; ++ init_vectors[0].ab = 0; ++ init_vectors[0].ib = 0; ++ current_vectors[0].src = -1; ++ current_vectors[0].dst = -1; ++ current_vectors[0].ab = 0; ++ current_vectors[0].ib = 0; ++ requested_vectors[0].src = -1; ++ requested_vectors[0].dst = -1; ++ requested_vectors[0].ab = 0; ++ requested_vectors[0].ib = 0; ++ clstate.enable = 0; ++ clstate.current_index = 0; ++} ++ ++static int msm_bus_dbg_update_cl_request(uint32_t cl) ++{ ++ int ret = 0; ++ ++ if (clstate.current_index < 2) ++ clstate.current_index = 2; ++ else { ++ clstate.current_index = 1; ++ current_vectors[0].ab = requested_vectors[0].ab; ++ current_vectors[0].ib = requested_vectors[0].ib; ++ } ++ ++ if (clstate.enable) { ++ MSM_BUS_DBG("Updating request for shell client, index: %d\n", ++ clstate.current_index); ++ ret = msm_bus_scale_client_update_request(clstate.cl, ++ clstate.current_index); ++ } else ++ MSM_BUS_DBG("Enable bit not set. Skipping update request\n"); ++ ++ return ret; ++} ++ ++static void msm_bus_dbg_unregister_client(uint32_t cl) ++{ ++ MSM_BUS_DBG("Unregistering shell client\n"); ++ msm_bus_scale_unregister_client(clstate.cl); ++ clstate.cl = 0; ++} ++ ++static uint32_t msm_bus_dbg_register_client(void) ++{ ++ int ret = 0; ++ ++ if (init_vectors[0].src != requested_vectors[0].src) { ++ MSM_BUS_DBG("Shell client master changed. Unregistering\n"); ++ msm_bus_dbg_unregister_client(clstate.cl); ++ } ++ if (init_vectors[0].dst != requested_vectors[0].dst) { ++ MSM_BUS_DBG("Shell client slave changed. Unregistering\n"); ++ msm_bus_dbg_unregister_client(clstate.cl); ++ } ++ ++ current_vectors[0].src = init_vectors[0].src; ++ requested_vectors[0].src = init_vectors[0].src; ++ current_vectors[0].dst = init_vectors[0].dst; ++ requested_vectors[0].dst = init_vectors[0].dst; ++ ++ if (!clstate.enable) { ++ MSM_BUS_DBG("Enable bit not set, skipping registration: cl " ++ "%d\n", clstate.cl); ++ return 0; ++ } ++ ++ if (clstate.cl) { ++ MSM_BUS_DBG("Client registered, skipping registration\n"); ++ return clstate.cl; ++ } ++ ++ MSM_BUS_DBG("Registering shell client\n"); ++ ret = msm_bus_scale_register_client(&shell_client); ++ return ret; ++} ++ ++static int msm_bus_dbg_mas_get(void *data, u64 *val) ++{ ++ *val = init_vectors[0].src; ++ MSM_BUS_DBG("Get master: %llu\n", *val); ++ return 0; ++} ++ ++static int msm_bus_dbg_mas_set(void *data, u64 val) ++{ ++ init_vectors[0].src = val; ++ MSM_BUS_DBG("Set master: %llu\n", val); ++ clstate.cl = msm_bus_dbg_register_client(); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(shell_client_mas_fops, msm_bus_dbg_mas_get, ++ msm_bus_dbg_mas_set, "%llu\n"); ++ ++static int msm_bus_dbg_slv_get(void *data, u64 *val) ++{ ++ *val = init_vectors[0].dst; ++ MSM_BUS_DBG("Get slave: %llu\n", *val); ++ return 0; ++} ++ ++static int msm_bus_dbg_slv_set(void *data, u64 val) ++{ ++ init_vectors[0].dst = val; ++ MSM_BUS_DBG("Set slave: %llu\n", val); ++ clstate.cl = msm_bus_dbg_register_client(); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(shell_client_slv_fops, msm_bus_dbg_slv_get, ++ msm_bus_dbg_slv_set, "%llu\n"); ++ ++static int msm_bus_dbg_ab_get(void *data, u64 *val) ++{ ++ *val = requested_vectors[0].ab; ++ MSM_BUS_DBG("Get ab: %llu\n", *val); ++ return 0; ++} ++ ++static int msm_bus_dbg_ab_set(void *data, u64 val) ++{ ++ requested_vectors[0].ab = val; ++ MSM_BUS_DBG("Set ab: %llu\n", val); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(shell_client_ab_fops, msm_bus_dbg_ab_get, ++ msm_bus_dbg_ab_set, "%llu\n"); ++ ++static int msm_bus_dbg_ib_get(void *data, u64 *val) ++{ ++ *val = requested_vectors[0].ib; ++ MSM_BUS_DBG("Get ib: %llu\n", *val); ++ return 0; ++} ++ ++static int msm_bus_dbg_ib_set(void *data, u64 val) ++{ ++ requested_vectors[0].ib = val; ++ MSM_BUS_DBG("Set ib: %llu\n", val); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(shell_client_ib_fops, msm_bus_dbg_ib_get, ++ msm_bus_dbg_ib_set, "%llu\n"); ++ ++static int msm_bus_dbg_en_get(void *data, u64 *val) ++{ ++ *val = clstate.enable; ++ MSM_BUS_DBG("Get enable: %llu\n", *val); ++ return 0; ++} ++ ++static int msm_bus_dbg_en_set(void *data, u64 val) ++{ ++ int ret = 0; ++ ++ clstate.enable = val; ++ if (clstate.enable) { ++ if (!clstate.cl) { ++ MSM_BUS_DBG("client: %u\n", clstate.cl); ++ clstate.cl = msm_bus_dbg_register_client(); ++ if (clstate.cl) ++ ret = msm_bus_dbg_update_cl_request(clstate.cl); ++ } else { ++ MSM_BUS_DBG("update request for cl: %u\n", clstate.cl); ++ ret = msm_bus_dbg_update_cl_request(clstate.cl); ++ } ++ } ++ ++ MSM_BUS_DBG("Set enable: %llu\n", val); ++ return ret; ++} ++DEFINE_SIMPLE_ATTRIBUTE(shell_client_en_fops, msm_bus_dbg_en_get, ++ msm_bus_dbg_en_set, "%llu\n"); ++ ++/** ++ * The following funtions are used for viewing the client data ++ * and changing the client request at run-time ++ */ ++ ++static ssize_t client_data_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int bsize = 0; ++ uint32_t cl = (uint32_t)(uintptr_t)file->private_data; ++ struct msm_bus_cldata *cldata = NULL; ++ int found = 0; ++ ++ list_for_each_entry(cldata, &cl_list, list) { ++ if (cldata->clid == cl) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return 0; ++ ++ bsize = cldata->size; ++ return simple_read_from_buffer(buf, count, ppos, ++ cldata->buffer, bsize); ++} ++ ++static int client_data_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static const struct file_operations client_data_fops = { ++ .open = client_data_open, ++ .read = client_data_read, ++}; ++ ++struct dentry *msm_bus_dbg_create(const char *name, mode_t mode, ++ struct dentry *dent, uint32_t clid) ++{ ++ if (dent == NULL) { ++ MSM_BUS_DBG("debugfs not ready yet\n"); ++ return NULL; ++ } ++ return debugfs_create_file(name, mode, dent, (void *)(uintptr_t)clid, ++ &client_data_fops); ++} ++ ++#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) ++static int msm_bus_dbg_record_client(const struct msm_bus_scale_pdata *pdata, ++ int index, uint32_t clid, struct dentry *file) ++{ ++ struct msm_bus_cldata *cldata; ++ ++ cldata = kmalloc(sizeof(struct msm_bus_cldata), GFP_KERNEL); ++ if (!cldata) { ++ MSM_BUS_DBG("Failed to allocate memory for client data\n"); ++ return -ENOMEM; ++ } ++ cldata->pdata = pdata; ++ cldata->index = index; ++ cldata->clid = clid; ++ cldata->file = file; ++ cldata->size = 0; ++ list_add_tail(&cldata->list, &cl_list); ++ return 0; ++} ++ ++static void msm_bus_dbg_free_client(uint32_t clid) ++{ ++ struct msm_bus_cldata *cldata = NULL; ++ ++ list_for_each_entry(cldata, &cl_list, list) { ++ if (cldata->clid == clid) { ++ debugfs_remove(cldata->file); ++ list_del(&cldata->list); ++ kfree(cldata); ++ break; ++ } ++ } ++} ++ ++static int msm_bus_dbg_fill_cl_buffer(const struct msm_bus_scale_pdata *pdata, ++ int index, uint32_t clid) ++{ ++ int i = 0, j; ++ char *buf = NULL; ++ struct msm_bus_cldata *cldata = NULL; ++ struct timespec ts; ++ int found = 0; ++ ++ list_for_each_entry(cldata, &cl_list, list) { ++ if (cldata->clid == clid) { ++ found = 1; ++ break; ++ } ++ } ++ ++ if (!found) ++ return -ENOENT; ++ ++ if (cldata->file == NULL) { ++ if (pdata->name == NULL) { ++ MSM_BUS_DBG("Client doesn't have a name\n"); ++ return -EINVAL; ++ } ++ cldata->file = msm_bus_dbg_create(pdata->name, S_IRUGO, ++ clients, clid); ++ } ++ ++ if (cldata->size < (MAX_BUFF_SIZE - FILL_LIMIT)) ++ i = cldata->size; ++ else { ++ i = 0; ++ cldata->size = 0; ++ } ++ buf = cldata->buffer; ++ ts = ktime_to_timespec(ktime_get()); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n%d.%d\n", ++ (int)ts.tv_sec, (int)ts.tv_nsec); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "curr : %d\n", index); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "masters: "); ++ ++ for (j = 0; j < pdata->usecase->num_paths; j++) ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%d ", ++ pdata->usecase[index].vectors[j].src); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nslaves : "); ++ for (j = 0; j < pdata->usecase->num_paths; j++) ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%d ", ++ pdata->usecase[index].vectors[j].dst); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nab : "); ++ for (j = 0; j < pdata->usecase->num_paths; j++) ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ", ++ pdata->usecase[index].vectors[j].ab); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nib : "); ++ for (j = 0; j < pdata->usecase->num_paths; j++) ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ", ++ pdata->usecase[index].vectors[j].ib); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n"); ++ ++ for (j = 0; j < pdata->usecase->num_paths; j++) ++ trace_bus_update_request((int)ts.tv_sec, (int)ts.tv_nsec, ++ pdata->name, index, ++ pdata->usecase[index].vectors[j].src, ++ pdata->usecase[index].vectors[j].dst, ++ pdata->usecase[index].vectors[j].ab, ++ pdata->usecase[index].vectors[j].ib); ++ ++ cldata->size = i; ++ return i; ++} ++#endif ++ ++static int msm_bus_dbg_update_request(struct msm_bus_cldata *cldata, int index) ++{ ++ int ret = 0; ++ ++ if ((index < 0) || (index > cldata->pdata->num_usecases)) { ++ MSM_BUS_DBG("Invalid index!\n"); ++ return -EINVAL; ++ } ++ ret = msm_bus_scale_client_update_request(cldata->clid, index); ++ return ret; ++} ++ ++static ssize_t msm_bus_dbg_update_request_write(struct file *file, ++ const char __user *ubuf, size_t cnt, loff_t *ppos) ++{ ++ struct msm_bus_cldata *cldata; ++ unsigned long index = 0; ++ int ret = 0; ++ char *chid; ++ char *buf = kmalloc((sizeof(char) * (cnt + 1)), GFP_KERNEL); ++ int found = 0; ++ ++ if (!buf || IS_ERR(buf)) { ++ MSM_BUS_ERR("Memory allocation for buffer failed\n"); ++ return -ENOMEM; ++ } ++ if (cnt == 0) { ++ kfree(buf); ++ return 0; ++ } ++ if (copy_from_user(buf, ubuf, cnt)) { ++ kfree(buf); ++ return -EFAULT; ++ } ++ buf[cnt] = '\0'; ++ chid = buf; ++ MSM_BUS_DBG("buffer: %s\n size: %zu\n", buf, sizeof(ubuf)); ++ ++ list_for_each_entry(cldata, &cl_list, list) { ++ if (strnstr(chid, cldata->pdata->name, cnt)) { ++ found = 1; ++ cldata = cldata; ++ strsep(&chid, " "); ++ if (chid) { ++ ret = kstrtoul(chid, 10, &index); ++ if (ret) { ++ MSM_BUS_DBG("Index conversion" ++ " failed\n"); ++ return -EFAULT; ++ } ++ } else { ++ MSM_BUS_DBG("Error parsing input. Index not" ++ " found\n"); ++ found = 0; ++ } ++ break; ++ } ++ } ++ ++ if (found) ++ msm_bus_dbg_update_request(cldata, index); ++ kfree(buf); ++ return cnt; ++} ++ ++/** ++ * The following funtions are used for viewing the commit data ++ * for each fabric ++ */ ++static ssize_t fabric_data_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct msm_bus_fab_list *fablist = NULL; ++ int bsize = 0; ++ ssize_t ret; ++ const char *name = file->private_data; ++ int found = 0; ++ ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ list_for_each_entry(fablist, &fabdata_list, list) { ++ if (strcmp(fablist->name, name) == 0) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return -ENOENT; ++ bsize = fablist->size; ++ ret = simple_read_from_buffer(buf, count, ppos, ++ fablist->buffer, bsize); ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ return ret; ++} ++ ++static const struct file_operations fabric_data_fops = { ++ .open = client_data_open, ++ .read = fabric_data_read, ++}; ++ ++static ssize_t rules_dbg_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t ret; ++ memset(rules_buf, 0, MAX_BUFF_SIZE); ++ print_rules_buf(rules_buf, MAX_BUFF_SIZE); ++ ret = simple_read_from_buffer(buf, count, ppos, ++ rules_buf, MAX_BUFF_SIZE); ++ return ret; ++} ++ ++static int rules_dbg_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static const struct file_operations rules_dbg_fops = { ++ .open = rules_dbg_open, ++ .read = rules_dbg_read, ++}; ++ ++#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) ++static int msm_bus_dbg_record_fabric(const char *fabname, struct dentry *file) ++{ ++ struct msm_bus_fab_list *fablist; ++ int ret = 0; ++ ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ fablist = kmalloc(sizeof(struct msm_bus_fab_list), GFP_KERNEL); ++ if (!fablist) { ++ MSM_BUS_DBG("Failed to allocate memory for commit data\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ fablist->name = fabname; ++ fablist->size = 0; ++ list_add_tail(&fablist->list, &fabdata_list); ++err: ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ return ret; ++} ++ ++static void msm_bus_dbg_free_fabric(const char *fabname) ++{ ++ struct msm_bus_fab_list *fablist = NULL; ++ ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ list_for_each_entry(fablist, &fabdata_list, list) { ++ if (strcmp(fablist->name, fabname) == 0) { ++ debugfs_remove(fablist->file); ++ list_del(&fablist->list); ++ kfree(fablist); ++ break; ++ } ++ } ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++} ++ ++static int msm_bus_dbg_fill_fab_buffer(const char *fabname, ++ void *cdata, int nmasters, int nslaves, ++ int ntslaves) ++{ ++ int i; ++ char *buf = NULL; ++ struct msm_bus_fab_list *fablist = NULL; ++ struct timespec ts; ++ int found = 0; ++ ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ list_for_each_entry(fablist, &fabdata_list, list) { ++ if (strcmp(fablist->name, fabname) == 0) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return -ENOENT; ++ ++ if (fablist->file == NULL) { ++ MSM_BUS_DBG("Fabric dbg entry does not exist\n"); ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ return -EFAULT; ++ } ++ ++ if (fablist->size < MAX_BUFF_SIZE - 256) ++ i = fablist->size; ++ else { ++ i = 0; ++ fablist->size = 0; ++ } ++ buf = fablist->buffer; ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ ts = ktime_to_timespec(ktime_get()); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n%d.%d\n", ++ (int)ts.tv_sec, (int)ts.tv_nsec); ++ ++ msm_bus_rpm_fill_cdata_buffer(&i, buf, MAX_BUFF_SIZE, cdata, ++ nmasters, nslaves, ntslaves); ++ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n"); ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ fablist->size = i; ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ return 0; ++} ++#endif ++ ++static const struct file_operations msm_bus_dbg_update_request_fops = { ++ .open = client_data_open, ++ .write = msm_bus_dbg_update_request_write, ++}; ++ ++#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) ++/** ++ * msm_bus_dbg_client_data() - Add debug data for clients ++ * @pdata: Platform data of the client ++ * @index: The current index or operation to be performed ++ * @clid: Client handle obtained during registration ++ */ ++void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, int index, ++ uint32_t clid) ++{ ++ struct dentry *file = NULL; ++ ++ if (index == MSM_BUS_DBG_REGISTER) { ++ msm_bus_dbg_record_client(pdata, index, clid, file); ++ if (!pdata->name) { ++ MSM_BUS_DBG("Cannot create debugfs entry. Null name\n"); ++ return; ++ } ++ } else if (index == MSM_BUS_DBG_UNREGISTER) { ++ msm_bus_dbg_free_client(clid); ++ MSM_BUS_DBG("Client %d unregistered\n", clid); ++ } else ++ msm_bus_dbg_fill_cl_buffer(pdata, index, clid); ++} ++EXPORT_SYMBOL(msm_bus_dbg_client_data); ++ ++/** ++ * msm_bus_dbg_commit_data() - Add commit data from fabrics ++ * @fabname: Fabric name specified in platform data ++ * @cdata: Commit Data ++ * @nmasters: Number of masters attached to fabric ++ * @nslaves: Number of slaves attached to fabric ++ * @ntslaves: Number of tiered slaves attached to fabric ++ * @op: Operation to be performed ++ */ ++void msm_bus_dbg_commit_data(const char *fabname, void *cdata, ++ int nmasters, int nslaves, int ntslaves, int op) ++{ ++ struct dentry *file = NULL; ++ ++ if (op == MSM_BUS_DBG_REGISTER) ++ msm_bus_dbg_record_fabric(fabname, file); ++ else if (op == MSM_BUS_DBG_UNREGISTER) ++ msm_bus_dbg_free_fabric(fabname); ++ else ++ msm_bus_dbg_fill_fab_buffer(fabname, cdata, nmasters, ++ nslaves, ntslaves); ++} ++EXPORT_SYMBOL(msm_bus_dbg_commit_data); ++#endif ++ ++static int __init msm_bus_debugfs_init(void) ++{ ++ struct dentry *commit, *shell_client, *rules_dbg; ++ struct msm_bus_fab_list *fablist; ++ struct msm_bus_cldata *cldata = NULL; ++ uint64_t val = 0; ++ ++ dir = debugfs_create_dir("msm-bus-dbg", NULL); ++ if ((!dir) || IS_ERR(dir)) { ++ MSM_BUS_ERR("Couldn't create msm-bus-dbg\n"); ++ goto err; ++ } ++ ++ clients = debugfs_create_dir("client-data", dir); ++ if ((!dir) || IS_ERR(dir)) { ++ MSM_BUS_ERR("Couldn't create clients\n"); ++ goto err; ++ } ++ ++ shell_client = debugfs_create_dir("shell-client", dir); ++ if ((!dir) || IS_ERR(dir)) { ++ MSM_BUS_ERR("Couldn't create clients\n"); ++ goto err; ++ } ++ ++ commit = debugfs_create_dir("commit-data", dir); ++ if ((!dir) || IS_ERR(dir)) { ++ MSM_BUS_ERR("Couldn't create commit\n"); ++ goto err; ++ } ++ ++ rules_dbg = debugfs_create_dir("rules-dbg", dir); ++ if ((!rules_dbg) || IS_ERR(rules_dbg)) { ++ MSM_BUS_ERR("Couldn't create rules-dbg\n"); ++ goto err; ++ } ++ ++ if (debugfs_create_file("print_rules", S_IRUGO | S_IWUSR, ++ rules_dbg, &val, &rules_dbg_fops) == NULL) ++ goto err; ++ ++ if (debugfs_create_file("update_request", S_IRUGO | S_IWUSR, ++ shell_client, &val, &shell_client_en_fops) == NULL) ++ goto err; ++ if (debugfs_create_file("ib", S_IRUGO | S_IWUSR, shell_client, &val, ++ &shell_client_ib_fops) == NULL) ++ goto err; ++ if (debugfs_create_file("ab", S_IRUGO | S_IWUSR, shell_client, &val, ++ &shell_client_ab_fops) == NULL) ++ goto err; ++ if (debugfs_create_file("slv", S_IRUGO | S_IWUSR, shell_client, ++ &val, &shell_client_slv_fops) == NULL) ++ goto err; ++ if (debugfs_create_file("mas", S_IRUGO | S_IWUSR, shell_client, ++ &val, &shell_client_mas_fops) == NULL) ++ goto err; ++ if (debugfs_create_file("update-request", S_IRUGO | S_IWUSR, ++ clients, NULL, &msm_bus_dbg_update_request_fops) == NULL) ++ goto err; ++ ++ rules_buf = kzalloc(MAX_BUFF_SIZE, GFP_KERNEL); ++ if (!rules_buf) { ++ MSM_BUS_ERR("Failed to alloc rules_buf"); ++ goto err; ++ } ++ ++ list_for_each_entry(cldata, &cl_list, list) { ++ if (cldata->pdata->name == NULL) { ++ MSM_BUS_DBG("Client name not found\n"); ++ continue; ++ } ++ cldata->file = msm_bus_dbg_create(cldata-> ++ pdata->name, S_IRUGO, clients, cldata->clid); ++ } ++ ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ list_for_each_entry(fablist, &fabdata_list, list) { ++ fablist->file = debugfs_create_file(fablist->name, S_IRUGO, ++ commit, (void *)fablist->name, &fabric_data_fops); ++ if (fablist->file == NULL) { ++ MSM_BUS_DBG("Cannot create files for commit data\n"); ++ kfree(rules_buf); ++ goto err; ++ } ++ } ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++ ++ msm_bus_dbg_init_vectors(); ++ return 0; ++err: ++ debugfs_remove_recursive(dir); ++ return -ENODEV; ++} ++late_initcall(msm_bus_debugfs_init); ++ ++static void __exit msm_bus_dbg_teardown(void) ++{ ++ struct msm_bus_fab_list *fablist = NULL, *fablist_temp; ++ struct msm_bus_cldata *cldata = NULL, *cldata_temp; ++ ++ debugfs_remove_recursive(dir); ++ list_for_each_entry_safe(cldata, cldata_temp, &cl_list, list) { ++ list_del(&cldata->list); ++ kfree(cldata); ++ } ++ mutex_lock(&msm_bus_dbg_fablist_lock); ++ list_for_each_entry_safe(fablist, fablist_temp, &fabdata_list, list) { ++ list_del(&fablist->list); ++ kfree(fablist); ++ } ++ kfree(rules_buf); ++ mutex_unlock(&msm_bus_dbg_fablist_lock); ++} ++module_exit(msm_bus_dbg_teardown); ++MODULE_DESCRIPTION("Debugfs for msm bus scaling client"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Gagan Mac "); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_fabric_adhoc.c +@@ -0,0 +1,1281 @@ ++/* Copyright (c) 2014, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rpm-smd.h" ++#include "msm_bus_core.h" ++#include "msm_bus_adhoc.h" ++#include "msm_bus_noc.h" ++#include "msm_bus_bimc.h" ++ ++ssize_t vrail_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct msm_bus_node_info_type *node_info = NULL; ++ struct msm_bus_node_device_type *bus_node = NULL; ++ ++ bus_node = dev->platform_data; ++ if (!bus_node) ++ return -EINVAL; ++ node_info = bus_node->node_info; ++ ++ return snprintf(buf, PAGE_SIZE, "%u", node_info->vrail_comp); ++} ++ ++ssize_t vrail_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct msm_bus_node_info_type *node_info = NULL; ++ struct msm_bus_node_device_type *bus_node = NULL; ++ int ret = 0; ++ ++ bus_node = dev->platform_data; ++ if (!bus_node) ++ return -EINVAL; ++ node_info = bus_node->node_info; ++ ++ ret = sscanf(buf, "%u", &node_info->vrail_comp); ++ if (ret != 1) ++ return -EINVAL; ++ return count; ++} ++ ++DEVICE_ATTR(vrail, 0600, vrail_show, vrail_store); ++ ++struct static_rules_type { ++ int num_rules; ++ struct bus_rule_type *rules; ++}; ++ ++static struct static_rules_type static_rules; ++ ++static int enable_nodeclk(struct nodeclk *nclk) ++{ ++ int ret = 0; ++ ++ if (!nclk->enable) { ++ ret = clk_prepare_enable(nclk->clk); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: failed to enable clk ", __func__); ++ nclk->enable = false; ++ } else ++ nclk->enable = true; ++ } ++ return ret; ++} ++ ++static int disable_nodeclk(struct nodeclk *nclk) ++{ ++ int ret = 0; ++ ++ if (nclk->enable) { ++ clk_disable_unprepare(nclk->clk); ++ nclk->enable = false; ++ } ++ return ret; ++} ++ ++static int setrate_nodeclk(struct nodeclk *nclk, long rate) ++{ ++ int ret = 0; ++ ++ ret = clk_set_rate(nclk->clk, rate); ++ ++ if (ret) ++ MSM_BUS_ERR("%s: failed to setrate clk", __func__); ++ return ret; ++} ++ ++static int msm_bus_agg_fab_clks(struct device *bus_dev, void *data) ++{ ++ struct msm_bus_node_device_type *node = NULL; ++ int ret = 0; ++ int ctx = *(int *)data; ++ ++ if (ctx >= NUM_CTX) { ++ MSM_BUS_ERR("%s: Invalid Context %d", __func__, ctx); ++ goto exit_agg_fab_clks; ++ } ++ ++ node = bus_dev->platform_data; ++ if (!node) { ++ MSM_BUS_ERR("%s: Can't get device info", __func__); ++ goto exit_agg_fab_clks; ++ } ++ ++ if (!node->node_info->is_fab_dev) { ++ struct msm_bus_node_device_type *bus_dev = NULL; ++ ++ bus_dev = node->node_info->bus_device->platform_data; ++ ++ if (node->cur_clk_hz[ctx] >= bus_dev->cur_clk_hz[ctx]) ++ bus_dev->cur_clk_hz[ctx] = node->cur_clk_hz[ctx]; ++ } ++ ++exit_agg_fab_clks: ++ return ret; ++} ++ ++static int msm_bus_reset_fab_clks(struct device *bus_dev, void *data) ++{ ++ struct msm_bus_node_device_type *node = NULL; ++ int ret = 0; ++ int ctx = *(int *)data; ++ ++ if (ctx >= NUM_CTX) { ++ MSM_BUS_ERR("%s: Invalid Context %d", __func__, ctx); ++ goto exit_reset_fab_clks; ++ } ++ ++ node = bus_dev->platform_data; ++ if (!node) { ++ MSM_BUS_ERR("%s: Can't get device info", __func__); ++ goto exit_reset_fab_clks; ++ } ++ ++ if (node->node_info->is_fab_dev) { ++ node->cur_clk_hz[ctx] = 0; ++ MSM_BUS_DBG("Resetting for node %d", node->node_info->id); ++ } ++exit_reset_fab_clks: ++ return ret; ++} ++ ++ ++static int send_rpm_msg(struct device *device) ++{ ++ int ret = 0; ++ int ctx; ++ int rsc_type; ++ struct msm_bus_node_device_type *ndev = ++ device->platform_data; ++ struct msm_rpm_kvp rpm_kvp; ++ ++ if (!ndev) { ++ MSM_BUS_ERR("%s: Error getting node info.", __func__); ++ ret = -ENODEV; ++ goto exit_send_rpm_msg; ++ } ++ ++ rpm_kvp.length = sizeof(uint64_t); ++ rpm_kvp.key = RPM_MASTER_FIELD_BW; ++ ++ for (ctx = MSM_RPM_CTX_ACTIVE_SET; ctx <= MSM_RPM_CTX_SLEEP_SET; ++ ctx++) { ++ if (ctx == MSM_RPM_CTX_ACTIVE_SET) ++ rpm_kvp.data = ++ (uint8_t *)&ndev->node_ab.ab[MSM_RPM_CTX_ACTIVE_SET]; ++ else { ++ rpm_kvp.data = ++ (uint8_t *) &ndev->node_ab.ab[MSM_RPM_CTX_SLEEP_SET]; ++ } ++ ++ if (ndev->node_info->mas_rpm_id != -1) { ++ rsc_type = RPM_BUS_MASTER_REQ; ++ ret = msm_rpm_send_message(ctx, rsc_type, ++ ndev->node_info->mas_rpm_id, &rpm_kvp, 1); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to send RPM message:", ++ __func__); ++ MSM_BUS_ERR("%s:Node Id %d RPM id %d", ++ __func__, ndev->node_info->id, ++ ndev->node_info->mas_rpm_id); ++ goto exit_send_rpm_msg; ++ } ++ } ++ ++ if (ndev->node_info->slv_rpm_id != -1) { ++ rsc_type = RPM_BUS_SLAVE_REQ; ++ ret = msm_rpm_send_message(ctx, rsc_type, ++ ndev->node_info->slv_rpm_id, &rpm_kvp, 1); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to send RPM message:", ++ __func__); ++ MSM_BUS_ERR("%s: Node Id %d RPM id %d", ++ __func__, ndev->node_info->id, ++ ndev->node_info->slv_rpm_id); ++ goto exit_send_rpm_msg; ++ } ++ } ++ } ++exit_send_rpm_msg: ++ return ret; ++} ++ ++static int flush_bw_data(struct device *node_device, int ctx) ++{ ++ struct msm_bus_node_device_type *node_info; ++ int ret = 0; ++ ++ node_info = node_device->platform_data; ++ if (!node_info) { ++ MSM_BUS_ERR("%s: Unable to find bus device for device", ++ __func__); ++ ret = -ENODEV; ++ goto exit_flush_bw_data; ++ } ++ ++ if (node_info->node_ab.dirty) { ++ if (node_info->ap_owned) { ++ struct msm_bus_node_device_type *bus_device = ++ node_info->node_info->bus_device->platform_data; ++ struct msm_bus_fab_device_type *fabdev = ++ bus_device->fabdev; ++ ++ if (fabdev && fabdev->noc_ops.update_bw_reg && ++ fabdev->noc_ops.update_bw_reg ++ (node_info->node_info->qos_params.mode)) ++ ret = fabdev->noc_ops.set_bw(node_info, ++ fabdev->qos_base, ++ fabdev->base_offset, ++ fabdev->qos_off, ++ fabdev->qos_freq); ++ } else { ++ ret = send_rpm_msg(node_device); ++ ++ if (ret) ++ MSM_BUS_ERR("%s: Failed to send RPM msg for%d", ++ __func__, node_info->node_info->id); ++ } ++ node_info->node_ab.dirty = false; ++ } ++ ++exit_flush_bw_data: ++ return ret; ++ ++} ++ ++static int flush_clk_data(struct device *node_device, int ctx) ++{ ++ struct msm_bus_node_device_type *node; ++ struct nodeclk *nodeclk = NULL; ++ int ret = 0; ++ ++ node = node_device->platform_data; ++ if (!node) { ++ MSM_BUS_ERR("Unable to find bus device"); ++ ret = -ENODEV; ++ goto exit_flush_clk_data; ++ } ++ ++ nodeclk = &node->clk[ctx]; ++ if (node->node_info->is_fab_dev) { ++ if (nodeclk->rate != node->cur_clk_hz[ctx]) { ++ nodeclk->rate = node->cur_clk_hz[ctx]; ++ nodeclk->dirty = true; ++ } ++ } ++ ++ if (nodeclk && nodeclk->clk && nodeclk->dirty) { ++ long rounded_rate; ++ ++ if (nodeclk->rate) { ++ rounded_rate = clk_round_rate(nodeclk->clk, ++ nodeclk->rate); ++ ret = setrate_nodeclk(nodeclk, rounded_rate); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to set_rate %lu for %d", ++ __func__, rounded_rate, ++ node->node_info->id); ++ ret = -ENODEV; ++ goto exit_flush_clk_data; ++ } ++ ++ ret = enable_nodeclk(nodeclk); ++ } else ++ ret = disable_nodeclk(nodeclk); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to enable for %d", __func__, ++ node->node_info->id); ++ ret = -ENODEV; ++ goto exit_flush_clk_data; ++ } ++ MSM_BUS_DBG("%s: Updated %d clk to %llu", __func__, ++ node->node_info->id, nodeclk->rate); ++ ++ } ++exit_flush_clk_data: ++ /* Reset the aggregated clock rate for fab devices*/ ++ if (node && node->node_info->is_fab_dev) ++ node->cur_clk_hz[ctx] = 0; ++ ++ if (nodeclk) ++ nodeclk->dirty = 0; ++ return ret; ++} ++ ++int msm_bus_commit_data(int *dirty_nodes, int ctx, int num_dirty) ++{ ++ int ret = 0; ++ int i = 0; ++ ++ /* Aggregate the bus clocks */ ++ bus_for_each_dev(&msm_bus_type, NULL, (void *)&ctx, ++ msm_bus_agg_fab_clks); ++ ++ for (i = 0; i < num_dirty; i++) { ++ struct device *node_device = ++ bus_find_device(&msm_bus_type, NULL, ++ (void *)&dirty_nodes[i], ++ msm_bus_device_match_adhoc); ++ ++ if (!node_device) { ++ MSM_BUS_ERR("Can't find device for %d", dirty_nodes[i]); ++ continue; ++ } ++ ++ ret = flush_bw_data(node_device, ctx); ++ if (ret) ++ MSM_BUS_ERR("%s: Error flushing bw data for node %d", ++ __func__, dirty_nodes[i]); ++ ++ ret = flush_clk_data(node_device, ctx); ++ if (ret) ++ MSM_BUS_ERR("%s: Error flushing clk data for node %d", ++ __func__, dirty_nodes[i]); ++ } ++ kfree(dirty_nodes); ++ /* Aggregate the bus clocks */ ++ bus_for_each_dev(&msm_bus_type, NULL, (void *)&ctx, ++ msm_bus_reset_fab_clks); ++ return ret; ++} ++ ++void *msm_bus_realloc_devmem(struct device *dev, void *p, size_t old_size, ++ size_t new_size, gfp_t flags) ++{ ++ void *ret; ++ size_t copy_size = old_size; ++ ++ if (!new_size) { ++ devm_kfree(dev, p); ++ return ZERO_SIZE_PTR; ++ } ++ ++ if (new_size < old_size) ++ copy_size = new_size; ++ ++ ret = devm_kzalloc(dev, new_size, flags); ++ if (!ret) { ++ MSM_BUS_ERR("%s: Error Reallocating memory", __func__); ++ goto exit_realloc_devmem; ++ } ++ ++ memcpy(ret, p, copy_size); ++ devm_kfree(dev, p); ++exit_realloc_devmem: ++ return ret; ++} ++ ++ ++static int add_dirty_node(int **dirty_nodes, int id, int *num_dirty) ++{ ++ int i; ++ int found = 0; ++ int ret = 0; ++ int *dnode = NULL; ++ ++ for (i = 0; i < *num_dirty; i++) { ++ if ((*dirty_nodes)[i] == id) { ++ found = 1; ++ break; ++ } ++ } ++ ++ if (!found) { ++ (*num_dirty)++; ++ dnode = ++ krealloc(*dirty_nodes, sizeof(int) * (*num_dirty), ++ GFP_KERNEL); ++ ++ if (ZERO_OR_NULL_PTR(dnode)) { ++ MSM_BUS_ERR("%s: Failure allocating dirty nodes array", ++ __func__); ++ ret = -ENOMEM; ++ } else { ++ *dirty_nodes = dnode; ++ (*dirty_nodes)[(*num_dirty) - 1] = id; ++ } ++ } ++ ++ return ret; ++} ++ ++int msm_bus_update_bw(struct msm_bus_node_device_type *nodedev, int ctx, ++ int64_t add_bw, int **dirty_nodes, int *num_dirty) ++{ ++ int ret = 0; ++ int i, j; ++ uint64_t cur_ab_slp = 0; ++ uint64_t cur_ab_act = 0; ++ ++ if (nodedev->node_info->virt_dev) ++ goto exit_update_bw; ++ ++ for (i = 0; i < NUM_CTX; i++) { ++ for (j = 0; j < nodedev->num_lnodes; j++) { ++ if (i == DUAL_CTX) { ++ cur_ab_act += ++ nodedev->lnode_list[j].lnode_ab[i]; ++ cur_ab_slp += ++ nodedev->lnode_list[j].lnode_ab[i]; ++ } else ++ cur_ab_act += ++ nodedev->lnode_list[j].lnode_ab[i]; ++ } ++ } ++ ++ if (nodedev->node_ab.ab[MSM_RPM_CTX_ACTIVE_SET] != cur_ab_act) { ++ nodedev->node_ab.ab[MSM_RPM_CTX_ACTIVE_SET] = cur_ab_act; ++ nodedev->node_ab.ab[MSM_RPM_CTX_SLEEP_SET] = cur_ab_slp; ++ nodedev->node_ab.dirty = true; ++ ret = add_dirty_node(dirty_nodes, nodedev->node_info->id, ++ num_dirty); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to add dirty node %d", __func__, ++ nodedev->node_info->id); ++ goto exit_update_bw; ++ } ++ } ++ ++exit_update_bw: ++ return ret; ++} ++ ++int msm_bus_update_clks(struct msm_bus_node_device_type *nodedev, ++ int ctx, int **dirty_nodes, int *num_dirty) ++{ ++ int status = 0; ++ struct nodeclk *nodeclk; ++ struct nodeclk *busclk; ++ struct msm_bus_node_device_type *bus_info = NULL; ++ uint64_t req_clk; ++ ++ bus_info = nodedev->node_info->bus_device->platform_data; ++ ++ if (!bus_info) { ++ MSM_BUS_ERR("%s: Unable to find bus device for device %d", ++ __func__, nodedev->node_info->id); ++ status = -ENODEV; ++ goto exit_set_clks; ++ } ++ ++ req_clk = nodedev->cur_clk_hz[ctx]; ++ busclk = &bus_info->clk[ctx]; ++ ++ if (busclk->rate != req_clk) { ++ busclk->rate = req_clk; ++ busclk->dirty = 1; ++ MSM_BUS_DBG("%s: Modifying bus clk %d Rate %llu", __func__, ++ bus_info->node_info->id, req_clk); ++ status = add_dirty_node(dirty_nodes, bus_info->node_info->id, ++ num_dirty); ++ ++ if (status) { ++ MSM_BUS_ERR("%s: Failed to add dirty node %d", __func__, ++ bus_info->node_info->id); ++ goto exit_set_clks; ++ } ++ } ++ ++ req_clk = nodedev->cur_clk_hz[ctx]; ++ nodeclk = &nodedev->clk[ctx]; ++ ++ if (IS_ERR_OR_NULL(nodeclk)) ++ goto exit_set_clks; ++ ++ if (!nodeclk->dirty || (nodeclk->dirty && (nodeclk->rate < req_clk))) { ++ nodeclk->rate = req_clk; ++ nodeclk->dirty = 1; ++ MSM_BUS_DBG("%s: Modifying node clk %d Rate %llu", __func__, ++ nodedev->node_info->id, req_clk); ++ status = add_dirty_node(dirty_nodes, nodedev->node_info->id, ++ num_dirty); ++ if (status) { ++ MSM_BUS_ERR("%s: Failed to add dirty node %d", __func__, ++ nodedev->node_info->id); ++ goto exit_set_clks; ++ } ++ } ++ ++exit_set_clks: ++ return status; ++} ++ ++static void msm_bus_fab_init_noc_ops(struct msm_bus_node_device_type *bus_dev) ++{ ++ switch (bus_dev->fabdev->bus_type) { ++ case MSM_BUS_NOC: ++ msm_bus_noc_set_ops(bus_dev); ++ break; ++ case MSM_BUS_BIMC: ++ msm_bus_bimc_set_ops(bus_dev); ++ break; ++ default: ++ MSM_BUS_ERR("%s: Invalid Bus type", __func__); ++ } ++} ++ ++static int msm_bus_qos_disable_clk(struct msm_bus_node_device_type *node, ++ int disable_bus_qos_clk) ++{ ++ struct msm_bus_node_device_type *bus_node = NULL; ++ int ret = 0; ++ ++ if (!node) { ++ ret = -ENXIO; ++ goto exit_disable_qos_clk; ++ } ++ ++ bus_node = node->node_info->bus_device->platform_data; ++ ++ if (!bus_node) { ++ ret = -ENXIO; ++ goto exit_disable_qos_clk; ++ } ++ ++ if (disable_bus_qos_clk) ++ ret = disable_nodeclk(&bus_node->clk[DUAL_CTX]); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to disable bus clk, node %d", ++ __func__, node->node_info->id); ++ goto exit_disable_qos_clk; ++ } ++ ++ if (!IS_ERR_OR_NULL(node->qos_clk.clk)) { ++ ret = disable_nodeclk(&node->qos_clk); ++ ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to disable mas qos clk,node %d", ++ __func__, node->node_info->id); ++ goto exit_disable_qos_clk; ++ } ++ } ++ ++exit_disable_qos_clk: ++ return ret; ++} ++ ++static int msm_bus_qos_enable_clk(struct msm_bus_node_device_type *node) ++{ ++ struct msm_bus_node_device_type *bus_node = NULL; ++ long rounded_rate; ++ int ret = 0; ++ int bus_qos_enabled = 0; ++ ++ if (!node) { ++ ret = -ENXIO; ++ goto exit_enable_qos_clk; ++ } ++ ++ bus_node = node->node_info->bus_device->platform_data; ++ ++ if (!bus_node) { ++ ret = -ENXIO; ++ goto exit_enable_qos_clk; ++ } ++ ++ /* Check if the bus clk is already set before trying to set it ++ * Do this only during ++ * a. Bootup ++ * b. Only for bus clks ++ **/ ++ if (!clk_get_rate(bus_node->clk[DUAL_CTX].clk)) { ++ rounded_rate = clk_round_rate(bus_node->clk[DUAL_CTX].clk, 1); ++ ret = setrate_nodeclk(&bus_node->clk[DUAL_CTX], rounded_rate); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to set bus clk, node %d", ++ __func__, node->node_info->id); ++ goto exit_enable_qos_clk; ++ } ++ ++ ret = enable_nodeclk(&bus_node->clk[DUAL_CTX]); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to enable bus clk, node %d", ++ __func__, node->node_info->id); ++ goto exit_enable_qos_clk; ++ } ++ bus_qos_enabled = 1; ++ } ++ ++ if (!IS_ERR_OR_NULL(node->qos_clk.clk)) { ++ rounded_rate = clk_round_rate(node->qos_clk.clk, 1); ++ ret = setrate_nodeclk(&node->qos_clk, rounded_rate); ++ if (ret) { ++ MSM_BUS_ERR("%s: Failed to enable mas qos clk, node %d", ++ __func__, node->node_info->id); ++ goto exit_enable_qos_clk; ++ } ++ ++ ret = enable_nodeclk(&node->qos_clk); ++ if (ret) { ++ MSM_BUS_ERR("Err enable mas qos clk, node %d ret %d", ++ node->node_info->id, ret); ++ goto exit_enable_qos_clk; ++ } ++ } ++ ret = bus_qos_enabled; ++ ++exit_enable_qos_clk: ++ return ret; ++} ++ ++int msm_bus_enable_limiter(struct msm_bus_node_device_type *node_dev, ++ bool enable, uint64_t lim_bw) ++{ ++ int ret = 0; ++ struct msm_bus_node_device_type *bus_node_dev; ++ ++ if (!node_dev) { ++ MSM_BUS_ERR("No device specified"); ++ ret = -ENXIO; ++ goto exit_enable_limiter; ++ } ++ ++ if (!node_dev->ap_owned) { ++ MSM_BUS_ERR("Device is not AP owned %d.", ++ node_dev->node_info->id); ++ ret = -ENXIO; ++ goto exit_enable_limiter; ++ } ++ ++ bus_node_dev = node_dev->node_info->bus_device->platform_data; ++ if (!bus_node_dev) { ++ MSM_BUS_ERR("Unable to get bus device infofor %d", ++ node_dev->node_info->id); ++ ret = -ENXIO; ++ goto exit_enable_limiter; ++ } ++ if (bus_node_dev->fabdev && ++ bus_node_dev->fabdev->noc_ops.limit_mport) { ++ ret = msm_bus_qos_enable_clk(node_dev); ++ if (ret < 0) { ++ MSM_BUS_ERR("Can't Enable QoS clk %d", ++ node_dev->node_info->id); ++ goto exit_enable_limiter; ++ } ++ bus_node_dev->fabdev->noc_ops.limit_mport( ++ node_dev, ++ bus_node_dev->fabdev->qos_base, ++ bus_node_dev->fabdev->base_offset, ++ bus_node_dev->fabdev->qos_off, ++ bus_node_dev->fabdev->qos_freq, ++ enable, lim_bw); ++ msm_bus_qos_disable_clk(node_dev, ret); ++ } ++ ++exit_enable_limiter: ++ return ret; ++} ++ ++static int msm_bus_dev_init_qos(struct device *dev, void *data) ++{ ++ int ret = 0; ++ struct msm_bus_node_device_type *node_dev = NULL; ++ ++ node_dev = dev->platform_data; ++ ++ if (!node_dev) { ++ MSM_BUS_ERR("%s: Unable to get node device info" , __func__); ++ ret = -ENXIO; ++ goto exit_init_qos; ++ } ++ ++ MSM_BUS_DBG("Device = %d", node_dev->node_info->id); ++ ++ if (node_dev->ap_owned) { ++ struct msm_bus_node_device_type *bus_node_info; ++ ++ bus_node_info = node_dev->node_info->bus_device->platform_data; ++ ++ if (!bus_node_info) { ++ MSM_BUS_ERR("%s: Unable to get bus device infofor %d", ++ __func__, ++ node_dev->node_info->id); ++ ret = -ENXIO; ++ goto exit_init_qos; ++ } ++ ++ if (bus_node_info->fabdev && ++ bus_node_info->fabdev->noc_ops.qos_init) { ++ int ret = 0; ++ ++ if (node_dev->ap_owned && ++ (node_dev->node_info->qos_params.mode) != -1) { ++ ++ if (bus_node_info->fabdev->bypass_qos_prg) ++ goto exit_init_qos; ++ ++ ret = msm_bus_qos_enable_clk(node_dev); ++ if (ret < 0) { ++ MSM_BUS_ERR("Can't Enable QoS clk %d", ++ node_dev->node_info->id); ++ goto exit_init_qos; ++ } ++ ++ bus_node_info->fabdev->noc_ops.qos_init( ++ node_dev, ++ bus_node_info->fabdev->qos_base, ++ bus_node_info->fabdev->base_offset, ++ bus_node_info->fabdev->qos_off, ++ bus_node_info->fabdev->qos_freq); ++ msm_bus_qos_disable_clk(node_dev, ret); ++ } ++ } else ++ MSM_BUS_ERR("%s: Skipping QOS init for %d", ++ __func__, node_dev->node_info->id); ++ } ++exit_init_qos: ++ return ret; ++} ++ ++static int msm_bus_fabric_init(struct device *dev, ++ struct msm_bus_node_device_type *pdata) ++{ ++ struct msm_bus_fab_device_type *fabdev; ++ struct msm_bus_node_device_type *node_dev = NULL; ++ int ret = 0; ++ ++ node_dev = dev->platform_data; ++ if (!node_dev) { ++ MSM_BUS_ERR("%s: Unable to get bus device info" , __func__); ++ ret = -ENXIO; ++ goto exit_fabric_init; ++ } ++ ++ if (node_dev->node_info->virt_dev) { ++ MSM_BUS_ERR("%s: Skip Fab init for virtual device %d", __func__, ++ node_dev->node_info->id); ++ goto exit_fabric_init; ++ } ++ ++ fabdev = devm_kzalloc(dev, sizeof(struct msm_bus_fab_device_type), ++ GFP_KERNEL); ++ if (!fabdev) { ++ MSM_BUS_ERR("Fabric alloc failed\n"); ++ ret = -ENOMEM; ++ goto exit_fabric_init; ++ } ++ ++ node_dev->fabdev = fabdev; ++ fabdev->pqos_base = pdata->fabdev->pqos_base; ++ fabdev->qos_range = pdata->fabdev->qos_range; ++ fabdev->base_offset = pdata->fabdev->base_offset; ++ fabdev->qos_off = pdata->fabdev->qos_off; ++ fabdev->qos_freq = pdata->fabdev->qos_freq; ++ fabdev->bus_type = pdata->fabdev->bus_type; ++ fabdev->bypass_qos_prg = pdata->fabdev->bypass_qos_prg; ++ fabdev->util_fact = pdata->fabdev->util_fact; ++ fabdev->vrail_comp = pdata->fabdev->vrail_comp; ++ msm_bus_fab_init_noc_ops(node_dev); ++ ++ fabdev->qos_base = devm_ioremap(dev, ++ fabdev->pqos_base, fabdev->qos_range); ++ if (!fabdev->qos_base) { ++ MSM_BUS_ERR("%s: Error remapping address 0x%zx :bus device %d", ++ __func__, ++ (size_t)fabdev->pqos_base, node_dev->node_info->id); ++ ret = -ENOMEM; ++ goto exit_fabric_init; ++ } ++ ++ /*if (msmbus_coresight_init(pdev)) ++ pr_warn("Coresight support absent for bus: %d\n", pdata->id);*/ ++exit_fabric_init: ++ return ret; ++} ++ ++static int msm_bus_init_clk(struct device *bus_dev, ++ struct msm_bus_node_device_type *pdata) ++{ ++ unsigned int ctx; ++ int ret = 0; ++ struct msm_bus_node_device_type *node_dev = bus_dev->platform_data; ++ ++ for (ctx = 0; ctx < NUM_CTX; ctx++) { ++ if (!IS_ERR_OR_NULL(pdata->clk[ctx].clk)) { ++ node_dev->clk[ctx].clk = pdata->clk[ctx].clk; ++ node_dev->clk[ctx].enable = false; ++ node_dev->clk[ctx].dirty = false; ++ MSM_BUS_ERR("%s: Valid node clk node %d ctx %d", ++ __func__, node_dev->node_info->id, ctx); ++ } ++ } ++ ++ if (!IS_ERR_OR_NULL(pdata->qos_clk.clk)) { ++ node_dev->qos_clk.clk = pdata->qos_clk.clk; ++ node_dev->qos_clk.enable = false; ++ MSM_BUS_ERR("%s: Valid Iface clk node %d", __func__, ++ node_dev->node_info->id); ++ } ++ ++ return ret; ++} ++ ++static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata, ++ struct device *bus_dev) ++{ ++ int ret = 0; ++ struct msm_bus_node_info_type *node_info = NULL; ++ struct msm_bus_node_info_type *pdata_node_info = NULL; ++ struct msm_bus_node_device_type *bus_node = NULL; ++ ++ bus_node = bus_dev->platform_data; ++ ++ if (!bus_node || !pdata) { ++ ret = -ENXIO; ++ MSM_BUS_ERR("%s: Invalid pointers pdata %p, bus_node %p", ++ __func__, pdata, bus_node); ++ goto exit_copy_node_info; ++ } ++ ++ node_info = bus_node->node_info; ++ pdata_node_info = pdata->node_info; ++ ++ node_info->name = pdata_node_info->name; ++ node_info->id = pdata_node_info->id; ++ node_info->bus_device_id = pdata_node_info->bus_device_id; ++ node_info->mas_rpm_id = pdata_node_info->mas_rpm_id; ++ node_info->slv_rpm_id = pdata_node_info->slv_rpm_id; ++ node_info->num_connections = pdata_node_info->num_connections; ++ node_info->num_blist = pdata_node_info->num_blist; ++ node_info->num_qports = pdata_node_info->num_qports; ++ node_info->buswidth = pdata_node_info->buswidth; ++ node_info->virt_dev = pdata_node_info->virt_dev; ++ node_info->is_fab_dev = pdata_node_info->is_fab_dev; ++ node_info->qos_params.mode = pdata_node_info->qos_params.mode; ++ node_info->qos_params.prio1 = pdata_node_info->qos_params.prio1; ++ node_info->qos_params.prio0 = pdata_node_info->qos_params.prio0; ++ node_info->qos_params.prio_lvl = pdata_node_info->qos_params.prio_lvl; ++ node_info->qos_params.prio_rd = pdata_node_info->qos_params.prio_rd; ++ node_info->qos_params.prio_wr = pdata_node_info->qos_params.prio_wr; ++ node_info->qos_params.gp = pdata_node_info->qos_params.gp; ++ node_info->qos_params.thmp = pdata_node_info->qos_params.thmp; ++ node_info->qos_params.ws = pdata_node_info->qos_params.ws; ++ node_info->qos_params.bw_buffer = pdata_node_info->qos_params.bw_buffer; ++ node_info->util_fact = pdata_node_info->util_fact; ++ node_info->vrail_comp = pdata_node_info->vrail_comp; ++ ++ node_info->dev_connections = devm_kzalloc(bus_dev, ++ sizeof(struct device *) * ++ pdata_node_info->num_connections, ++ GFP_KERNEL); ++ if (!node_info->dev_connections) { ++ MSM_BUS_ERR("%s:Bus dev connections alloc failed\n", __func__); ++ ret = -ENOMEM; ++ goto exit_copy_node_info; ++ } ++ ++ node_info->connections = devm_kzalloc(bus_dev, ++ sizeof(int) * pdata_node_info->num_connections, ++ GFP_KERNEL); ++ if (!node_info->connections) { ++ MSM_BUS_ERR("%s:Bus connections alloc failed\n", __func__); ++ devm_kfree(bus_dev, node_info->dev_connections); ++ ret = -ENOMEM; ++ goto exit_copy_node_info; ++ } ++ ++ memcpy(node_info->connections, ++ pdata_node_info->connections, ++ sizeof(int) * pdata_node_info->num_connections); ++ ++ node_info->black_connections = devm_kzalloc(bus_dev, ++ sizeof(struct device *) * ++ pdata_node_info->num_blist, ++ GFP_KERNEL); ++ if (!node_info->black_connections) { ++ MSM_BUS_ERR("%s: Bus black connections alloc failed\n", ++ __func__); ++ devm_kfree(bus_dev, node_info->dev_connections); ++ devm_kfree(bus_dev, node_info->connections); ++ ret = -ENOMEM; ++ goto exit_copy_node_info; ++ } ++ ++ node_info->black_listed_connections = devm_kzalloc(bus_dev, ++ pdata_node_info->num_blist * sizeof(int), ++ GFP_KERNEL); ++ if (!node_info->black_listed_connections) { ++ MSM_BUS_ERR("%s:Bus black list connections alloc failed\n", ++ __func__); ++ devm_kfree(bus_dev, node_info->black_connections); ++ devm_kfree(bus_dev, node_info->dev_connections); ++ devm_kfree(bus_dev, node_info->connections); ++ ret = -ENOMEM; ++ goto exit_copy_node_info; ++ } ++ ++ memcpy(node_info->black_listed_connections, ++ pdata_node_info->black_listed_connections, ++ sizeof(int) * pdata_node_info->num_blist); ++ ++ node_info->qport = devm_kzalloc(bus_dev, ++ sizeof(int) * pdata_node_info->num_qports, ++ GFP_KERNEL); ++ if (!node_info->qport) { ++ MSM_BUS_ERR("%s:Bus qport allocation failed\n", __func__); ++ devm_kfree(bus_dev, node_info->dev_connections); ++ devm_kfree(bus_dev, node_info->connections); ++ devm_kfree(bus_dev, node_info->black_listed_connections); ++ ret = -ENOMEM; ++ goto exit_copy_node_info; ++ } ++ ++ memcpy(node_info->qport, ++ pdata_node_info->qport, ++ sizeof(int) * pdata_node_info->num_qports); ++ ++exit_copy_node_info: ++ return ret; ++} ++ ++static struct device *msm_bus_device_init( ++ struct msm_bus_node_device_type *pdata) ++{ ++ struct device *bus_dev = NULL; ++ struct msm_bus_node_device_type *bus_node = NULL; ++ struct msm_bus_node_info_type *node_info = NULL; ++ int ret = 0; ++ ++ bus_dev = kzalloc(sizeof(struct device), GFP_KERNEL); ++ if (!bus_dev) { ++ MSM_BUS_ERR("%s:Device alloc failed\n", __func__); ++ bus_dev = NULL; ++ goto exit_device_init; ++ } ++ /** ++ * Init here so we can use devm calls ++ */ ++ device_initialize(bus_dev); ++ ++ bus_node = devm_kzalloc(bus_dev, ++ sizeof(struct msm_bus_node_device_type), GFP_KERNEL); ++ if (!bus_node) { ++ MSM_BUS_ERR("%s:Bus node alloc failed\n", __func__); ++ kfree(bus_dev); ++ bus_dev = NULL; ++ goto exit_device_init; ++ } ++ ++ node_info = devm_kzalloc(bus_dev, ++ sizeof(struct msm_bus_node_info_type), GFP_KERNEL); ++ if (!node_info) { ++ MSM_BUS_ERR("%s:Bus node info alloc failed\n", __func__); ++ devm_kfree(bus_dev, bus_node); ++ kfree(bus_dev); ++ bus_dev = NULL; ++ goto exit_device_init; ++ } ++ ++ bus_node->node_info = node_info; ++ bus_node->ap_owned = pdata->ap_owned; ++ bus_dev->platform_data = bus_node; ++ ++ if (msm_bus_copy_node_info(pdata, bus_dev) < 0) { ++ devm_kfree(bus_dev, bus_node); ++ devm_kfree(bus_dev, node_info); ++ kfree(bus_dev); ++ bus_dev = NULL; ++ goto exit_device_init; ++ } ++ ++ bus_dev->bus = &msm_bus_type; ++ dev_set_name(bus_dev, bus_node->node_info->name); ++ ++ ret = device_add(bus_dev); ++ if (ret < 0) { ++ MSM_BUS_ERR("%s: Error registering device %d", ++ __func__, pdata->node_info->id); ++ devm_kfree(bus_dev, bus_node); ++ devm_kfree(bus_dev, node_info->dev_connections); ++ devm_kfree(bus_dev, node_info->connections); ++ devm_kfree(bus_dev, node_info->black_connections); ++ devm_kfree(bus_dev, node_info->black_listed_connections); ++ devm_kfree(bus_dev, node_info); ++ kfree(bus_dev); ++ bus_dev = NULL; ++ goto exit_device_init; ++ } ++ device_create_file(bus_dev, &dev_attr_vrail); ++ ++exit_device_init: ++ return bus_dev; ++} ++ ++static int msm_bus_setup_dev_conn(struct device *bus_dev, void *data) ++{ ++ struct msm_bus_node_device_type *bus_node = NULL; ++ int ret = 0; ++ int j; ++ ++ bus_node = bus_dev->platform_data; ++ if (!bus_node) { ++ MSM_BUS_ERR("%s: Can't get device info", __func__); ++ ret = -ENODEV; ++ goto exit_setup_dev_conn; ++ } ++ ++ /* Setup parent bus device for this node */ ++ if (!bus_node->node_info->is_fab_dev) { ++ struct device *bus_parent_device = ++ bus_find_device(&msm_bus_type, NULL, ++ (void *)&bus_node->node_info->bus_device_id, ++ msm_bus_device_match_adhoc); ++ ++ if (!bus_parent_device) { ++ MSM_BUS_ERR("%s: Error finding parentdev %d parent %d", ++ __func__, ++ bus_node->node_info->id, ++ bus_node->node_info->bus_device_id); ++ ret = -ENXIO; ++ goto exit_setup_dev_conn; ++ } ++ bus_node->node_info->bus_device = bus_parent_device; ++ } ++ ++ bus_node->node_info->is_traversed = false; ++ ++ for (j = 0; j < bus_node->node_info->num_connections; j++) { ++ bus_node->node_info->dev_connections[j] = ++ bus_find_device(&msm_bus_type, NULL, ++ (void *)&bus_node->node_info->connections[j], ++ msm_bus_device_match_adhoc); ++ ++ if (!bus_node->node_info->dev_connections[j]) { ++ MSM_BUS_ERR("%s: Error finding conn %d for device %d", ++ __func__, bus_node->node_info->connections[j], ++ bus_node->node_info->id); ++ ret = -ENODEV; ++ goto exit_setup_dev_conn; ++ } ++ } ++ ++ for (j = 0; j < bus_node->node_info->num_blist; j++) { ++ bus_node->node_info->black_connections[j] = ++ bus_find_device(&msm_bus_type, NULL, ++ (void *)&bus_node->node_info-> ++ black_listed_connections[j], ++ msm_bus_device_match_adhoc); ++ ++ if (!bus_node->node_info->black_connections[j]) { ++ MSM_BUS_ERR("%s: Error finding conn %d for device %d\n", ++ __func__, bus_node->node_info-> ++ black_listed_connections[j], ++ bus_node->node_info->id); ++ ret = -ENODEV; ++ goto exit_setup_dev_conn; ++ } ++ } ++ ++exit_setup_dev_conn: ++ return ret; ++} ++ ++static int msm_bus_node_debug(struct device *bus_dev, void *data) ++{ ++ int j; ++ int ret = 0; ++ struct msm_bus_node_device_type *bus_node = NULL; ++ ++ bus_node = bus_dev->platform_data; ++ if (!bus_node) { ++ MSM_BUS_ERR("%s: Can't get device info", __func__); ++ ret = -ENODEV; ++ goto exit_node_debug; ++ } ++ ++ MSM_BUS_DBG("Device = %d buswidth %u", bus_node->node_info->id, ++ bus_node->node_info->buswidth); ++ for (j = 0; j < bus_node->node_info->num_connections; j++) { ++ struct msm_bus_node_device_type *bdev = ++ (struct msm_bus_node_device_type *) ++ bus_node->node_info->dev_connections[j]->platform_data; ++ MSM_BUS_DBG("\n\t Connection[%d] %d", j, bdev->node_info->id); ++ } ++ ++exit_node_debug: ++ return ret; ++} ++ ++static int msm_bus_device_probe(struct platform_device *pdev) ++{ ++ unsigned int i, ret; ++ struct msm_bus_device_node_registration *pdata; ++ ++ /* If possible, get pdata from device-tree */ ++ if (pdev->dev.of_node) ++ pdata = msm_bus_of_to_pdata(pdev); ++ else { ++ pdata = (struct msm_bus_device_node_registration *)pdev-> ++ dev.platform_data; ++ } ++ ++ if (IS_ERR_OR_NULL(pdata)) { ++ MSM_BUS_ERR("No platform data found"); ++ ret = -ENODATA; ++ goto exit_device_probe; ++ } ++ ++ for (i = 0; i < pdata->num_devices; i++) { ++ struct device *node_dev = NULL; ++ ++ node_dev = msm_bus_device_init(&pdata->info[i]); ++ ++ if (!node_dev) { ++ MSM_BUS_ERR("%s: Error during dev init for %d", ++ __func__, pdata->info[i].node_info->id); ++ ret = -ENXIO; ++ goto exit_device_probe; ++ } ++ ++ ret = msm_bus_init_clk(node_dev, &pdata->info[i]); ++ /*Is this a fabric device ?*/ ++ if (pdata->info[i].node_info->is_fab_dev) { ++ MSM_BUS_DBG("%s: %d is a fab", __func__, ++ pdata->info[i].node_info->id); ++ ret = msm_bus_fabric_init(node_dev, &pdata->info[i]); ++ if (ret) { ++ MSM_BUS_ERR("%s: Error intializing fab %d", ++ __func__, pdata->info[i].node_info->id); ++ goto exit_device_probe; ++ } ++ } ++ } ++ ++ ret = bus_for_each_dev(&msm_bus_type, NULL, NULL, ++ msm_bus_setup_dev_conn); ++ if (ret) { ++ MSM_BUS_ERR("%s: Error setting up dev connections", __func__); ++ goto exit_device_probe; ++ } ++ ++ ret = bus_for_each_dev(&msm_bus_type, NULL, NULL, msm_bus_dev_init_qos); ++ if (ret) { ++ MSM_BUS_ERR("%s: Error during qos init", __func__); ++ goto exit_device_probe; ++ } ++ ++ bus_for_each_dev(&msm_bus_type, NULL, NULL, msm_bus_node_debug); ++ ++ /* Register the arb layer ops */ ++ msm_bus_arb_setops_adhoc(&arb_ops); ++ devm_kfree(&pdev->dev, pdata->info); ++ devm_kfree(&pdev->dev, pdata); ++exit_device_probe: ++ return ret; ++} ++ ++static int msm_bus_device_rules_probe(struct platform_device *pdev) ++{ ++ struct bus_rule_type *rule_data = NULL; ++ int num_rules = 0; ++ ++ num_rules = msm_bus_of_get_static_rules(pdev, &rule_data); ++ ++ if (!rule_data) ++ goto exit_rules_probe; ++ ++ msm_rule_register(num_rules, rule_data, NULL); ++ static_rules.num_rules = num_rules; ++ static_rules.rules = rule_data; ++ pdev->dev.platform_data = &static_rules; ++ ++exit_rules_probe: ++ return 0; ++} ++ ++int msm_bus_device_rules_remove(struct platform_device *pdev) ++{ ++ struct static_rules_type *static_rules = NULL; ++ ++ static_rules = pdev->dev.platform_data; ++ if (static_rules) ++ msm_rule_unregister(static_rules->num_rules, ++ static_rules->rules, NULL); ++ return 0; ++} ++ ++static int msm_bus_free_dev(struct device *dev, void *data) ++{ ++ struct msm_bus_node_device_type *bus_node = NULL; ++ ++ bus_node = dev->platform_data; ++ ++ if (bus_node) ++ MSM_BUS_ERR("\n%s: Removing device %d", __func__, ++ bus_node->node_info->id); ++ device_unregister(dev); ++ return 0; ++} ++ ++int msm_bus_device_remove(struct platform_device *pdev) ++{ ++ bus_for_each_dev(&msm_bus_type, NULL, NULL, msm_bus_free_dev); ++ return 0; ++} ++ ++static struct of_device_id rules_match[] = { ++ {.compatible = "qcom,msm-bus-static-bw-rules"}, ++ {} ++}; ++ ++static struct platform_driver msm_bus_rules_driver = { ++ .probe = msm_bus_device_rules_probe, ++ .remove = msm_bus_device_rules_remove, ++ .driver = { ++ .name = "msm_bus_rules_device", ++ .owner = THIS_MODULE, ++ .of_match_table = rules_match, ++ }, ++}; ++ ++static struct of_device_id fabric_match[] = { ++ {.compatible = "qcom,msm-bus-device"}, ++ {} ++}; ++ ++static struct platform_driver msm_bus_device_driver = { ++ .probe = msm_bus_device_probe, ++ .remove = msm_bus_device_remove, ++ .driver = { ++ .name = "msm_bus_device", ++ .owner = THIS_MODULE, ++ .of_match_table = fabric_match, ++ }, ++}; ++ ++int __init msm_bus_device_init_driver(void) ++{ ++ int rc; ++ ++ MSM_BUS_ERR("msm_bus_fabric_init_driver\n"); ++ rc = platform_driver_register(&msm_bus_device_driver); ++ ++ if (rc) { ++ MSM_BUS_ERR("Failed to register bus device driver"); ++ return rc; ++ } ++ return platform_driver_register(&msm_bus_rules_driver); ++} ++subsys_initcall(msm_bus_device_init_driver); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_id.c +@@ -0,0 +1,94 @@ ++/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "msm-bus.h" ++#include "msm-bus-board.h" ++#include "msm_bus_core.h" ++#include "msm_bus_noc.h" ++#include "msm_bus_bimc.h" ++ ++static uint32_t master_iids[MSM_BUS_MASTER_LAST]; ++static uint32_t slave_iids[MSM_BUS_SLAVE_LAST - SLAVE_ID_KEY]; ++ ++static void msm_bus_assign_iids(struct msm_bus_fabric_registration ++ *fabreg, int fabid) ++{ ++ int i; ++ for (i = 0; i < fabreg->len; i++) { ++ if (!fabreg->info[i].gateway) { ++ fabreg->info[i].priv_id = fabid + fabreg->info[i].id; ++ if (fabreg->info[i].id < SLAVE_ID_KEY) { ++ if (fabreg->info[i].id >= MSM_BUS_MASTER_LAST) { ++ WARN(1, "id %d exceeds array size!\n", ++ fabreg->info[i].id); ++ continue; ++ } ++ ++ master_iids[fabreg->info[i].id] = ++ fabreg->info[i].priv_id; ++ } else { ++ if ((fabreg->info[i].id - SLAVE_ID_KEY) >= ++ (MSM_BUS_SLAVE_LAST - SLAVE_ID_KEY)) { ++ WARN(1, "id %d exceeds array size!\n", ++ fabreg->info[i].id); ++ continue; ++ } ++ ++ slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] ++ = fabreg->info[i].priv_id; ++ } ++ } else { ++ fabreg->info[i].priv_id = fabreg->info[i].id; ++ } ++ } ++} ++ ++static int msm_bus_get_iid(int id) ++{ ++ if ((id < SLAVE_ID_KEY && id >= MSM_BUS_MASTER_LAST) || ++ id >= MSM_BUS_SLAVE_LAST) { ++ MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); ++ return -EINVAL; ++ } ++ ++ return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] : ++ slave_iids[id - SLAVE_ID_KEY]), id); ++} ++ ++static struct msm_bus_board_algorithm msm_bus_id_algo = { ++ .get_iid = msm_bus_get_iid, ++ .assign_iids = msm_bus_assign_iids, ++}; ++ ++int msm_bus_board_rpm_get_il_ids(uint16_t *id) ++{ ++ return -ENXIO; ++} ++ ++void msm_bus_board_init(struct msm_bus_fabric_registration *pdata) ++{ ++ pdata->board_algo = &msm_bus_id_algo; ++} ++ ++void msm_bus_board_set_nfab(struct msm_bus_fabric_registration *pdata, ++ int nfab) ++{ ++ if (nfab <= 0) ++ return; ++ ++ msm_bus_id_algo.board_nfab = nfab; ++} +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_noc.c +@@ -0,0 +1,770 @@ ++/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: NOC: %s(): " fmt, __func__ ++ ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm_bus_core.h" ++#include "msm_bus_noc.h" ++#include "msm_bus_adhoc.h" ++ ++/* NOC_QOS generic */ ++#define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x)) ++#define SAT_SCALE 16 /* 16 bytes minimum for saturation */ ++#define BW_SCALE 256 /* 1/256 byte per cycle unit */ ++#define QOS_DEFAULT_BASEOFFSET 0x00003000 ++#define QOS_DEFAULT_DELTA 0x80 ++#define MAX_BW_FIELD (NOC_QOS_BWn_BW_BMSK >> NOC_QOS_BWn_BW_SHFT) ++#define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT) ++ ++#define NOC_QOS_REG_BASE(b, o) ((b) + (o)) ++ ++#define NOC_QOS_ID_COREIDn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + (d) * (n)) ++enum noc_qos_id_coreidn { ++ NOC_QOS_ID_COREIDn_RMSK = 0xffffffff, ++ NOC_QOS_ID_COREIDn_MAXn = 32, ++ NOC_QOS_ID_COREIDn_CORECHSUM_BMSK = 0xffffff00, ++ NOC_QOS_ID_COREIDn_CORECHSUM_SHFT = 0x8, ++ NOC_QOS_ID_COREIDn_CORETYPEID_BMSK = 0xff, ++ NOC_QOS_ID_COREIDn_CORETYPEID_SHFT = 0x0, ++}; ++ ++#define NOC_QOS_ID_REVISIONIDn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + 0x4 + (d) * (n)) ++enum noc_qos_id_revisionidn { ++ NOC_QOS_ID_REVISIONIDn_RMSK = 0xffffffff, ++ NOC_QOS_ID_REVISIONIDn_MAXn = 32, ++ NOC_QOS_ID_REVISIONIDn_FLEXNOCID_BMSK = 0xffffff00, ++ NOC_QOS_ID_REVISIONIDn_FLEXNOCID_SHFT = 0x8, ++ NOC_QOS_ID_REVISIONIDn_USERID_BMSK = 0xff, ++ NOC_QOS_ID_REVISIONIDn_USERID_SHFT = 0x0, ++}; ++ ++#define NOC_QOS_PRIORITYn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + 0x8 + (d) * (n)) ++enum noc_qos_id_priorityn { ++ NOC_QOS_PRIORITYn_RMSK = 0x0000000f, ++ NOC_QOS_PRIORITYn_MAXn = 32, ++ NOC_QOS_PRIORITYn_P1_BMSK = 0xc, ++ NOC_QOS_PRIORITYn_P1_SHFT = 0x2, ++ NOC_QOS_PRIORITYn_P0_BMSK = 0x3, ++ NOC_QOS_PRIORITYn_P0_SHFT = 0x0, ++}; ++ ++#define NOC_QOS_MODEn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + 0xC + (d) * (n)) ++enum noc_qos_id_moden_rmsk { ++ NOC_QOS_MODEn_RMSK = 0x00000003, ++ NOC_QOS_MODEn_MAXn = 32, ++ NOC_QOS_MODEn_MODE_BMSK = 0x3, ++ NOC_QOS_MODEn_MODE_SHFT = 0x0, ++}; ++ ++#define NOC_QOS_BWn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + 0x10 + (d) * (n)) ++enum noc_qos_id_bwn { ++ NOC_QOS_BWn_RMSK = 0x0000ffff, ++ NOC_QOS_BWn_MAXn = 32, ++ NOC_QOS_BWn_BW_BMSK = 0xffff, ++ NOC_QOS_BWn_BW_SHFT = 0x0, ++}; ++ ++/* QOS Saturation registers */ ++#define NOC_QOS_SATn_ADDR(b, o, n, d) \ ++ (NOC_QOS_REG_BASE(b, o) + 0x14 + (d) * (n)) ++enum noc_qos_id_saturationn { ++ NOC_QOS_SATn_RMSK = 0x000003ff, ++ NOC_QOS_SATn_MAXn = 32, ++ NOC_QOS_SATn_SAT_BMSK = 0x3ff, ++ NOC_QOS_SATn_SAT_SHFT = 0x0, ++}; ++ ++static int noc_div(uint64_t *a, uint32_t b) ++{ ++ if ((*a > 0) && (*a < b)) ++ return 1; ++ else ++ return do_div(*a, b); ++} ++ ++/** ++ * Calculates bw hardware is using from register values ++ * bw returned is in bytes/sec ++ */ ++static uint64_t noc_bw(uint32_t bw_field, uint32_t qos_freq) ++{ ++ uint64_t res; ++ uint32_t rem, scale; ++ ++ res = 2 * qos_freq * bw_field; ++ scale = BW_SCALE * 1000; ++ rem = noc_div(&res, scale); ++ MSM_BUS_DBG("NOC: Calculated bw: %llu\n", res * 1000000ULL); ++ return res * 1000000ULL; ++} ++ ++static uint32_t noc_bw_ceil(long int bw_field, uint32_t qos_freq) ++{ ++ uint64_t bw_temp = 2 * qos_freq * bw_field; ++ uint32_t scale = 1000 * BW_SCALE; ++ noc_div(&bw_temp, scale); ++ return bw_temp * 1000000; ++} ++#define MAX_BW(timebase) noc_bw_ceil(MAX_BW_FIELD, (timebase)) ++ ++/** ++ * Calculates ws hardware is using from register values ++ * ws returned is in nanoseconds ++ */ ++static uint32_t noc_ws(uint64_t bw, uint32_t sat, uint32_t qos_freq) ++{ ++ if (bw && qos_freq) { ++ uint32_t bwf = bw * qos_freq; ++ uint64_t scale = 1000000000000LL * BW_SCALE * ++ SAT_SCALE * sat; ++ noc_div(&scale, bwf); ++ MSM_BUS_DBG("NOC: Calculated ws: %llu\n", scale); ++ return scale; ++ } ++ ++ return 0; ++} ++#define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase)) ++ ++/* Calculate bandwidth field value for requested bandwidth */ ++static uint32_t noc_bw_field(uint64_t bw, uint32_t qos_freq) ++{ ++ uint32_t bw_field = 0; ++ ++ if (bw) { ++ uint32_t rem; ++ uint64_t bw_capped = min_t(uint64_t, bw, MAX_BW(qos_freq)); ++ uint64_t bwc = bw_capped * BW_SCALE; ++ uint64_t qf = 2 * qos_freq * 1000; ++ ++ rem = noc_div(&bwc, qf); ++ bw_field = (uint32_t)min_t(uint64_t, bwc, MAX_BW_FIELD); ++ } ++ ++ MSM_BUS_DBG("NOC: bw_field: %u\n", bw_field); ++ return bw_field; ++} ++ ++static uint32_t noc_sat_field(uint64_t bw, uint32_t ws, uint32_t qos_freq) ++{ ++ uint32_t sat_field = 0, win; ++ ++ if (bw) { ++ /* Limit to max bw and scale bw to 100 KB increments */ ++ uint64_t tbw, tscale; ++ uint64_t bw_scaled = min_t(uint64_t, bw, MAX_BW(qos_freq)); ++ uint32_t rem = noc_div(&bw_scaled, 100000); ++ ++ /** ++ * Calculate saturation from windows size. ++ * WS must be at least one arb period. ++ * Saturation must not exceed max field size ++ * ++ * Bandwidth is in 100KB increments ++ * Window size is in ns ++ * qos_freq is in KHz ++ */ ++ win = max(ws, 1000000 / qos_freq); ++ tbw = bw_scaled * win * qos_freq; ++ tscale = 10000000ULL * BW_SCALE * SAT_SCALE; ++ rem = noc_div(&tbw, tscale); ++ sat_field = (uint32_t)min_t(uint64_t, tbw, MAX_SAT_FIELD); ++ } ++ ++ MSM_BUS_DBG("NOC: sat_field: %d\n", sat_field); ++ return sat_field; ++} ++ ++static void noc_set_qos_mode(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, uint8_t mode, ++ uint8_t perm_mode) ++{ ++ if (mode < NOC_QOS_MODE_MAX && ++ ((1 << mode) & perm_mode)) { ++ uint32_t reg_val; ++ ++ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(base, qos_off, ++ mport, qos_delta)) & NOC_QOS_MODEn_RMSK; ++ writel_relaxed(((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) | ++ (mode & NOC_QOS_MODEn_MODE_BMSK)), ++ NOC_QOS_MODEn_ADDR(base, qos_off, mport, qos_delta)); ++ } ++ /* Ensure qos mode is set before exiting */ ++ wmb(); ++} ++ ++static void noc_set_qos_priority(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, ++ struct msm_bus_noc_qos_priority *priority) ++{ ++ uint32_t reg_val, val; ++ ++ reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(base, qos_off, mport, ++ qos_delta)) & NOC_QOS_PRIORITYn_RMSK; ++ val = priority->p1 << NOC_QOS_PRIORITYn_P1_SHFT; ++ writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P1_BMSK))) | ++ (val & NOC_QOS_PRIORITYn_P1_BMSK)), ++ NOC_QOS_PRIORITYn_ADDR(base, qos_off, mport, qos_delta)); ++ ++ reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(base, qos_off, mport, ++ qos_delta)) ++ & NOC_QOS_PRIORITYn_RMSK; ++ writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P0_BMSK))) | ++ (priority->p0 & NOC_QOS_PRIORITYn_P0_BMSK)), ++ NOC_QOS_PRIORITYn_ADDR(base, qos_off, mport, qos_delta)); ++ /* Ensure qos priority is set before exiting */ ++ wmb(); ++} ++ ++static void msm_bus_noc_set_qos_bw(void __iomem *base, uint32_t qos_off, ++ uint32_t qos_freq, uint32_t mport, uint32_t qos_delta, ++ uint8_t perm_mode, struct msm_bus_noc_qos_bw *qbw) ++{ ++ uint32_t reg_val, val, mode; ++ ++ if (!qos_freq) { ++ MSM_BUS_DBG("Zero QoS Freq\n"); ++ return; ++ } ++ ++ ++ /* If Limiter or Regulator modes are not supported, bw not available*/ ++ if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER | ++ NOC_QOS_PERM_MODE_REGULATOR)) { ++ uint32_t bw_val = noc_bw_field(qbw->bw, qos_freq); ++ uint32_t sat_val = noc_sat_field(qbw->bw, qbw->ws, ++ qos_freq); ++ ++ MSM_BUS_DBG("NOC: BW: perm_mode: %d bw_val: %d, sat_val: %d\n", ++ perm_mode, bw_val, sat_val); ++ /* ++ * If in Limiter/Regulator mode, first go to fixed mode. ++ * Clear QoS accumulator ++ **/ ++ mode = readl_relaxed(NOC_QOS_MODEn_ADDR(base, qos_off, ++ mport, qos_delta)) & NOC_QOS_MODEn_MODE_BMSK; ++ if (mode == NOC_QOS_MODE_REGULATOR || mode == ++ NOC_QOS_MODE_LIMITER) { ++ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR( ++ base, qos_off, mport, qos_delta)); ++ val = NOC_QOS_MODE_FIXED; ++ writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) ++ | (val & NOC_QOS_MODEn_MODE_BMSK), ++ NOC_QOS_MODEn_ADDR(base, qos_off, mport, ++ qos_delta)); ++ } ++ ++ reg_val = readl_relaxed(NOC_QOS_BWn_ADDR(base, qos_off, mport, ++ qos_delta)); ++ val = bw_val << NOC_QOS_BWn_BW_SHFT; ++ writel_relaxed(((reg_val & (~(NOC_QOS_BWn_BW_BMSK))) | ++ (val & NOC_QOS_BWn_BW_BMSK)), ++ NOC_QOS_BWn_ADDR(base, qos_off, mport, qos_delta)); ++ ++ MSM_BUS_DBG("NOC: BW: Wrote value: 0x%x\n", ((reg_val & ++ (~NOC_QOS_BWn_BW_BMSK)) | (val & ++ NOC_QOS_BWn_BW_BMSK))); ++ ++ reg_val = readl_relaxed(NOC_QOS_SATn_ADDR(base, qos_off, ++ mport, qos_delta)); ++ val = sat_val << NOC_QOS_SATn_SAT_SHFT; ++ writel_relaxed(((reg_val & (~(NOC_QOS_SATn_SAT_BMSK))) | ++ (val & NOC_QOS_SATn_SAT_BMSK)), ++ NOC_QOS_SATn_ADDR(base, qos_off, mport, qos_delta)); ++ ++ MSM_BUS_DBG("NOC: SAT: Wrote value: 0x%x\n", ((reg_val & ++ (~NOC_QOS_SATn_SAT_BMSK)) | (val & ++ NOC_QOS_SATn_SAT_BMSK))); ++ ++ /* Set mode back to what it was initially */ ++ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(base, qos_off, ++ mport, qos_delta)); ++ writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) ++ | (mode & NOC_QOS_MODEn_MODE_BMSK), ++ NOC_QOS_MODEn_ADDR(base, qos_off, mport, qos_delta)); ++ /* Ensure that all writes for bandwidth registers have ++ * completed before returning ++ */ ++ wmb(); ++ } ++} ++ ++uint8_t msm_bus_noc_get_qos_mode(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, uint32_t mode, uint32_t perm_mode) ++{ ++ if (NOC_QOS_MODES_ALL_PERM == perm_mode) ++ return readl_relaxed(NOC_QOS_MODEn_ADDR(base, qos_off, ++ mport, qos_delta)) & NOC_QOS_MODEn_MODE_BMSK; ++ else ++ return 31 - __CLZ(mode & ++ NOC_QOS_MODES_ALL_PERM); ++} ++ ++void msm_bus_noc_get_qos_priority(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, ++ struct msm_bus_noc_qos_priority *priority) ++{ ++ priority->p1 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(base, qos_off, ++ mport, qos_delta)) & NOC_QOS_PRIORITYn_P1_BMSK) >> ++ NOC_QOS_PRIORITYn_P1_SHFT; ++ ++ priority->p0 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(base, qos_off, ++ mport, qos_delta)) & NOC_QOS_PRIORITYn_P0_BMSK) >> ++ NOC_QOS_PRIORITYn_P0_SHFT; ++} ++ ++void msm_bus_noc_get_qos_bw(void __iomem *base, uint32_t qos_off, ++ uint32_t qos_freq, ++ uint32_t mport, uint32_t qos_delta, uint8_t perm_mode, ++ struct msm_bus_noc_qos_bw *qbw) ++{ ++ if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER | ++ NOC_QOS_PERM_MODE_REGULATOR)) { ++ uint32_t bw_val = readl_relaxed(NOC_QOS_BWn_ADDR( ++ base, qos_off, mport, qos_delta)) & NOC_QOS_BWn_BW_BMSK; ++ uint32_t sat = readl_relaxed(NOC_QOS_SATn_ADDR( ++ base, qos_off, mport, qos_delta)) ++ & NOC_QOS_SATn_SAT_BMSK; ++ ++ qbw->bw = noc_bw(bw_val, qos_freq); ++ qbw->ws = noc_ws(qbw->bw, sat, qos_freq); ++ } else { ++ qbw->bw = 0; ++ qbw->ws = 0; ++ } ++} ++ ++static int msm_bus_noc_mas_init(struct msm_bus_noc_info *ninfo, ++ struct msm_bus_inode_info *info) ++{ ++ int i; ++ struct msm_bus_noc_qos_priority *prio; ++ prio = kzalloc(sizeof(struct msm_bus_noc_qos_priority), ++ GFP_KERNEL); ++ if (!prio) { ++ MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n", ++ info->node_info->id); ++ return -ENOMEM; ++ } ++ ++ prio->read_prio = info->node_info->prio_rd; ++ prio->write_prio = info->node_info->prio_wr; ++ prio->p1 = info->node_info->prio1; ++ prio->p0 = info->node_info->prio0; ++ info->hw_data = (void *)prio; ++ ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ return 0; ++ } ++ ++ for (i = 0; i < info->node_info->num_mports; i++) { ++ if (info->node_info->mode != NOC_QOS_MODE_BYPASS) { ++ noc_set_qos_priority(ninfo->base, ninfo->qos_baseoffset, ++ info->node_info->qport[i], ninfo->qos_delta, ++ prio); ++ ++ if (info->node_info->mode != NOC_QOS_MODE_FIXED) { ++ struct msm_bus_noc_qos_bw qbw; ++ qbw.ws = info->node_info->ws; ++ qbw.bw = 0; ++ msm_bus_noc_set_qos_bw(ninfo->base, ++ ninfo->qos_baseoffset, ++ ninfo->qos_freq, info->node_info-> ++ qport[i], ninfo->qos_delta, ++ info->node_info->perm_mode, ++ &qbw); ++ } ++ } ++ ++ noc_set_qos_mode(ninfo->base, ninfo->qos_baseoffset, ++ info->node_info->qport[i], ninfo->qos_delta, ++ info->node_info->mode, ++ info->node_info->perm_mode); ++ } ++ ++ return 0; ++} ++ ++static void msm_bus_noc_node_init(void *hw_data, ++ struct msm_bus_inode_info *info) ++{ ++ struct msm_bus_noc_info *ninfo = ++ (struct msm_bus_noc_info *)hw_data; ++ ++ if (!IS_SLAVE(info->node_info->priv_id)) ++ if (info->node_info->hw_sel != MSM_BUS_RPM) ++ msm_bus_noc_mas_init(ninfo, info); ++} ++ ++static int msm_bus_noc_allocate_commit_data(struct msm_bus_fabric_registration ++ *fab_pdata, void **cdata, int ctx) ++{ ++ struct msm_bus_noc_commit **cd = (struct msm_bus_noc_commit **)cdata; ++ struct msm_bus_noc_info *ninfo = ++ (struct msm_bus_noc_info *)fab_pdata->hw_data; ++ ++ *cd = kzalloc(sizeof(struct msm_bus_noc_commit), GFP_KERNEL); ++ if (!*cd) { ++ MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); ++ return -ENOMEM; ++ } ++ ++ (*cd)->mas = ninfo->cdata[ctx].mas; ++ (*cd)->slv = ninfo->cdata[ctx].slv; ++ ++ return 0; ++} ++ ++static void *msm_bus_noc_allocate_noc_data(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *fab_pdata) ++{ ++ struct resource *noc_mem; ++ struct resource *noc_io; ++ struct msm_bus_noc_info *ninfo; ++ int i; ++ ++ ninfo = kzalloc(sizeof(struct msm_bus_noc_info), GFP_KERNEL); ++ if (!ninfo) { ++ MSM_BUS_DBG("Couldn't alloc mem for noc info\n"); ++ return NULL; ++ } ++ ++ ninfo->nmasters = fab_pdata->nmasters; ++ ninfo->nqos_masters = fab_pdata->nmasters; ++ ninfo->nslaves = fab_pdata->nslaves; ++ ninfo->qos_freq = fab_pdata->qos_freq; ++ ++ if (!fab_pdata->qos_baseoffset) ++ ninfo->qos_baseoffset = QOS_DEFAULT_BASEOFFSET; ++ else ++ ninfo->qos_baseoffset = fab_pdata->qos_baseoffset; ++ ++ if (!fab_pdata->qos_delta) ++ ninfo->qos_delta = QOS_DEFAULT_DELTA; ++ else ++ ninfo->qos_delta = fab_pdata->qos_delta; ++ ++ ninfo->mas_modes = kzalloc(sizeof(uint32_t) * fab_pdata->nmasters, ++ GFP_KERNEL); ++ if (!ninfo->mas_modes) { ++ MSM_BUS_DBG("Couldn't alloc mem for noc master-modes\n"); ++ kfree(ninfo); ++ return NULL; ++ } ++ ++ for (i = 0; i < NUM_CTX; i++) { ++ ninfo->cdata[i].mas = kzalloc(sizeof(struct ++ msm_bus_node_hw_info) * fab_pdata->nmasters * 2, ++ GFP_KERNEL); ++ if (!ninfo->cdata[i].mas) { ++ MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n"); ++ kfree(ninfo->mas_modes); ++ kfree(ninfo); ++ return NULL; ++ } ++ ++ ninfo->cdata[i].slv = kzalloc(sizeof(struct ++ msm_bus_node_hw_info) * fab_pdata->nslaves * 2, ++ GFP_KERNEL); ++ if (!ninfo->cdata[i].slv) { ++ MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n"); ++ kfree(ninfo->cdata[i].mas); ++ goto err; ++ } ++ } ++ ++ /* If it's a virtual fabric, don't get memory info */ ++ if (fab_pdata->virt) ++ goto skip_mem; ++ ++ noc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!noc_mem && !fab_pdata->virt) { ++ MSM_BUS_ERR("Cannot get NoC Base address\n"); ++ goto err; ++ } ++ ++ noc_io = request_mem_region(noc_mem->start, ++ resource_size(noc_mem), pdev->name); ++ if (!noc_io) { ++ MSM_BUS_ERR("NoC memory unavailable\n"); ++ goto err; ++ } ++ ++ ninfo->base = ioremap(noc_mem->start, resource_size(noc_mem)); ++ if (!ninfo->base) { ++ MSM_BUS_ERR("IOremap failed for NoC!\n"); ++ release_mem_region(noc_mem->start, resource_size(noc_mem)); ++ goto err; ++ } ++ ++skip_mem: ++ fab_pdata->hw_data = (void *)ninfo; ++ return (void *)ninfo; ++ ++err: ++ kfree(ninfo->mas_modes); ++ kfree(ninfo); ++ return NULL; ++} ++ ++static void free_commit_data(void *cdata) ++{ ++ struct msm_bus_noc_commit *cd = (struct msm_bus_noc_commit *)cdata; ++ ++ kfree(cd->mas); ++ kfree(cd->slv); ++ kfree(cd); ++} ++ ++static bool msm_bus_noc_update_bw_reg(int mode) ++{ ++ bool ret = false; ++ ++ if ((mode == NOC_QOS_MODE_LIMITER) || ++ (mode == NOC_QOS_MODE_REGULATOR)) ++ ret = true; ++ ++ return ret; ++} ++ ++static void msm_bus_noc_update_bw(struct msm_bus_inode_info *hop, ++ struct msm_bus_inode_info *info, ++ struct msm_bus_fabric_registration *fab_pdata, ++ void *sel_cdata, int *master_tiers, ++ int64_t add_bw) ++{ ++ struct msm_bus_noc_info *ninfo; ++ struct msm_bus_noc_qos_bw qos_bw; ++ int i, ports; ++ int64_t bw; ++ struct msm_bus_noc_commit *sel_cd = ++ (struct msm_bus_noc_commit *)sel_cdata; ++ ++ ninfo = (struct msm_bus_noc_info *)fab_pdata->hw_data; ++ if (!ninfo->qos_freq) { ++ MSM_BUS_DBG("NOC: No qos frequency to update bw\n"); ++ return; ++ } ++ ++ if (info->node_info->num_mports == 0) { ++ MSM_BUS_DBG("NOC: Skip Master BW\n"); ++ goto skip_mas_bw; ++ } ++ ++ ports = info->node_info->num_mports; ++ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports); ++ ++ MSM_BUS_DBG("NOC: Update bw for: %d: %lld\n", ++ info->node_info->priv_id, add_bw); ++ for (i = 0; i < ports; i++) { ++ sel_cd->mas[info->node_info->masterp[i]].bw += bw; ++ sel_cd->mas[info->node_info->masterp[i]].hw_id = ++ info->node_info->mas_hw_id; ++ MSM_BUS_DBG("NOC: Update mas_bw: ID: %d, BW: %llu ports:%d\n", ++ info->node_info->priv_id, ++ sel_cd->mas[info->node_info->masterp[i]].bw, ++ ports); ++ /* Check if info is a shared master. ++ * If it is, mark it dirty ++ * If it isn't, then set QOS Bandwidth ++ **/ ++ if (info->node_info->hw_sel == MSM_BUS_RPM) ++ sel_cd->mas[info->node_info->masterp[i]].dirty = 1; ++ else { ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No qos ports to update!\n"); ++ break; ++ } ++ ++ if (!(info->node_info->mode == NOC_QOS_MODE_REGULATOR) ++ || (info->node_info->mode == ++ NOC_QOS_MODE_LIMITER)) { ++ MSM_BUS_DBG("Skip QoS reg programming\n"); ++ break; ++ } ++ qos_bw.bw = sel_cd->mas[info->node_info->masterp[i]]. ++ bw; ++ qos_bw.ws = info->node_info->ws; ++ msm_bus_noc_set_qos_bw(ninfo->base, ++ ninfo->qos_baseoffset, ++ ninfo->qos_freq, ++ info->node_info->qport[i], ninfo->qos_delta, ++ info->node_info->perm_mode, &qos_bw); ++ MSM_BUS_DBG("NOC: QoS: Update mas_bw: ws: %u\n", ++ qos_bw.ws); ++ } ++ } ++ ++skip_mas_bw: ++ ports = hop->node_info->num_sports; ++ for (i = 0; i < ports; i++) { ++ sel_cd->slv[hop->node_info->slavep[i]].bw += add_bw; ++ sel_cd->slv[hop->node_info->slavep[i]].hw_id = ++ hop->node_info->slv_hw_id; ++ MSM_BUS_DBG("NOC: Update slave_bw for ID: %d -> %llu\n", ++ hop->node_info->priv_id, ++ sel_cd->slv[hop->node_info->slavep[i]].bw); ++ MSM_BUS_DBG("NOC: Update slave_bw for hw_id: %d, index: %d\n", ++ hop->node_info->slv_hw_id, hop->node_info->slavep[i]); ++ /* Check if hop is a shared slave. ++ * If it is, mark it dirty ++ * If it isn't, then nothing to be done as the ++ * slaves are in bypass mode. ++ **/ ++ if (hop->node_info->hw_sel == MSM_BUS_RPM) ++ sel_cd->slv[hop->node_info->slavep[i]].dirty = 1; ++ } ++} ++ ++static int msm_bus_noc_commit(struct msm_bus_fabric_registration ++ *fab_pdata, void *hw_data, void **cdata) ++{ ++ MSM_BUS_DBG("\nReached NOC Commit\n"); ++ msm_bus_remote_hw_commit(fab_pdata, hw_data, cdata); ++ return 0; ++} ++ ++static int msm_bus_noc_port_halt(uint32_t haltid, uint8_t mport) ++{ ++ return 0; ++} ++ ++static int msm_bus_noc_port_unhalt(uint32_t haltid, uint8_t mport) ++{ ++ return 0; ++} ++ ++static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info, ++ void __iomem *qos_base, ++ uint32_t qos_off, uint32_t qos_delta, ++ uint32_t qos_freq) ++{ ++ struct msm_bus_noc_qos_priority prio; ++ int ret = 0; ++ int i; ++ ++ prio.p1 = info->node_info->qos_params.prio1; ++ prio.p0 = info->node_info->qos_params.prio0; ++ ++ if (!info->node_info->qport) { ++ MSM_BUS_DBG("No QoS Ports to init\n"); ++ ret = 0; ++ goto err_qos_init; ++ } ++ ++ for (i = 0; i < info->node_info->num_qports; i++) { ++ if (info->node_info->qos_params.mode != NOC_QOS_MODE_BYPASS) { ++ noc_set_qos_priority(qos_base, qos_off, ++ info->node_info->qport[i], qos_delta, ++ &prio); ++ ++ if (info->node_info->qos_params.mode != ++ NOC_QOS_MODE_FIXED) { ++ struct msm_bus_noc_qos_bw qbw; ++ qbw.ws = info->node_info->qos_params.ws; ++ qbw.bw = 0; ++ msm_bus_noc_set_qos_bw(qos_base, qos_off, ++ qos_freq, ++ info->node_info->qport[i], ++ qos_delta, ++ info->node_info->qos_params.mode, ++ &qbw); ++ } ++ } ++ ++ noc_set_qos_mode(qos_base, qos_off, info->node_info->qport[i], ++ qos_delta, info->node_info->qos_params.mode, ++ (1 << info->node_info->qos_params.mode)); ++ } ++err_qos_init: ++ return ret; ++} ++ ++static int msm_bus_noc_set_bw(struct msm_bus_node_device_type *dev, ++ void __iomem *qos_base, ++ uint32_t qos_off, uint32_t qos_delta, ++ uint32_t qos_freq) ++{ ++ int ret = 0; ++ uint64_t bw = 0; ++ int i; ++ struct msm_bus_node_info_type *info = dev->node_info; ++ ++ if (info && info->num_qports && ++ ((info->qos_params.mode == NOC_QOS_MODE_REGULATOR) || ++ (info->qos_params.mode == ++ NOC_QOS_MODE_LIMITER))) { ++ struct msm_bus_noc_qos_bw qos_bw; ++ ++ bw = msm_bus_div64(info->num_qports, ++ dev->node_ab.ab[DUAL_CTX]); ++ ++ for (i = 0; i < info->num_qports; i++) { ++ if (!info->qport) { ++ MSM_BUS_DBG("No qos ports to update!\n"); ++ break; ++ } ++ ++ qos_bw.bw = bw; ++ qos_bw.ws = info->qos_params.ws; ++ msm_bus_noc_set_qos_bw(qos_base, qos_off, qos_freq, ++ info->qport[i], qos_delta, ++ info->qos_params.mode, &qos_bw); ++ MSM_BUS_DBG("NOC: QoS: Update mas_bw: ws: %u\n", ++ qos_bw.ws); ++ } ++ } ++ return ret; ++} ++int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata, ++ struct msm_bus_hw_algorithm *hw_algo) ++{ ++ /* Set interleaving to true by default */ ++ pdata->il_flag = true; ++ hw_algo->allocate_commit_data = msm_bus_noc_allocate_commit_data; ++ hw_algo->allocate_hw_data = msm_bus_noc_allocate_noc_data; ++ hw_algo->node_init = msm_bus_noc_node_init; ++ hw_algo->free_commit_data = free_commit_data; ++ hw_algo->update_bw = msm_bus_noc_update_bw; ++ hw_algo->commit = msm_bus_noc_commit; ++ hw_algo->port_halt = msm_bus_noc_port_halt; ++ hw_algo->port_unhalt = msm_bus_noc_port_unhalt; ++ hw_algo->update_bw_reg = msm_bus_noc_update_bw_reg; ++ hw_algo->config_master = NULL; ++ hw_algo->config_limiter = NULL; ++ ++ return 0; ++} ++ ++int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev) ++{ ++ if (!bus_dev) ++ return -ENODEV; ++ else { ++ bus_dev->fabdev->noc_ops.qos_init = msm_bus_noc_qos_init; ++ bus_dev->fabdev->noc_ops.set_bw = msm_bus_noc_set_bw; ++ bus_dev->fabdev->noc_ops.limit_mport = NULL; ++ bus_dev->fabdev->noc_ops.update_bw_reg = ++ msm_bus_noc_update_bw_reg; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(msm_bus_noc_set_ops); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_noc.h +@@ -0,0 +1,76 @@ ++/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_BUS_BIMC_H ++#define _ARCH_ARM_MACH_MSM_BUS_BIMC_H ++ ++enum msm_bus_noc_qos_mode_type { ++ NOC_QOS_MODE_FIXED = 0, ++ NOC_QOS_MODE_LIMITER, ++ NOC_QOS_MODE_BYPASS, ++ NOC_QOS_MODE_REGULATOR, ++ NOC_QOS_MODE_MAX, ++}; ++ ++enum msm_bus_noc_qos_mode_perm { ++ NOC_QOS_PERM_MODE_FIXED = (1 << NOC_QOS_MODE_FIXED), ++ NOC_QOS_PERM_MODE_LIMITER = (1 << NOC_QOS_MODE_LIMITER), ++ NOC_QOS_PERM_MODE_BYPASS = (1 << NOC_QOS_MODE_BYPASS), ++ NOC_QOS_PERM_MODE_REGULATOR = (1 << NOC_QOS_MODE_REGULATOR), ++}; ++ ++#define NOC_QOS_MODES_ALL_PERM (NOC_QOS_PERM_MODE_FIXED | \ ++ NOC_QOS_PERM_MODE_LIMITER | NOC_QOS_PERM_MODE_BYPASS | \ ++ NOC_QOS_PERM_MODE_REGULATOR) ++ ++struct msm_bus_noc_commit { ++ struct msm_bus_node_hw_info *mas; ++ struct msm_bus_node_hw_info *slv; ++}; ++ ++struct msm_bus_noc_info { ++ void __iomem *base; ++ uint32_t base_addr; ++ uint32_t nmasters; ++ uint32_t nqos_masters; ++ uint32_t nslaves; ++ uint32_t qos_freq; /* QOS Clock in KHz */ ++ uint32_t qos_baseoffset; ++ uint32_t qos_delta; ++ uint32_t *mas_modes; ++ struct msm_bus_noc_commit cdata[NUM_CTX]; ++}; ++ ++struct msm_bus_noc_qos_priority { ++ uint32_t high_prio; ++ uint32_t low_prio; ++ uint32_t read_prio; ++ uint32_t write_prio; ++ uint32_t p1; ++ uint32_t p0; ++}; ++ ++struct msm_bus_noc_qos_bw { ++ uint64_t bw; /* Bandwidth in bytes per second */ ++ uint32_t ws; /* Window size in nano seconds */ ++}; ++ ++void msm_bus_noc_init(struct msm_bus_noc_info *ninfo); ++uint8_t msm_bus_noc_get_qos_mode(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, uint32_t mode, uint32_t perm_mode); ++void msm_bus_noc_get_qos_priority(void __iomem *base, uint32_t qos_off, ++ uint32_t mport, uint32_t qos_delta, ++ struct msm_bus_noc_qos_priority *qprio); ++void msm_bus_noc_get_qos_bw(void __iomem *base, uint32_t qos_off, ++ uint32_t qos_freq, uint32_t mport, uint32_t qos_delta, ++ uint8_t perm_mode, struct msm_bus_noc_qos_bw *qbw); ++#endif /*_ARCH_ARM_MACH_MSM_BUS_NOC_H */ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_of.c +@@ -0,0 +1,705 @@ ++/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus.h" ++#include "msm-bus-board.h" ++#include "msm_bus_core.h" ++ ++static const char * const hw_sel_name[] = {"RPM", "NoC", "BIMC", NULL}; ++static const char * const mode_sel_name[] = {"Fixed", "Limiter", "Bypass", ++ "Regulator", NULL}; ++ ++static int get_num(const char *const str[], const char *name) ++{ ++ int i = 0; ++ ++ do { ++ if (!strcmp(name, str[i])) ++ return i; ++ ++ i++; ++ } while (str[i] != NULL); ++ ++ pr_err("Error: string %s not found\n", name); ++ return -EINVAL; ++} ++ ++#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) ++static struct msm_bus_scale_pdata *get_pdata(struct platform_device *pdev, ++ struct device_node *of_node) ++{ ++ struct msm_bus_scale_pdata *pdata = NULL; ++ struct msm_bus_paths *usecase = NULL; ++ int i = 0, j, ret, num_usecases = 0, num_paths, len; ++ const uint32_t *vec_arr = NULL; ++ bool mem_err = false; ++ ++ if (!pdev) { ++ pr_err("Error: Null Platform device\n"); ++ return NULL; ++ } ++ ++ pdata = devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_scale_pdata), ++ GFP_KERNEL); ++ if (!pdata) { ++ pr_err("Error: Memory allocation for pdata failed\n"); ++ mem_err = true; ++ goto err; ++ } ++ ++ ret = of_property_read_string(of_node, "qcom,msm-bus,name", ++ &pdata->name); ++ if (ret) { ++ pr_err("Error: Client name not found\n"); ++ goto err; ++ } ++ ++ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases", ++ &num_usecases); ++ if (ret) { ++ pr_err("Error: num-usecases not found\n"); ++ goto err; ++ } ++ ++ pdata->num_usecases = num_usecases; ++ ++ if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) ++ pdata->active_only = 1; ++ else { ++ pr_debug("active_only flag absent.\n"); ++ pr_debug("Using dual context by default\n"); ++ } ++ ++ usecase = devm_kzalloc(&pdev->dev, (sizeof(struct msm_bus_paths) * ++ pdata->num_usecases), GFP_KERNEL); ++ if (!usecase) { ++ pr_err("Error: Memory allocation for paths failed\n"); ++ mem_err = true; ++ goto err; ++ } ++ ++ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths", ++ &num_paths); ++ if (ret) { ++ pr_err("Error: num_paths not found\n"); ++ goto err; ++ } ++ ++ vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len); ++ if (vec_arr == NULL) { ++ pr_err("Error: Vector array not found\n"); ++ goto err; ++ } ++ ++ if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) { ++ pr_err("Error: Length-error on getting vectors\n"); ++ goto err; ++ } ++ ++ for (i = 0; i < num_usecases; i++) { ++ usecase[i].num_paths = num_paths; ++ usecase[i].vectors = devm_kzalloc(&pdev->dev, num_paths * ++ sizeof(struct msm_bus_vectors), GFP_KERNEL); ++ if (!usecase[i].vectors) { ++ mem_err = true; ++ pr_err("Error: Mem alloc failure in vectors\n"); ++ goto err; ++ } ++ ++ for (j = 0; j < num_paths; j++) { ++ int index = ((i * num_paths) + j) * 4; ++ usecase[i].vectors[j].src = be32_to_cpu(vec_arr[index]); ++ usecase[i].vectors[j].dst = ++ be32_to_cpu(vec_arr[index + 1]); ++ usecase[i].vectors[j].ab = (uint64_t) ++ KBTOB(be32_to_cpu(vec_arr[index + 2])); ++ usecase[i].vectors[j].ib = (uint64_t) ++ KBTOB(be32_to_cpu(vec_arr[index + 3])); ++ } ++ } ++ ++ pdata->usecase = usecase; ++ return pdata; ++err: ++ if (mem_err) { ++ for (; i > 0; i--) ++ kfree(usecase[i-1].vectors); ++ ++ kfree(usecase); ++ kfree(pdata); ++ } ++ ++ return NULL; ++} ++ ++/** ++ * msm_bus_cl_get_pdata() - Generate bus client data from device tree ++ * provided by clients. ++ * ++ * of_node: Device tree node to extract information from ++ * ++ * The function returns a valid pointer to the allocated bus-scale-pdata ++ * if the vectors were correctly read from the client's device node. ++ * Any error in reading or parsing the device node will return NULL ++ * to the caller. ++ */ ++struct msm_bus_scale_pdata *msm_bus_cl_get_pdata(struct platform_device *pdev) ++{ ++ struct device_node *of_node; ++ struct msm_bus_scale_pdata *pdata = NULL; ++ ++ if (!pdev) { ++ pr_err("Error: Null Platform device\n"); ++ return NULL; ++ } ++ ++ of_node = pdev->dev.of_node; ++ pdata = get_pdata(pdev, of_node); ++ if (!pdata) { ++ pr_err("client has to provide missing entry for successful registration\n"); ++ return NULL; ++ } ++ ++ return pdata; ++} ++EXPORT_SYMBOL(msm_bus_cl_get_pdata); ++ ++/** ++ * msm_bus_cl_pdata_from_node() - Generate bus client data from device tree ++ * node provided by clients. This function should be used when a client ++ * driver needs to register multiple bus-clients from a single device-tree ++ * node associated with the platform-device. ++ * ++ * of_node: The subnode containing information about the bus scaling ++ * data ++ * ++ * pdev: Platform device associated with the device-tree node ++ * ++ * The function returns a valid pointer to the allocated bus-scale-pdata ++ * if the vectors were correctly read from the client's device node. ++ * Any error in reading or parsing the device node will return NULL ++ * to the caller. ++ */ ++struct msm_bus_scale_pdata *msm_bus_pdata_from_node( ++ struct platform_device *pdev, struct device_node *of_node) ++{ ++ struct msm_bus_scale_pdata *pdata = NULL; ++ ++ if (!pdev) { ++ pr_err("Error: Null Platform device\n"); ++ return NULL; ++ } ++ ++ if (!of_node) { ++ pr_err("Error: Null of_node passed to bus driver\n"); ++ return NULL; ++ } ++ ++ pdata = get_pdata(pdev, of_node); ++ if (!pdata) { ++ pr_err("client has to provide missing entry for successful registration\n"); ++ return NULL; ++ } ++ ++ return pdata; ++} ++EXPORT_SYMBOL(msm_bus_pdata_from_node); ++ ++/** ++ * msm_bus_cl_clear_pdata() - Clear pdata allocated from device-tree ++ * of_node: Device tree node to extract information from ++ */ ++void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata) ++{ ++ int i; ++ ++ for (i = 0; i < pdata->num_usecases; i++) ++ kfree(pdata->usecase[i].vectors); ++ ++ kfree(pdata->usecase); ++ kfree(pdata); ++} ++EXPORT_SYMBOL(msm_bus_cl_clear_pdata); ++#endif ++ ++static int *get_arr(struct platform_device *pdev, ++ const struct device_node *node, const char *prop, ++ int *nports) ++{ ++ int size = 0, ret; ++ int *arr = NULL; ++ ++ if (of_get_property(node, prop, &size)) { ++ *nports = size / sizeof(int); ++ } else { ++ pr_debug("Property %s not available\n", prop); ++ *nports = 0; ++ return NULL; ++ } ++ ++ if (!size) { ++ *nports = 0; ++ return NULL; ++ } ++ ++ arr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(arr)) { ++ pr_err("Error: Failed to alloc mem for %s\n", prop); ++ return NULL; ++ } ++ ++ ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports); ++ if (ret) { ++ pr_err("Error in reading property: %s\n", prop); ++ goto err; ++ } ++ ++ return arr; ++err: ++ devm_kfree(&pdev->dev, arr); ++ return NULL; ++} ++ ++static u64 *get_th_params(struct platform_device *pdev, ++ const struct device_node *node, const char *prop, ++ int *nports) ++{ ++ int size = 0, ret; ++ u64 *ret_arr = NULL; ++ int *arr = NULL; ++ int i; ++ ++ if (of_get_property(node, prop, &size)) { ++ *nports = size / sizeof(int); ++ } else { ++ pr_debug("Property %s not available\n", prop); ++ *nports = 0; ++ return NULL; ++ } ++ ++ if (!size) { ++ *nports = 0; ++ return NULL; ++ } ++ ++ ret_arr = devm_kzalloc(&pdev->dev, (*nports * sizeof(u64)), ++ GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(ret_arr)) { ++ pr_err("Error: Failed to alloc mem for ret arr %s\n", prop); ++ return NULL; ++ } ++ ++ arr = kzalloc(size, GFP_KERNEL); ++ if ((ZERO_OR_NULL_PTR(arr))) { ++ pr_err("Error: Failed to alloc temp mem for %s\n", prop); ++ return NULL; ++ } ++ ++ ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports); ++ if (ret) { ++ pr_err("Error in reading property: %s\n", prop); ++ goto err; ++ } ++ ++ for (i = 0; i < *nports; i++) ++ ret_arr[i] = (uint64_t)KBTOB(arr[i]); ++ ++ MSM_BUS_DBG("%s: num entries %d prop %s", __func__, *nports, prop); ++ ++ for (i = 0; i < *nports; i++) ++ MSM_BUS_DBG("Th %d val %llu", i, ret_arr[i]); ++ ++ kfree(arr); ++ return ret_arr; ++err: ++ kfree(arr); ++ devm_kfree(&pdev->dev, ret_arr); ++ return NULL; ++} ++ ++static struct msm_bus_node_info *get_nodes(struct device_node *of_node, ++ struct platform_device *pdev, ++ struct msm_bus_fabric_registration *pdata) ++{ ++ struct msm_bus_node_info *info; ++ struct device_node *child_node = NULL; ++ int i = 0, ret; ++ int num_bw = 0; ++ u32 temp; ++ ++ for_each_child_of_node(of_node, child_node) { ++ i++; ++ } ++ ++ pdata->len = i; ++ info = (struct msm_bus_node_info *) ++ devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_node_info) * ++ pdata->len, GFP_KERNEL); ++ if (ZERO_OR_NULL_PTR(info)) { ++ pr_err("Failed to alloc memory for nodes: %d\n", pdata->len); ++ goto err; ++ } ++ ++ i = 0; ++ child_node = NULL; ++ for_each_child_of_node(of_node, child_node) { ++ const char *sel_str; ++ ++ ret = of_property_read_string(child_node, "label", ++ &info[i].name); ++ if (ret) ++ pr_err("Error reading node label\n"); ++ ++ ret = of_property_read_u32(child_node, "cell-id", &info[i].id); ++ if (ret) { ++ pr_err("Error reading node id\n"); ++ goto err; ++ } ++ ++ if (of_property_read_bool(child_node, "qcom,gateway")) ++ info[i].gateway = 1; ++ ++ of_property_read_u32(child_node, "qcom,mas-hw-id", ++ &info[i].mas_hw_id); ++ ++ of_property_read_u32(child_node, "qcom,slv-hw-id", ++ &info[i].slv_hw_id); ++ info[i].masterp = get_arr(pdev, child_node, ++ "qcom,masterp", &info[i].num_mports); ++ /* No need to store number of qports */ ++ info[i].qport = get_arr(pdev, child_node, ++ "qcom,qport", &ret); ++ pdata->nmasters += info[i].num_mports; ++ ++ ++ info[i].slavep = get_arr(pdev, child_node, ++ "qcom,slavep", &info[i].num_sports); ++ pdata->nslaves += info[i].num_sports; ++ ++ ++ info[i].tier = get_arr(pdev, child_node, ++ "qcom,tier", &info[i].num_tiers); ++ ++ if (of_property_read_bool(child_node, "qcom,ahb")) ++ info[i].ahb = 1; ++ ++ ret = of_property_read_string(child_node, "qcom,hw-sel", ++ &sel_str); ++ if (ret) ++ info[i].hw_sel = 0; ++ else { ++ ret = get_num(hw_sel_name, sel_str); ++ if (ret < 0) { ++ pr_err("Invalid hw-sel\n"); ++ goto err; ++ } ++ ++ info[i].hw_sel = ret; ++ } ++ ++ of_property_read_u32(child_node, "qcom,buswidth", ++ &info[i].buswidth); ++ of_property_read_u32(child_node, "qcom,ws", &info[i].ws); ++ ++ info[i].dual_conf = ++ of_property_read_bool(child_node, "qcom,dual-conf"); ++ ++ ++ info[i].th = get_th_params(pdev, child_node, "qcom,thresh", ++ &info[i].num_thresh); ++ ++ info[i].bimc_bw = get_th_params(pdev, child_node, ++ "qcom,bimc,bw", &num_bw); ++ ++ if (num_bw != info[i].num_thresh) { ++ pr_err("%s:num_bw %d must equal num_thresh %d", ++ __func__, num_bw, info[i].num_thresh); ++ pr_err("%s:Err setting up dual conf for %s", ++ __func__, info[i].name); ++ goto err; ++ } ++ ++ of_property_read_u32(child_node, "qcom,bimc,gp", ++ &info[i].bimc_gp); ++ of_property_read_u32(child_node, "qcom,bimc,thmp", ++ &info[i].bimc_thmp); ++ ++ ret = of_property_read_string(child_node, "qcom,mode-thresh", ++ &sel_str); ++ if (ret) ++ info[i].mode_thresh = 0; ++ else { ++ ret = get_num(mode_sel_name, sel_str); ++ if (ret < 0) { ++ pr_err("Unknown mode :%s\n", sel_str); ++ goto err; ++ } ++ ++ info[i].mode_thresh = ret; ++ MSM_BUS_DBG("AXI: THreshold mode set: %d\n", ++ info[i].mode_thresh); ++ } ++ ++ ret = of_property_read_string(child_node, "qcom,mode", ++ &sel_str); ++ ++ if (ret) ++ info[i].mode = 0; ++ else { ++ ret = get_num(mode_sel_name, sel_str); ++ if (ret < 0) { ++ pr_err("Unknown mode :%s\n", sel_str); ++ goto err; ++ } ++ ++ info[i].mode = ret; ++ } ++ ++ info[i].nr_lim = ++ of_property_read_bool(child_node, "qcom,nr-lim"); ++ ++ ret = of_property_read_u32(child_node, "qcom,ff", ++ &info[i].ff); ++ if (ret) { ++ pr_debug("fudge factor not present %d", info[i].id); ++ info[i].ff = 0; ++ } ++ ++ ret = of_property_read_u32(child_node, "qcom,floor-bw", ++ &temp); ++ if (ret) { ++ pr_debug("fabdev floor bw not present %d", info[i].id); ++ info[i].floor_bw = 0; ++ } else { ++ info[i].floor_bw = KBTOB(temp); ++ } ++ ++ info[i].rt_mas = ++ of_property_read_bool(child_node, "qcom,rt-mas"); ++ ++ ret = of_property_read_string(child_node, "qcom,perm-mode", ++ &sel_str); ++ if (ret) ++ info[i].perm_mode = 0; ++ else { ++ ret = get_num(mode_sel_name, sel_str); ++ if (ret < 0) ++ goto err; ++ ++ info[i].perm_mode = 1 << ret; ++ } ++ ++ of_property_read_u32(child_node, "qcom,prio-lvl", ++ &info[i].prio_lvl); ++ of_property_read_u32(child_node, "qcom,prio-rd", ++ &info[i].prio_rd); ++ of_property_read_u32(child_node, "qcom,prio-wr", ++ &info[i].prio_wr); ++ of_property_read_u32(child_node, "qcom,prio0", &info[i].prio0); ++ of_property_read_u32(child_node, "qcom,prio1", &info[i].prio1); ++ ret = of_property_read_string(child_node, "qcom,slaveclk-dual", ++ &info[i].slaveclk[DUAL_CTX]); ++ if (!ret) ++ pr_debug("Got slaveclk_dual: %s\n", ++ info[i].slaveclk[DUAL_CTX]); ++ else ++ info[i].slaveclk[DUAL_CTX] = NULL; ++ ++ ret = of_property_read_string(child_node, ++ "qcom,slaveclk-active", &info[i].slaveclk[ACTIVE_CTX]); ++ if (!ret) ++ pr_debug("Got slaveclk_active\n"); ++ else ++ info[i].slaveclk[ACTIVE_CTX] = NULL; ++ ++ ret = of_property_read_string(child_node, "qcom,memclk-dual", ++ &info[i].memclk[DUAL_CTX]); ++ if (!ret) ++ pr_debug("Got memclk_dual\n"); ++ else ++ info[i].memclk[DUAL_CTX] = NULL; ++ ++ ret = of_property_read_string(child_node, "qcom,memclk-active", ++ &info[i].memclk[ACTIVE_CTX]); ++ if (!ret) ++ pr_debug("Got memclk_active\n"); ++ else ++ info[i].memclk[ACTIVE_CTX] = NULL; ++ ++ ret = of_property_read_string(child_node, "qcom,iface-clk-node", ++ &info[i].iface_clk_node); ++ if (!ret) ++ pr_debug("Got iface_clk_node\n"); ++ else ++ info[i].iface_clk_node = NULL; ++ ++ pr_debug("Node name: %s\n", info[i].name); ++ of_node_put(child_node); ++ i++; ++ } ++ ++ pr_debug("Bus %d added: %d masters\n", pdata->id, pdata->nmasters); ++ pr_debug("Bus %d added: %d slaves\n", pdata->id, pdata->nslaves); ++ return info; ++err: ++ return NULL; ++} ++ ++void msm_bus_of_get_nfab(struct platform_device *pdev, ++ struct msm_bus_fabric_registration *pdata) ++{ ++ struct device_node *of_node; ++ int ret, nfab = 0; ++ ++ if (!pdev) { ++ pr_err("Error: Null platform device\n"); ++ return; ++ } ++ ++ of_node = pdev->dev.of_node; ++ ret = of_property_read_u32(of_node, "qcom,nfab", ++ &nfab); ++ if (!ret) ++ pr_debug("Fab_of: Read number of buses: %u\n", nfab); ++ ++ msm_bus_board_set_nfab(pdata, nfab); ++} ++ ++struct msm_bus_fabric_registration ++ *msm_bus_of_get_fab_data(struct platform_device *pdev) ++{ ++ struct device_node *of_node; ++ struct msm_bus_fabric_registration *pdata; ++ bool mem_err = false; ++ int ret = 0; ++ const char *sel_str; ++ u32 temp; ++ ++ if (!pdev) { ++ pr_err("Error: Null platform device\n"); ++ return NULL; ++ } ++ ++ of_node = pdev->dev.of_node; ++ pdata = devm_kzalloc(&pdev->dev, ++ sizeof(struct msm_bus_fabric_registration), GFP_KERNEL); ++ if (!pdata) { ++ pr_err("Error: Memory allocation for pdata failed\n"); ++ mem_err = true; ++ goto err; ++ } ++ ++ ret = of_property_read_string(of_node, "label", &pdata->name); ++ if (ret) { ++ pr_err("Error: label not found\n"); ++ goto err; ++ } ++ pr_debug("Fab_of: Read name: %s\n", pdata->name); ++ ++ ret = of_property_read_u32(of_node, "cell-id", ++ &pdata->id); ++ if (ret) { ++ pr_err("Error: num-usecases not found\n"); ++ goto err; ++ } ++ pr_debug("Fab_of: Read id: %u\n", pdata->id); ++ ++ if (of_property_read_bool(of_node, "qcom,ahb")) ++ pdata->ahb = 1; ++ ++ ret = of_property_read_string(of_node, "qcom,fabclk-dual", ++ &pdata->fabclk[DUAL_CTX]); ++ if (ret) { ++ pr_debug("fabclk_dual not available\n"); ++ pdata->fabclk[DUAL_CTX] = NULL; ++ } else ++ pr_debug("Fab_of: Read clk dual ctx: %s\n", ++ pdata->fabclk[DUAL_CTX]); ++ ret = of_property_read_string(of_node, "qcom,fabclk-active", ++ &pdata->fabclk[ACTIVE_CTX]); ++ if (ret) { ++ pr_debug("Error: fabclk_active not available\n"); ++ pdata->fabclk[ACTIVE_CTX] = NULL; ++ } else ++ pr_debug("Fab_of: Read clk act ctx: %s\n", ++ pdata->fabclk[ACTIVE_CTX]); ++ ++ ret = of_property_read_u32(of_node, "qcom,ntieredslaves", ++ &pdata->ntieredslaves); ++ if (ret) { ++ pr_err("Error: ntieredslaves not found\n"); ++ goto err; ++ } ++ ++ ret = of_property_read_u32(of_node, "qcom,qos-freq", &pdata->qos_freq); ++ if (ret) ++ pr_debug("qos_freq not available\n"); ++ ++ ret = of_property_read_string(of_node, "qcom,hw-sel", &sel_str); ++ if (ret) { ++ pr_err("Error: hw_sel not found\n"); ++ goto err; ++ } else { ++ ret = get_num(hw_sel_name, sel_str); ++ if (ret < 0) ++ goto err; ++ ++ pdata->hw_sel = ret; ++ } ++ ++ if (of_property_read_bool(of_node, "qcom,virt")) ++ pdata->virt = true; ++ ++ ret = of_property_read_u32(of_node, "qcom,qos-baseoffset", ++ &pdata->qos_baseoffset); ++ if (ret) ++ pr_debug("%s:qos_baseoffset not available\n", __func__); ++ ++ ret = of_property_read_u32(of_node, "qcom,qos-delta", ++ &pdata->qos_delta); ++ if (ret) ++ pr_debug("%s:qos_delta not available\n", __func__); ++ ++ if (of_property_read_bool(of_node, "qcom,rpm-en")) ++ pdata->rpm_enabled = 1; ++ ++ ret = of_property_read_u32(of_node, "qcom,nr-lim-thresh", ++ &temp); ++ ++ if (ret) { ++ pr_err("nr-lim threshold not specified"); ++ pdata->nr_lim_thresh = 0; ++ } else { ++ pdata->nr_lim_thresh = KBTOB(temp); ++ } ++ ++ ret = of_property_read_u32(of_node, "qcom,eff-fact", ++ &pdata->eff_fact); ++ if (ret) { ++ pr_err("Fab eff-factor not present"); ++ pdata->eff_fact = 0; ++ } ++ ++ pdata->info = get_nodes(of_node, pdev, pdata); ++ return pdata; ++err: ++ return NULL; ++} ++EXPORT_SYMBOL(msm_bus_of_get_fab_data); +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_of_adhoc.c +@@ -0,0 +1,641 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msm-bus.h" ++#include "msm-bus-board.h" ++#include "msm_bus_rules.h" ++#include "msm_bus_core.h" ++#include "msm_bus_adhoc.h" ++ ++#define DEFAULT_QOS_FREQ 19200 ++#define DEFAULT_UTIL_FACT 100 ++#define DEFAULT_VRAIL_COMP 100 ++ ++static int get_qos_mode(struct platform_device *pdev, ++ struct device_node *node, const char *qos_mode) ++{ ++ const char *qos_names[] = {"fixed", "limiter", "bypass", "regulator"}; ++ int i = 0; ++ int ret = -1; ++ ++ if (!qos_mode) ++ goto exit_get_qos_mode; ++ ++ for (i = 0; i < ARRAY_SIZE(qos_names); i++) { ++ if (!strcmp(qos_mode, qos_names[i])) ++ break; ++ } ++ if (i == ARRAY_SIZE(qos_names)) ++ dev_err(&pdev->dev, "Cannot match mode qos %s using Bypass", ++ qos_mode); ++ else ++ ret = i; ++ ++exit_get_qos_mode: ++ return ret; ++} ++ ++static int *get_arr(struct platform_device *pdev, ++ struct device_node *node, const char *prop, ++ int *nports) ++{ ++ int size = 0, ret; ++ int *arr = NULL; ++ ++ if (of_get_property(node, prop, &size)) { ++ *nports = size / sizeof(int); ++ } else { ++ dev_dbg(&pdev->dev, "Property %s not available\n", prop); ++ *nports = 0; ++ return NULL; ++ } ++ ++ arr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); ++ if ((size > 0) && ZERO_OR_NULL_PTR(arr)) { ++ dev_err(&pdev->dev, "Error: Failed to alloc mem for %s\n", ++ prop); ++ return NULL; ++ } ++ ++ ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports); ++ if (ret) { ++ dev_err(&pdev->dev, "Error in reading property: %s\n", prop); ++ goto arr_err; ++ } ++ ++ return arr; ++arr_err: ++ devm_kfree(&pdev->dev, arr); ++ return NULL; ++} ++ ++static struct msm_bus_fab_device_type *get_fab_device_info( ++ struct device_node *dev_node, ++ struct platform_device *pdev) ++{ ++ struct msm_bus_fab_device_type *fab_dev; ++ unsigned int ret; ++ struct resource *res; ++ const char *base_name; ++ ++ fab_dev = devm_kzalloc(&pdev->dev, ++ sizeof(struct msm_bus_fab_device_type), ++ GFP_KERNEL); ++ if (!fab_dev) { ++ dev_err(&pdev->dev, ++ "Error: Unable to allocate memory for fab_dev\n"); ++ return NULL; ++ } ++ ++ ret = of_property_read_string(dev_node, "qcom,base-name", &base_name); ++ if (ret) { ++ dev_err(&pdev->dev, "Error: Unable to get base address name\n"); ++ goto fab_dev_err; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, base_name); ++ if (!res) { ++ dev_err(&pdev->dev, "Error getting qos base addr %s\n", ++ base_name); ++ goto fab_dev_err; ++ } ++ fab_dev->pqos_base = res->start; ++ fab_dev->qos_range = resource_size(res); ++ fab_dev->bypass_qos_prg = of_property_read_bool(dev_node, ++ "qcom,bypass-qos-prg"); ++ ++ ret = of_property_read_u32(dev_node, "qcom,base-offset", ++ &fab_dev->base_offset); ++ if (ret) ++ dev_dbg(&pdev->dev, "Bus base offset is missing\n"); ++ ++ ret = of_property_read_u32(dev_node, "qcom,qos-off", ++ &fab_dev->qos_off); ++ if (ret) ++ dev_dbg(&pdev->dev, "Bus qos off is missing\n"); ++ ++ ++ ret = of_property_read_u32(dev_node, "qcom,bus-type", ++ &fab_dev->bus_type); ++ if (ret) { ++ dev_warn(&pdev->dev, "Bus type is missing\n"); ++ goto fab_dev_err; ++ } ++ ++ ret = of_property_read_u32(dev_node, "qcom,qos-freq", ++ &fab_dev->qos_freq); ++ if (ret) { ++ dev_dbg(&pdev->dev, "Bus qos freq is missing\n"); ++ fab_dev->qos_freq = DEFAULT_QOS_FREQ; ++ } ++ ++ ret = of_property_read_u32(dev_node, "qcom,util-fact", ++ &fab_dev->util_fact); ++ if (ret) { ++ dev_info(&pdev->dev, "Util-fact is missing, default to %d\n", ++ DEFAULT_UTIL_FACT); ++ fab_dev->util_fact = DEFAULT_UTIL_FACT; ++ } ++ ++ ret = of_property_read_u32(dev_node, "qcom,vrail-comp", ++ &fab_dev->vrail_comp); ++ if (ret) { ++ dev_info(&pdev->dev, "Vrail-comp is missing, default to %d\n", ++ DEFAULT_VRAIL_COMP); ++ fab_dev->vrail_comp = DEFAULT_VRAIL_COMP; ++ } ++ ++ return fab_dev; ++ ++fab_dev_err: ++ devm_kfree(&pdev->dev, fab_dev); ++ fab_dev = 0; ++ return NULL; ++} ++ ++static void get_qos_params( ++ struct device_node * const dev_node, ++ struct platform_device * const pdev, ++ struct msm_bus_node_info_type *node_info) ++{ ++ const char *qos_mode = NULL; ++ unsigned int ret; ++ unsigned int temp; ++ ++ ret = of_property_read_string(dev_node, "qcom,qos-mode", &qos_mode); ++ ++ if (ret) ++ node_info->qos_params.mode = -1; ++ else ++ node_info->qos_params.mode = get_qos_mode(pdev, dev_node, ++ qos_mode); ++ ++ of_property_read_u32(dev_node, "qcom,prio-lvl", ++ &node_info->qos_params.prio_lvl); ++ ++ of_property_read_u32(dev_node, "qcom,prio1", ++ &node_info->qos_params.prio1); ++ ++ of_property_read_u32(dev_node, "qcom,prio0", ++ &node_info->qos_params.prio0); ++ ++ of_property_read_u32(dev_node, "qcom,prio-rd", ++ &node_info->qos_params.prio_rd); ++ ++ of_property_read_u32(dev_node, "qcom,prio-wr", ++ &node_info->qos_params.prio_wr); ++ ++ of_property_read_u32(dev_node, "qcom,gp", ++ &node_info->qos_params.gp); ++ ++ of_property_read_u32(dev_node, "qcom,thmp", ++ &node_info->qos_params.thmp); ++ ++ of_property_read_u32(dev_node, "qcom,ws", ++ &node_info->qos_params.ws); ++ ++ ret = of_property_read_u32(dev_node, "qcom,bw_buffer", &temp); ++ ++ if (ret) ++ node_info->qos_params.bw_buffer = 0; ++ else ++ node_info->qos_params.bw_buffer = KBTOB(temp); ++ ++} ++ ++ ++static struct msm_bus_node_info_type *get_node_info_data( ++ struct device_node * const dev_node, ++ struct platform_device * const pdev) ++{ ++ struct msm_bus_node_info_type *node_info; ++ unsigned int ret; ++ int size; ++ int i; ++ struct device_node *con_node; ++ struct device_node *bus_dev; ++ ++ node_info = devm_kzalloc(&pdev->dev, ++ sizeof(struct msm_bus_node_info_type), ++ GFP_KERNEL); ++ if (!node_info) { ++ dev_err(&pdev->dev, ++ "Error: Unable to allocate memory for node_info\n"); ++ return NULL; ++ } ++ ++ ret = of_property_read_u32(dev_node, "cell-id", &node_info->id); ++ if (ret) { ++ dev_warn(&pdev->dev, "Bus node is missing cell-id\n"); ++ goto node_info_err; ++ } ++ ret = of_property_read_string(dev_node, "label", &node_info->name); ++ if (ret) { ++ dev_warn(&pdev->dev, "Bus node is missing name\n"); ++ goto node_info_err; ++ } ++ node_info->qport = get_arr(pdev, dev_node, "qcom,qport", ++ &node_info->num_qports); ++ ++ if (of_get_property(dev_node, "qcom,connections", &size)) { ++ node_info->num_connections = size / sizeof(int); ++ node_info->connections = devm_kzalloc(&pdev->dev, size, ++ GFP_KERNEL); ++ } else { ++ node_info->num_connections = 0; ++ node_info->connections = 0; ++ } ++ ++ for (i = 0; i < node_info->num_connections; i++) { ++ con_node = of_parse_phandle(dev_node, "qcom,connections", i); ++ if (IS_ERR_OR_NULL(con_node)) ++ goto node_info_err; ++ ++ if (of_property_read_u32(con_node, "cell-id", ++ &node_info->connections[i])) ++ goto node_info_err; ++ of_node_put(con_node); ++ } ++ ++ if (of_get_property(dev_node, "qcom,blacklist", &size)) { ++ node_info->num_blist = size/sizeof(u32); ++ node_info->black_listed_connections = devm_kzalloc(&pdev->dev, ++ size, GFP_KERNEL); ++ } else { ++ node_info->num_blist = 0; ++ node_info->black_listed_connections = 0; ++ } ++ ++ for (i = 0; i < node_info->num_blist; i++) { ++ con_node = of_parse_phandle(dev_node, "qcom,blacklist", i); ++ if (IS_ERR_OR_NULL(con_node)) ++ goto node_info_err; ++ ++ if (of_property_read_u32(con_node, "cell-id", ++ &node_info->black_listed_connections[i])) ++ goto node_info_err; ++ of_node_put(con_node); ++ } ++ ++ bus_dev = of_parse_phandle(dev_node, "qcom,bus-dev", 0); ++ if (!IS_ERR_OR_NULL(bus_dev)) { ++ if (of_property_read_u32(bus_dev, "cell-id", ++ &node_info->bus_device_id)) { ++ dev_err(&pdev->dev, "Can't find bus device. Node %d", ++ node_info->id); ++ goto node_info_err; ++ } ++ ++ of_node_put(bus_dev); ++ } else ++ dev_dbg(&pdev->dev, "Can't find bdev phandle for %d", ++ node_info->id); ++ ++ node_info->is_fab_dev = of_property_read_bool(dev_node, "qcom,fab-dev"); ++ node_info->virt_dev = of_property_read_bool(dev_node, "qcom,virt-dev"); ++ ++ ret = of_property_read_u32(dev_node, "qcom,buswidth", ++ &node_info->buswidth); ++ if (ret) { ++ dev_dbg(&pdev->dev, "Using default 8 bytes %d", node_info->id); ++ node_info->buswidth = 8; ++ } ++ ++ ret = of_property_read_u32(dev_node, "qcom,mas-rpm-id", ++ &node_info->mas_rpm_id); ++ if (ret) { ++ dev_dbg(&pdev->dev, "mas rpm id is missing\n"); ++ node_info->mas_rpm_id = -1; ++ } ++ ++ ret = of_property_read_u32(dev_node, "qcom,slv-rpm-id", ++ &node_info->slv_rpm_id); ++ if (ret) { ++ dev_dbg(&pdev->dev, "slv rpm id is missing\n"); ++ node_info->slv_rpm_id = -1; ++ } ++ ret = of_property_read_u32(dev_node, "qcom,util-fact", ++ &node_info->util_fact); ++ if (ret) ++ node_info->util_fact = 0; ++ ret = of_property_read_u32(dev_node, "qcom,vrail-comp", ++ &node_info->vrail_comp); ++ if (ret) ++ node_info->vrail_comp = 0; ++ get_qos_params(dev_node, pdev, node_info); ++ ++ return node_info; ++ ++node_info_err: ++ devm_kfree(&pdev->dev, node_info); ++ node_info = 0; ++ return NULL; ++} ++ ++static unsigned int get_bus_node_device_data( ++ struct device_node * const dev_node, ++ struct platform_device * const pdev, ++ struct msm_bus_node_device_type * const node_device) ++{ ++ node_device->node_info = get_node_info_data(dev_node, pdev); ++ if (IS_ERR_OR_NULL(node_device->node_info)) { ++ dev_err(&pdev->dev, "Error: Node info missing\n"); ++ return -ENODATA; ++ } ++ node_device->ap_owned = of_property_read_bool(dev_node, ++ "qcom,ap-owned"); ++ ++ if (node_device->node_info->is_fab_dev) { ++ dev_dbg(&pdev->dev, "Dev %d\n", node_device->node_info->id); ++ ++ if (!node_device->node_info->virt_dev) { ++ node_device->fabdev = ++ get_fab_device_info(dev_node, pdev); ++ if (IS_ERR_OR_NULL(node_device->fabdev)) { ++ dev_err(&pdev->dev, ++ "Error: Fabric device info missing\n"); ++ devm_kfree(&pdev->dev, node_device->node_info); ++ return -ENODATA; ++ } ++ } ++ node_device->clk[DUAL_CTX].clk = of_clk_get_by_name(dev_node, ++ "bus_clk"); ++ ++ if (IS_ERR_OR_NULL(node_device->clk[DUAL_CTX].clk)) ++ dev_dbg(&pdev->dev, ++ "%s:Failed to get bus clk for bus%d ctx%d", ++ __func__, node_device->node_info->id, ++ DUAL_CTX); ++ ++ node_device->clk[ACTIVE_CTX].clk = of_clk_get_by_name(dev_node, ++ "bus_a_clk"); ++ if (IS_ERR_OR_NULL(node_device->clk[ACTIVE_CTX].clk)) ++ dev_err(&pdev->dev, ++ "Failed to get bus clk for bus%d ctx%d", ++ node_device->node_info->id, ACTIVE_CTX); ++ if (msmbus_coresight_init_adhoc(pdev, dev_node)) ++ dev_warn(&pdev->dev, ++ "Coresight support absent for bus: %d\n", ++ node_device->node_info->id); ++ } else { ++ node_device->qos_clk.clk = of_clk_get_by_name(dev_node, ++ "bus_qos_clk"); ++ ++ if (IS_ERR_OR_NULL(node_device->qos_clk.clk)) ++ dev_dbg(&pdev->dev, ++ "%s:Failed to get bus qos clk for mas%d", ++ __func__, node_device->node_info->id); ++ ++ node_device->clk[DUAL_CTX].clk = of_clk_get_by_name(dev_node, ++ "node_clk"); ++ ++ if (IS_ERR_OR_NULL(node_device->clk[DUAL_CTX].clk)) ++ dev_dbg(&pdev->dev, ++ "%s:Failed to get bus clk for bus%d ctx%d", ++ __func__, node_device->node_info->id, ++ DUAL_CTX); ++ ++ } ++ return 0; ++} ++ ++struct msm_bus_device_node_registration ++ *msm_bus_of_to_pdata(struct platform_device *pdev) ++{ ++ struct device_node *of_node, *child_node; ++ struct msm_bus_device_node_registration *pdata; ++ unsigned int i = 0, j; ++ unsigned int ret; ++ ++ if (!pdev) { ++ pr_err("Error: Null platform device\n"); ++ return NULL; ++ } ++ ++ of_node = pdev->dev.of_node; ++ ++ pdata = devm_kzalloc(&pdev->dev, ++ sizeof(struct msm_bus_device_node_registration), ++ GFP_KERNEL); ++ if (!pdata) { ++ dev_err(&pdev->dev, ++ "Error: Memory allocation for pdata failed\n"); ++ return NULL; ++ } ++ ++ pdata->num_devices = of_get_child_count(of_node); ++ ++ pdata->info = devm_kzalloc(&pdev->dev, ++ sizeof(struct msm_bus_node_device_type) * ++ pdata->num_devices, GFP_KERNEL); ++ ++ if (!pdata->info) { ++ dev_err(&pdev->dev, ++ "Error: Memory allocation for pdata->info failed\n"); ++ goto node_reg_err; ++ } ++ ++ ret = 0; ++ for_each_child_of_node(of_node, child_node) { ++ ret = get_bus_node_device_data(child_node, pdev, ++ &pdata->info[i]); ++ if (ret) { ++ dev_err(&pdev->dev, "Error: unable to initialize bus nodes\n"); ++ goto node_reg_err_1; ++ } ++ i++; ++ } ++ ++ dev_dbg(&pdev->dev, "bus topology:\n"); ++ for (i = 0; i < pdata->num_devices; i++) { ++ dev_dbg(&pdev->dev, "id %d\nnum_qports %d\nnum_connections %d", ++ pdata->info[i].node_info->id, ++ pdata->info[i].node_info->num_qports, ++ pdata->info[i].node_info->num_connections); ++ dev_dbg(&pdev->dev, "\nbus_device_id %d\n buswidth %d\n", ++ pdata->info[i].node_info->bus_device_id, ++ pdata->info[i].node_info->buswidth); ++ for (j = 0; j < pdata->info[i].node_info->num_connections; ++ j++) { ++ dev_dbg(&pdev->dev, "connection[%d]: %d\n", j, ++ pdata->info[i].node_info->connections[j]); ++ } ++ for (j = 0; j < pdata->info[i].node_info->num_blist; ++ j++) { ++ dev_dbg(&pdev->dev, "black_listed_node[%d]: %d\n", j, ++ pdata->info[i].node_info-> ++ black_listed_connections[j]); ++ } ++ if (pdata->info[i].fabdev) ++ dev_dbg(&pdev->dev, "base_addr %zu\nbus_type %d\n", ++ (size_t)pdata->info[i]. ++ fabdev->pqos_base, ++ pdata->info[i].fabdev->bus_type); ++ } ++ return pdata; ++ ++node_reg_err_1: ++ devm_kfree(&pdev->dev, pdata->info); ++node_reg_err: ++ devm_kfree(&pdev->dev, pdata); ++ pdata = NULL; ++ return NULL; ++} ++ ++static int msm_bus_of_get_ids(struct platform_device *pdev, ++ struct device_node *dev_node, int **dev_ids, ++ int *num_ids, char *prop_name) ++{ ++ int ret = 0; ++ int size, i; ++ struct device_node *rule_node; ++ int *ids = NULL; ++ ++ if (of_get_property(dev_node, prop_name, &size)) { ++ *num_ids = size / sizeof(int); ++ ids = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); ++ } else { ++ dev_err(&pdev->dev, "No rule nodes, skipping node"); ++ ret = -ENXIO; ++ goto exit_get_ids; ++ } ++ ++ *dev_ids = ids; ++ for (i = 0; i < *num_ids; i++) { ++ rule_node = of_parse_phandle(dev_node, prop_name, i); ++ if (IS_ERR_OR_NULL(rule_node)) { ++ dev_err(&pdev->dev, "Can't get rule node id"); ++ ret = -ENXIO; ++ goto err_get_ids; ++ } ++ ++ if (of_property_read_u32(rule_node, "cell-id", ++ &ids[i])) { ++ dev_err(&pdev->dev, "Can't get rule node id"); ++ ret = -ENXIO; ++ goto err_get_ids; ++ } ++ of_node_put(rule_node); ++ } ++exit_get_ids: ++ return ret; ++err_get_ids: ++ devm_kfree(&pdev->dev, ids); ++ of_node_put(rule_node); ++ ids = NULL; ++ return ret; ++} ++ ++int msm_bus_of_get_static_rules(struct platform_device *pdev, ++ struct bus_rule_type **static_rules) ++{ ++ int ret = 0; ++ struct device_node *of_node, *child_node; ++ int num_rules = 0; ++ int rule_idx = 0; ++ int bw_fld = 0; ++ int i; ++ struct bus_rule_type *static_rule = NULL; ++ ++ of_node = pdev->dev.of_node; ++ num_rules = of_get_child_count(of_node); ++ static_rule = devm_kzalloc(&pdev->dev, ++ sizeof(struct bus_rule_type) * num_rules, ++ GFP_KERNEL); ++ ++ if (IS_ERR_OR_NULL(static_rule)) { ++ ret = -ENOMEM; ++ goto exit_static_rules; ++ } ++ ++ *static_rules = static_rule; ++ for_each_child_of_node(of_node, child_node) { ++ ret = msm_bus_of_get_ids(pdev, child_node, ++ &static_rule[rule_idx].src_id, ++ &static_rule[rule_idx].num_src, ++ "qcom,src-nodes"); ++ ++ ret = msm_bus_of_get_ids(pdev, child_node, ++ &static_rule[rule_idx].dst_node, ++ &static_rule[rule_idx].num_dst, ++ "qcom,dest-node"); ++ ++ ret = of_property_read_u32(child_node, "qcom,src-field", ++ &static_rule[rule_idx].src_field); ++ if (ret) { ++ dev_err(&pdev->dev, "src-field missing"); ++ ret = -ENXIO; ++ goto err_static_rules; ++ } ++ ++ ret = of_property_read_u32(child_node, "qcom,src-op", ++ &static_rule[rule_idx].op); ++ if (ret) { ++ dev_err(&pdev->dev, "src-op missing"); ++ ret = -ENXIO; ++ goto err_static_rules; ++ } ++ ++ ret = of_property_read_u32(child_node, "qcom,mode", ++ &static_rule[rule_idx].mode); ++ if (ret) { ++ dev_err(&pdev->dev, "mode missing"); ++ ret = -ENXIO; ++ goto err_static_rules; ++ } ++ ++ ret = of_property_read_u32(child_node, "qcom,thresh", &bw_fld); ++ if (ret) { ++ dev_err(&pdev->dev, "thresh missing"); ++ ret = -ENXIO; ++ goto err_static_rules; ++ } else ++ static_rule[rule_idx].thresh = KBTOB(bw_fld); ++ ++ ret = of_property_read_u32(child_node, "qcom,dest-bw", ++ &bw_fld); ++ if (ret) ++ static_rule[rule_idx].dst_bw = 0; ++ else ++ static_rule[rule_idx].dst_bw = KBTOB(bw_fld); ++ ++ rule_idx++; ++ } ++ ret = rule_idx; ++exit_static_rules: ++ return ret; ++err_static_rules: ++ for (i = 0; i < num_rules; i++) { ++ if (!IS_ERR_OR_NULL(static_rule)) { ++ if (!IS_ERR_OR_NULL(static_rule[i].src_id)) ++ devm_kfree(&pdev->dev, ++ static_rule[i].src_id); ++ if (!IS_ERR_OR_NULL(static_rule[i].dst_node)) ++ devm_kfree(&pdev->dev, ++ static_rule[i].dst_node); ++ devm_kfree(&pdev->dev, static_rule); ++ } ++ } ++ devm_kfree(&pdev->dev, *static_rules); ++ static_rules = NULL; ++ return ret; ++} +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_rules.c +@@ -0,0 +1,624 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include "msm-bus-board.h" ++#include "msm_bus_rules.h" ++#include ++ ++struct node_vote_info { ++ int id; ++ u64 ib; ++ u64 ab; ++ u64 clk; ++}; ++ ++struct rules_def { ++ int rule_id; ++ int num_src; ++ int state; ++ struct node_vote_info *src_info; ++ struct bus_rule_type rule_ops; ++ bool state_change; ++ struct list_head link; ++}; ++ ++struct rule_node_info { ++ int id; ++ void *data; ++ struct raw_notifier_head rule_notify_list; ++ int cur_rule; ++ int num_rules; ++ struct list_head node_rules; ++ struct list_head link; ++ struct rule_apply_rcm_info apply; ++}; ++ ++DEFINE_MUTEX(msm_bus_rules_lock); ++static LIST_HEAD(node_list); ++static struct rule_node_info *get_node(u32 id, void *data); ++ ++#define LE(op1, op2) (op1 <= op2) ++#define LT(op1, op2) (op1 < op2) ++#define GE(op1, op2) (op1 >= op2) ++#define GT(op1, op2) (op1 > op2) ++#define NB_ID (0x201) ++ ++static struct rule_node_info *get_node(u32 id, void *data) ++{ ++ struct rule_node_info *node_it = NULL; ++ struct rule_node_info *node_match = NULL; ++ ++ list_for_each_entry(node_it, &node_list, link) { ++ if (node_it->id == id) { ++ if ((id == NB_ID)) { ++ if ((node_it->data == data)) { ++ node_match = node_it; ++ break; ++ } ++ } else { ++ node_match = node_it; ++ break; ++ } ++ } ++ } ++ return node_match; ++} ++ ++static struct rule_node_info *gen_node(u32 id, void *data) ++{ ++ struct rule_node_info *node_it = NULL; ++ struct rule_node_info *node_match = NULL; ++ ++ list_for_each_entry(node_it, &node_list, link) { ++ if (node_it->id == id) { ++ node_match = node_it; ++ break; ++ } ++ } ++ ++ if (!node_match) { ++ node_match = kzalloc(sizeof(struct rule_node_info), GFP_KERNEL); ++ if (!node_match) { ++ pr_err("%s: Cannot allocate memory", __func__); ++ goto exit_node_match; ++ } ++ ++ node_match->id = id; ++ node_match->cur_rule = -1; ++ node_match->num_rules = 0; ++ node_match->data = data; ++ list_add_tail(&node_match->link, &node_list); ++ INIT_LIST_HEAD(&node_match->node_rules); ++ RAW_INIT_NOTIFIER_HEAD(&node_match->rule_notify_list); ++ pr_debug("Added new node %d to list\n", id); ++ } ++exit_node_match: ++ return node_match; ++} ++ ++static bool do_compare_op(u64 op1, u64 op2, int op) ++{ ++ bool ret = false; ++ ++ switch (op) { ++ case OP_LE: ++ ret = LE(op1, op2); ++ break; ++ case OP_LT: ++ ret = LT(op1, op2); ++ break; ++ case OP_GT: ++ ret = GT(op1, op2); ++ break; ++ case OP_GE: ++ ret = GE(op1, op2); ++ break; ++ case OP_NOOP: ++ ret = true; ++ break; ++ default: ++ pr_info("Invalid OP %d", op); ++ break; ++ } ++ return ret; ++} ++ ++static void update_src_id_vote(struct rule_update_path_info *inp_node, ++ struct rule_node_info *rule_node) ++{ ++ struct rules_def *rule; ++ int i; ++ ++ list_for_each_entry(rule, &rule_node->node_rules, link) { ++ for (i = 0; i < rule->num_src; i++) { ++ if (rule->src_info[i].id == inp_node->id) { ++ rule->src_info[i].ib = inp_node->ib; ++ rule->src_info[i].ab = inp_node->ab; ++ rule->src_info[i].clk = inp_node->clk; ++ } ++ } ++ } ++} ++ ++static u64 get_field(struct rules_def *rule, int src_id) ++{ ++ u64 field = 0; ++ int i; ++ ++ for (i = 0; i < rule->num_src; i++) { ++ switch (rule->rule_ops.src_field) { ++ case FLD_IB: ++ field += rule->src_info[i].ib; ++ break; ++ case FLD_AB: ++ field += rule->src_info[i].ab; ++ break; ++ case FLD_CLK: ++ field += rule->src_info[i].clk; ++ break; ++ } ++ } ++ ++ return field; ++} ++ ++static bool check_rule(struct rules_def *rule, ++ struct rule_update_path_info *inp) ++{ ++ bool ret = false; ++ ++ if (!rule) ++ return ret; ++ ++ switch (rule->rule_ops.op) { ++ case OP_LE: ++ case OP_LT: ++ case OP_GT: ++ case OP_GE: ++ { ++ u64 src_field = get_field(rule, inp->id); ++ if (!src_field) ++ ret = false; ++ else ++ ret = do_compare_op(src_field, rule->rule_ops.thresh, ++ rule->rule_ops.op); ++ break; ++ } ++ default: ++ pr_err("Unsupported op %d", rule->rule_ops.op); ++ break; ++ } ++ return ret; ++} ++ ++static void match_rule(struct rule_update_path_info *inp_node, ++ struct rule_node_info *node) ++{ ++ struct rules_def *rule; ++ int i; ++ ++ list_for_each_entry(rule, &node->node_rules, link) { ++ for (i = 0; i < rule->num_src; i++) { ++ if (rule->src_info[i].id == inp_node->id) { ++ if (check_rule(rule, inp_node)) { ++ trace_bus_rules_matches(node->cur_rule, ++ inp_node->id, inp_node->ab, ++ inp_node->ib, inp_node->clk); ++ if (rule->state == ++ RULE_STATE_NOT_APPLIED) ++ rule->state_change = true; ++ rule->state = RULE_STATE_APPLIED; ++ } else { ++ if (rule->state == ++ RULE_STATE_APPLIED) ++ rule->state_change = true; ++ rule->state = RULE_STATE_NOT_APPLIED; ++ } ++ } ++ } ++ } ++} ++ ++static void apply_rule(struct rule_node_info *node, ++ struct list_head *output_list) ++{ ++ struct rules_def *rule; ++ ++ node->cur_rule = -1; ++ list_for_each_entry(rule, &node->node_rules, link) { ++ if ((rule->state == RULE_STATE_APPLIED) && ++ (node->cur_rule == -1)) ++ node->cur_rule = rule->rule_id; ++ ++ if (node->id == NB_ID) { ++ if (rule->state_change) { ++ rule->state_change = false; ++ raw_notifier_call_chain(&node->rule_notify_list, ++ rule->state, (void *)&rule->rule_ops); ++ } ++ } else { ++ if ((rule->state == RULE_STATE_APPLIED) && ++ (node->cur_rule == rule->rule_id)) { ++ node->apply.id = rule->rule_ops.dst_node[0]; ++ node->apply.throttle = rule->rule_ops.mode; ++ node->apply.lim_bw = rule->rule_ops.dst_bw; ++ list_add_tail(&node->apply.link, output_list); ++ } ++ rule->state_change = false; ++ } ++ } ++ ++} ++ ++int msm_rules_update_path(struct list_head *input_list, ++ struct list_head *output_list) ++{ ++ int ret = 0; ++ struct rule_update_path_info *inp_node; ++ struct rule_node_info *node_it = NULL; ++ ++ mutex_lock(&msm_bus_rules_lock); ++ list_for_each_entry(inp_node, input_list, link) { ++ list_for_each_entry(node_it, &node_list, link) { ++ update_src_id_vote(inp_node, node_it); ++ match_rule(inp_node, node_it); ++ } ++ } ++ ++ list_for_each_entry(node_it, &node_list, link) ++ apply_rule(node_it, output_list); ++ ++ mutex_unlock(&msm_bus_rules_lock); ++ return ret; ++} ++ ++static bool ops_equal(int op1, int op2) ++{ ++ bool ret = false; ++ ++ switch (op1) { ++ case OP_GT: ++ case OP_GE: ++ case OP_LT: ++ case OP_LE: ++ if (abs(op1 - op2) <= 1) ++ ret = true; ++ break; ++ default: ++ ret = (op1 == op2); ++ } ++ ++ return ret; ++} ++ ++static int node_rules_compare(void *priv, struct list_head *a, ++ struct list_head *b) ++{ ++ struct rules_def *ra = container_of(a, struct rules_def, link); ++ struct rules_def *rb = container_of(b, struct rules_def, link); ++ int ret = -1; ++ int64_t th_diff = 0; ++ ++ ++ if (ra->rule_ops.mode == rb->rule_ops.mode) { ++ if (ops_equal(ra->rule_ops.op, rb->rule_ops.op)) { ++ if ((ra->rule_ops.op == OP_LT) || ++ (ra->rule_ops.op == OP_LE)) { ++ th_diff = ra->rule_ops.thresh - ++ rb->rule_ops.thresh; ++ if (th_diff > 0) ++ ret = 1; ++ else ++ ret = -1; ++ } else if ((ra->rule_ops.op == OP_GT) || ++ (ra->rule_ops.op == OP_GE)) { ++ th_diff = rb->rule_ops.thresh - ++ ra->rule_ops.thresh; ++ if (th_diff > 0) ++ ret = 1; ++ else ++ ret = -1; ++ } ++ } else ++ ret = ra->rule_ops.op - rb->rule_ops.op; ++ } else if ((ra->rule_ops.mode == THROTTLE_OFF) && ++ (rb->rule_ops.mode == THROTTLE_ON)) { ++ ret = 1; ++ } else if ((ra->rule_ops.mode == THROTTLE_ON) && ++ (rb->rule_ops.mode == THROTTLE_OFF)) { ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++static void print_rules(struct rule_node_info *node_it) ++{ ++ struct rules_def *node_rule = NULL; ++ int i; ++ ++ if (!node_it) { ++ pr_err("%s: no node for found", __func__); ++ return; ++ } ++ ++ pr_info("\n Now printing rules for Node %d cur rule %d\n", ++ node_it->id, node_it->cur_rule); ++ list_for_each_entry(node_rule, &node_it->node_rules, link) { ++ pr_info("\n num Rules %d rule Id %d\n", ++ node_it->num_rules, node_rule->rule_id); ++ pr_info("Rule: src_field %d\n", node_rule->rule_ops.src_field); ++ for (i = 0; i < node_rule->rule_ops.num_src; i++) ++ pr_info("Rule: src %d\n", ++ node_rule->rule_ops.src_id[i]); ++ for (i = 0; i < node_rule->rule_ops.num_dst; i++) ++ pr_info("Rule: dst %d dst_bw %llu\n", ++ node_rule->rule_ops.dst_node[i], ++ node_rule->rule_ops.dst_bw); ++ pr_info("Rule: thresh %llu op %d mode %d State %d\n", ++ node_rule->rule_ops.thresh, ++ node_rule->rule_ops.op, ++ node_rule->rule_ops.mode, ++ node_rule->state); ++ } ++} ++ ++void print_all_rules(void) ++{ ++ struct rule_node_info *node_it = NULL; ++ ++ list_for_each_entry(node_it, &node_list, link) ++ print_rules(node_it); ++} ++ ++void print_rules_buf(char *buf, int max_buf) ++{ ++ struct rule_node_info *node_it = NULL; ++ struct rules_def *node_rule = NULL; ++ int i; ++ int cnt = 0; ++ ++ list_for_each_entry(node_it, &node_list, link) { ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "\n Now printing rules for Node %d cur_rule %d\n", ++ node_it->id, node_it->cur_rule); ++ list_for_each_entry(node_rule, &node_it->node_rules, link) { ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "\nNum Rules:%d ruleId %d STATE:%d change:%d\n", ++ node_it->num_rules, node_rule->rule_id, ++ node_rule->state, node_rule->state_change); ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "Src_field %d\n", ++ node_rule->rule_ops.src_field); ++ for (i = 0; i < node_rule->rule_ops.num_src; i++) ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "Src %d Cur Ib %llu Ab %llu\n", ++ node_rule->rule_ops.src_id[i], ++ node_rule->src_info[i].ib, ++ node_rule->src_info[i].ab); ++ for (i = 0; i < node_rule->rule_ops.num_dst; i++) ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "Dst %d dst_bw %llu\n", ++ node_rule->rule_ops.dst_node[0], ++ node_rule->rule_ops.dst_bw); ++ cnt += scnprintf(buf + cnt, max_buf - cnt, ++ "Thresh %llu op %d mode %d\n", ++ node_rule->rule_ops.thresh, ++ node_rule->rule_ops.op, ++ node_rule->rule_ops.mode); ++ } ++ } ++} ++ ++static int copy_rule(struct bus_rule_type *src, struct rules_def *node_rule, ++ struct notifier_block *nb) ++{ ++ int i; ++ int ret = 0; ++ ++ memcpy(&node_rule->rule_ops, src, ++ sizeof(struct bus_rule_type)); ++ node_rule->rule_ops.src_id = kzalloc( ++ (sizeof(int) * node_rule->rule_ops.num_src), ++ GFP_KERNEL); ++ if (!node_rule->rule_ops.src_id) { ++ pr_err("%s:Failed to allocate for src_id", ++ __func__); ++ return -ENOMEM; ++ } ++ memcpy(node_rule->rule_ops.src_id, src->src_id, ++ sizeof(int) * src->num_src); ++ ++ ++ if (!nb) { ++ node_rule->rule_ops.dst_node = kzalloc( ++ (sizeof(int) * node_rule->rule_ops.num_dst), ++ GFP_KERNEL); ++ if (!node_rule->rule_ops.dst_node) { ++ pr_err("%s:Failed to allocate for src_id", ++ __func__); ++ return -ENOMEM; ++ } ++ memcpy(node_rule->rule_ops.dst_node, src->dst_node, ++ sizeof(int) * src->num_dst); ++ } ++ ++ node_rule->num_src = src->num_src; ++ node_rule->src_info = kzalloc( ++ (sizeof(struct node_vote_info) * node_rule->rule_ops.num_src), ++ GFP_KERNEL); ++ if (!node_rule->src_info) { ++ pr_err("%s:Failed to allocate for src_id", ++ __func__); ++ return -ENOMEM; ++ } ++ for (i = 0; i < src->num_src; i++) ++ node_rule->src_info[i].id = src->src_id[i]; ++ ++ return ret; ++} ++ ++void msm_rule_register(int num_rules, struct bus_rule_type *rule, ++ struct notifier_block *nb) ++{ ++ struct rule_node_info *node = NULL; ++ int i, j; ++ struct rules_def *node_rule = NULL; ++ int num_dst = 0; ++ ++ if (!rule) ++ return; ++ ++ mutex_lock(&msm_bus_rules_lock); ++ for (i = 0; i < num_rules; i++) { ++ if (nb) ++ num_dst = 1; ++ else ++ num_dst = rule[i].num_dst; ++ ++ for (j = 0; j < num_dst; j++) { ++ int id = 0; ++ ++ if (nb) ++ id = NB_ID; ++ else ++ id = rule[i].dst_node[j]; ++ ++ node = gen_node(id, nb); ++ if (!node) { ++ pr_info("Error getting rule"); ++ goto exit_rule_register; ++ } ++ node_rule = kzalloc(sizeof(struct rules_def), ++ GFP_KERNEL); ++ if (!node_rule) { ++ pr_err("%s: Failed to allocate for rule", ++ __func__); ++ goto exit_rule_register; ++ } ++ ++ if (copy_rule(&rule[i], node_rule, nb)) { ++ pr_err("Error copying rule"); ++ goto exit_rule_register; ++ } ++ ++ node_rule->rule_id = node->num_rules++; ++ if (nb) ++ node->data = nb; ++ ++ list_add_tail(&node_rule->link, &node->node_rules); ++ } ++ } ++ list_sort(NULL, &node->node_rules, node_rules_compare); ++ ++ if (nb) ++ raw_notifier_chain_register(&node->rule_notify_list, nb); ++exit_rule_register: ++ mutex_unlock(&msm_bus_rules_lock); ++ return; ++} ++ ++static int comp_rules(struct bus_rule_type *rulea, struct bus_rule_type *ruleb) ++{ ++ int ret = 1; ++ ++ if (rulea->num_src == ruleb->num_src) ++ ret = memcmp(rulea->src_id, ruleb->src_id, ++ (sizeof(int) * rulea->num_src)); ++ if (!ret && (rulea->num_dst == ruleb->num_dst)) ++ ret = memcmp(rulea->dst_node, ruleb->dst_node, ++ (sizeof(int) * rulea->num_dst)); ++ if (!ret && (rulea->dst_bw == ruleb->dst_bw) && ++ (rulea->op == ruleb->op) && (rulea->thresh == ruleb->thresh)) ++ ret = 0; ++ ++ return ret; ++} ++ ++void msm_rule_unregister(int num_rules, struct bus_rule_type *rule, ++ struct notifier_block *nb) ++{ ++ int i; ++ struct rule_node_info *node = NULL; ++ struct rule_node_info *node_tmp = NULL; ++ struct rules_def *node_rule; ++ struct rules_def *node_rule_tmp; ++ bool match_found = false; ++ ++ if (!rule) ++ return; ++ ++ mutex_lock(&msm_bus_rules_lock); ++ if (nb) { ++ node = get_node(NB_ID, nb); ++ if (!node) { ++ pr_err("%s: Can't find node", __func__); ++ goto exit_unregister_rule; ++ } ++ ++ list_for_each_entry_safe(node_rule, node_rule_tmp, ++ &node->node_rules, link) { ++ list_del(&node_rule->link); ++ kfree(node_rule); ++ node->num_rules--; ++ } ++ raw_notifier_chain_unregister(&node->rule_notify_list, nb); ++ } else { ++ for (i = 0; i < num_rules; i++) { ++ match_found = false; ++ ++ list_for_each_entry(node, &node_list, link) { ++ list_for_each_entry_safe(node_rule, ++ node_rule_tmp, &node->node_rules, link) { ++ if (comp_rules(&node_rule->rule_ops, ++ &rule[i]) == 0) { ++ list_del(&node_rule->link); ++ kfree(node_rule); ++ match_found = true; ++ node->num_rules--; ++ list_sort(NULL, ++ &node->node_rules, ++ node_rules_compare); ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ list_for_each_entry_safe(node, node_tmp, ++ &node_list, link) { ++ if (!node->num_rules) { ++ pr_debug("Deleting Rule node %d", node->id); ++ list_del(&node->link); ++ kfree(node); ++ } ++ } ++exit_unregister_rule: ++ mutex_unlock(&msm_bus_rules_lock); ++} ++ ++bool msm_rule_are_rules_registered(void) ++{ ++ bool ret = false; ++ ++ if (list_empty(&node_list)) ++ ret = false; ++ else ++ ret = true; ++ ++ return ret; ++} ++ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_bus_rules.h +@@ -0,0 +1,77 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_BUS_RULES_H ++#define _ARCH_ARM_MACH_MSM_BUS_RULES_H ++ ++#include ++#include ++#include ++#include ++ ++#define MAX_NODES (5) ++ ++struct rule_update_path_info { ++ u32 id; ++ u64 ab; ++ u64 ib; ++ u64 clk; ++ struct list_head link; ++}; ++ ++struct rule_apply_rcm_info { ++ u32 id; ++ u64 lim_bw; ++ int throttle; ++ bool after_clk_commit; ++ struct list_head link; ++}; ++ ++struct bus_rule_type { ++ int num_src; ++ int *src_id; ++ int src_field; ++ int op; ++ u64 thresh; ++ int num_dst; ++ int *dst_node; ++ u64 dst_bw; ++ int mode; ++ void *client_data; ++}; ++ ++#if (defined(CONFIG_BUS_TOPOLOGY_ADHOC)) ++void msm_rule_register(int num_rules, struct bus_rule_type *rule, ++ struct notifier_block *nb); ++void msm_rule_unregister(int num_rules, struct bus_rule_type *rule, ++ struct notifier_block *nb); ++void print_rules_buf(char *buf, int count); ++bool msm_rule_are_rules_registered(void); ++#else ++static inline void msm_rule_register(int num_rules, struct bus_rule_type *rule, ++ struct notifier_block *nb) ++{ ++} ++static inline void msm_rule_unregister(int num_rules, ++ struct bus_rule_type *rule, ++ struct notifier_block *nb) ++{ ++} ++static inline void print_rules_buf(char *buf, int count) ++{ ++} ++static inline bool msm_rule_are_rules_registered(void) ++{ ++ return false; ++} ++#endif /* defined(CONFIG_BUS_TOPOLOGY_ADHOC) */ ++#endif /* _ARCH_ARM_MACH_MSM_BUS_RULES_H */ +--- /dev/null ++++ b/drivers/bus/msm_bus/msm_buspm_coresight_adhoc.c +@@ -0,0 +1,189 @@ ++/* Copyright (c) 2014 The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct msmbus_coresight_adhoc_clock_drvdata { ++ int id; ++ struct clk *clk; ++ struct list_head list; ++}; ++ ++struct msmbus_coresight_adhoc_drvdata { ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct coresight_desc *desc; ++ struct list_head clocks; ++}; ++ ++static int msmbus_coresight_enable_adhoc(struct coresight_device *csdev) ++{ ++ struct msmbus_coresight_adhoc_clock_drvdata *clk; ++ struct msmbus_coresight_adhoc_drvdata *drvdata = ++ dev_get_drvdata(csdev->dev.parent); ++ long rate; ++ ++ list_for_each_entry(clk, &drvdata->clocks, list) { ++ if (clk->id == csdev->id) { ++ rate = clk_round_rate(clk->clk, 1L); ++ clk_set_rate(clk->clk, rate); ++ return clk_prepare_enable(clk->clk); ++ } ++ } ++ ++ return -ENOENT; ++} ++ ++static void msmbus_coresight_disable_adhoc(struct coresight_device *csdev) ++{ ++ struct msmbus_coresight_adhoc_clock_drvdata *clk; ++ struct msmbus_coresight_adhoc_drvdata *drvdata = ++ dev_get_drvdata(csdev->dev.parent); ++ ++ list_for_each_entry(clk, &drvdata->clocks, list) { ++ if (clk->id == csdev->id) ++ clk_disable_unprepare(clk->clk); ++ } ++} ++ ++static const struct coresight_ops_source msmbus_coresight_adhoc_source_ops = { ++ .enable = msmbus_coresight_enable_adhoc, ++ .disable = msmbus_coresight_disable_adhoc, ++}; ++ ++static const struct coresight_ops msmbus_coresight_cs_ops = { ++ .source_ops = &msmbus_coresight_adhoc_source_ops, ++}; ++ ++void msmbus_coresight_remove_adhoc(struct platform_device *pdev) ++{ ++ struct msmbus_coresight_adhoc_clock_drvdata *clk, *next_clk; ++ struct msmbus_coresight_adhoc_drvdata *drvdata = ++ platform_get_drvdata(pdev); ++ ++ msmbus_coresight_disable_adhoc(drvdata->csdev); ++ coresight_unregister(drvdata->csdev); ++ list_for_each_entry_safe(clk, next_clk, &drvdata->clocks, list) { ++ list_del(&clk->list); ++ devm_kfree(&pdev->dev, clk); ++ } ++ devm_kfree(&pdev->dev, drvdata->desc); ++ devm_kfree(&pdev->dev, drvdata); ++ platform_set_drvdata(pdev, NULL); ++} ++EXPORT_SYMBOL(msmbus_coresight_remove_adhoc); ++ ++static int buspm_of_get_clk_adhoc(struct device_node *of_node, ++ struct msmbus_coresight_adhoc_drvdata *drvdata, int id) ++{ ++ struct msmbus_coresight_adhoc_clock_drvdata *clk; ++ clk = devm_kzalloc(drvdata->dev, sizeof(*clk), GFP_KERNEL); ++ ++ if (!clk) ++ return -ENOMEM; ++ ++ clk->id = id; ++ ++ clk->clk = of_clk_get_by_name(of_node, "bus_clk"); ++ if (IS_ERR(clk->clk)) { ++ pr_err("Error: unable to get clock for coresight node %d\n", ++ id); ++ goto err; ++ } ++ ++ list_add(&clk->list, &drvdata->clocks); ++ return 0; ++ ++err: ++ devm_kfree(drvdata->dev, clk); ++ return -EINVAL; ++} ++ ++int msmbus_coresight_init_adhoc(struct platform_device *pdev, ++ struct device_node *of_node) ++{ ++ int ret; ++ struct device *dev = &pdev->dev; ++ struct coresight_platform_data *pdata; ++ struct msmbus_coresight_adhoc_drvdata *drvdata; ++ struct coresight_desc *desc; ++ ++ pdata = of_get_coresight_platform_data(dev, of_node); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ ++ drvdata = platform_get_drvdata(pdev); ++ if (IS_ERR_OR_NULL(drvdata)) { ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) { ++ pr_err("coresight: Alloc for drvdata failed\n"); ++ return -ENOMEM; ++ } ++ INIT_LIST_HEAD(&drvdata->clocks); ++ drvdata->dev = &pdev->dev; ++ platform_set_drvdata(pdev, drvdata); ++ } ++ ret = buspm_of_get_clk_adhoc(of_node, drvdata, pdata->id); ++ if (ret) { ++ pr_err("Error getting clocks\n"); ++ ret = -ENXIO; ++ goto err1; ++ } ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) { ++ pr_err("coresight: Error allocating memory\n"); ++ ret = -ENOMEM; ++ goto err1; ++ } ++ ++ desc->type = CORESIGHT_DEV_TYPE_SOURCE; ++ desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_BUS; ++ desc->ops = &msmbus_coresight_cs_ops; ++ desc->pdata = pdata; ++ desc->dev = &pdev->dev; ++ desc->owner = THIS_MODULE; ++ drvdata->desc = desc; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) { ++ pr_err("coresight: Coresight register failed\n"); ++ ret = PTR_ERR(drvdata->csdev); ++ goto err0; ++ } ++ ++ dev_info(dev, "msmbus_coresight initialized\n"); ++ ++ return 0; ++err0: ++ devm_kfree(dev, desc); ++err1: ++ devm_kfree(dev, drvdata); ++ platform_set_drvdata(pdev, NULL); ++ return ret; ++} ++EXPORT_SYMBOL(msmbus_coresight_init_adhoc); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("MSM BusPM Adhoc CoreSight Driver"); +--- /dev/null ++++ b/drivers/bus/msm_bus/rpm-smd.h +@@ -0,0 +1,268 @@ ++/* Copyright (c) 2012, 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __ARCH_ARM_MACH_MSM_RPM_SMD_H ++#define __ARCH_ARM_MACH_MSM_RPM_SMD_H ++ ++/** ++ * enum msm_rpm_set - RPM enumerations for sleep/active set ++ * %MSM_RPM_CTX_SET_0: Set resource parameters for active mode. ++ * %MSM_RPM_CTX_SET_SLEEP: Set resource parameters for sleep. ++ */ ++enum msm_rpm_set { ++ MSM_RPM_CTX_ACTIVE_SET, ++ MSM_RPM_CTX_SLEEP_SET, ++}; ++ ++struct msm_rpm_request; ++ ++struct msm_rpm_kvp { ++ uint32_t key; ++ uint32_t length; ++ uint8_t *data; ++}; ++#ifdef CONFIG_MSM_RPM_SMD ++/** ++ * msm_rpm_request() - Creates a parent element to identify the ++ * resource on the RPM, that stores the KVPs for different fields modified ++ * for a hardware resource ++ * ++ * @set: if the device is setting the active/sleep set parameter ++ * for the resource ++ * @rsc_type: unsigned 32 bit integer that identifies the type of the resource ++ * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type ++ * @num_elements: number of KVPs pairs associated with the resource ++ * ++ * returns pointer to a msm_rpm_request on success, NULL on error ++ */ ++struct msm_rpm_request *msm_rpm_create_request( ++ enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, int num_elements); ++ ++/** ++ * msm_rpm_request_noirq() - Creates a parent element to identify the ++ * resource on the RPM, that stores the KVPs for different fields modified ++ * for a hardware resource. This function is similar to msm_rpm_create_request ++ * except that it has to be called with interrupts masked. ++ * ++ * @set: if the device is setting the active/sleep set parameter ++ * for the resource ++ * @rsc_type: unsigned 32 bit integer that identifies the type of the resource ++ * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type ++ * @num_elements: number of KVPs pairs associated with the resource ++ * ++ * returns pointer to a msm_rpm_request on success, NULL on error ++ */ ++struct msm_rpm_request *msm_rpm_create_request_noirq( ++ enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, int num_elements); ++ ++/** ++ * msm_rpm_add_kvp_data() - Adds a Key value pair to a existing RPM resource. ++ * ++ * @handle: RPM resource handle to which the data should be appended ++ * @key: unsigned integer identify the parameter modified ++ * @data: byte array that contains the value corresponding to key. ++ * @size: size of data in bytes. ++ * ++ * returns 0 on success or errno ++ */ ++int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, ++ uint32_t key, const uint8_t *data, int size); ++ ++/** ++ * msm_rpm_add_kvp_data_noirq() - Adds a Key value pair to a existing RPM ++ * resource. This function is similar to msm_rpm_add_kvp_data except that it ++ * has to be called with interrupts masked. ++ * ++ * @handle: RPM resource handle to which the data should be appended ++ * @key: unsigned integer identify the parameter modified ++ * @data: byte array that contains the value corresponding to key. ++ * @size: size of data in bytes. ++ * ++ * returns 0 on success or errno ++ */ ++int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, ++ uint32_t key, const uint8_t *data, int size); ++ ++/** msm_rpm_free_request() - clean up the RPM request handle created with ++ * msm_rpm_create_request ++ * ++ * @handle: RPM resource handle to be cleared. ++ */ ++ ++void msm_rpm_free_request(struct msm_rpm_request *handle); ++ ++/** ++ * msm_rpm_send_request() - Send the RPM messages using SMD. The function ++ * assigns a message id before sending the data out to the RPM. RPM hardware ++ * uses the message id to acknowledge the messages. ++ * ++ * @handle: pointer to the msm_rpm_request for the resource being modified. ++ * ++ * returns non-zero message id on success and zero on a failed transaction. ++ * The drivers use message id to wait for ACK from RPM. ++ */ ++int msm_rpm_send_request(struct msm_rpm_request *handle); ++ ++/** ++ * msm_rpm_send_request_noirq() - Send the RPM messages using SMD. The ++ * function assigns a message id before sending the data out to the RPM. ++ * RPM hardware uses the message id to acknowledge the messages. This function ++ * is similar to msm_rpm_send_request except that it has to be called with ++ * interrupts masked. ++ * ++ * @handle: pointer to the msm_rpm_request for the resource being modified. ++ * ++ * returns non-zero message id on success and zero on a failed transaction. ++ * The drivers use message id to wait for ACK from RPM. ++ */ ++int msm_rpm_send_request_noirq(struct msm_rpm_request *handle); ++ ++/** ++ * msm_rpm_wait_for_ack() - A blocking call that waits for acknowledgment of ++ * a message from RPM. ++ * ++ * @msg_id: the return from msm_rpm_send_requests ++ * ++ * returns 0 on success or errno ++ */ ++int msm_rpm_wait_for_ack(uint32_t msg_id); ++ ++/** ++ * msm_rpm_wait_for_ack_noirq() - A blocking call that waits for acknowledgment ++ * of a message from RPM. This function is similar to msm_rpm_wait_for_ack ++ * except that it has to be called with interrupts masked. ++ * ++ * @msg_id: the return from msm_rpm_send_request ++ * ++ * returns 0 on success or errno ++ */ ++int msm_rpm_wait_for_ack_noirq(uint32_t msg_id); ++ ++/** ++ * msm_rpm_send_message() -Wrapper function for clients to send data given an ++ * array of key value pairs. ++ * ++ * @set: if the device is setting the active/sleep set parameter ++ * for the resource ++ * @rsc_type: unsigned 32 bit integer that identifies the type of the resource ++ * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type ++ * @kvp: array of KVP data. ++ * @nelem: number of KVPs pairs associated with the message. ++ * ++ * returns 0 on success and errno on failure. ++ */ ++int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); ++ ++/** ++ * msm_rpm_send_message_noirq() -Wrapper function for clients to send data ++ * given an array of key value pairs. This function is similar to the ++ * msm_rpm_send_message() except that it has to be called with interrupts ++ * disabled. Clients should choose the irq version when possible for system ++ * performance. ++ * ++ * @set: if the device is setting the active/sleep set parameter ++ * for the resource ++ * @rsc_type: unsigned 32 bit integer that identifies the type of the resource ++ * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type ++ * @kvp: array of KVP data. ++ * @nelem: number of KVPs pairs associated with the message. ++ * ++ * returns 0 on success and errno on failure. ++ */ ++int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); ++ ++/** ++ * msm_rpm_driver_init() - Initialization function that registers for a ++ * rpm platform driver. ++ * ++ * returns 0 on success. ++ */ ++int __init msm_rpm_driver_init(void); ++ ++#else ++ ++static inline struct msm_rpm_request *msm_rpm_create_request( ++ enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, int num_elements) ++{ ++ return NULL; ++} ++ ++static inline struct msm_rpm_request *msm_rpm_create_request_noirq( ++ enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, int num_elements) ++{ ++ return NULL; ++ ++} ++static inline uint32_t msm_rpm_add_kvp_data(struct msm_rpm_request *handle, ++ uint32_t key, const uint8_t *data, int count) ++{ ++ return 0; ++} ++static inline uint32_t msm_rpm_add_kvp_data_noirq( ++ struct msm_rpm_request *handle, uint32_t key, ++ const uint8_t *data, int count) ++{ ++ return 0; ++} ++ ++static inline void msm_rpm_free_request(struct msm_rpm_request *handle) ++{ ++ return; ++} ++ ++static inline int msm_rpm_send_request(struct msm_rpm_request *handle) ++{ ++ return 0; ++} ++ ++static inline int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) ++{ ++ return 0; ++ ++} ++ ++static inline int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, ++ uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) ++{ ++ return 0; ++} ++ ++static inline int msm_rpm_send_message_noirq(enum msm_rpm_set set, ++ uint32_t rsc_type, uint32_t rsc_id, struct msm_rpm_kvp *kvp, ++ int nelems) ++{ ++ return 0; ++} ++ ++static inline int msm_rpm_wait_for_ack(uint32_t msg_id) ++{ ++ return 0; ++ ++} ++static inline int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) ++{ ++ return 0; ++} ++ ++static inline int __init msm_rpm_driver_init(void) ++{ ++ return 0; ++} ++#endif ++#endif /*__ARCH_ARM_MACH_MSM_RPM_SMD_H*/ +--- /dev/null ++++ b/include/trace/events/trace_msm_bus.h +@@ -0,0 +1,163 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM msm_bus ++ ++#if !defined(_TRACE_MSM_BUS_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_MSM_BUS_H ++ ++#include ++ ++TRACE_EVENT(bus_update_request, ++ ++ TP_PROTO(int sec, int nsec, const char *name, unsigned int index, ++ int src, int dest, unsigned long long ab, ++ unsigned long long ib), ++ ++ TP_ARGS(sec, nsec, name, index, src, dest, ab, ib), ++ ++ TP_STRUCT__entry( ++ __field(int, sec) ++ __field(int, nsec) ++ __string(name, name) ++ __field(u32, index) ++ __field(int, src) ++ __field(int, dest) ++ __field(u64, ab) ++ __field(u64, ib) ++ ), ++ ++ TP_fast_assign( ++ __entry->sec = sec; ++ __entry->nsec = nsec; ++ __assign_str(name, name); ++ __entry->index = index; ++ __entry->src = src; ++ __entry->dest = dest; ++ __entry->ab = ab; ++ __entry->ib = ib; ++ ), ++ ++ TP_printk("time= %d.%d name=%s index=%u src=%d dest=%d ab=%llu ib=%llu", ++ __entry->sec, ++ __entry->nsec, ++ __get_str(name), ++ (unsigned int)__entry->index, ++ __entry->src, ++ __entry->dest, ++ (unsigned long long)__entry->ab, ++ (unsigned long long)__entry->ib) ++); ++ ++TRACE_EVENT(bus_bimc_config_limiter, ++ ++ TP_PROTO(int mas_id, unsigned long long cur_lim_bw), ++ ++ TP_ARGS(mas_id, cur_lim_bw), ++ ++ TP_STRUCT__entry( ++ __field(int, mas_id) ++ __field(u64, cur_lim_bw) ++ ), ++ ++ TP_fast_assign( ++ __entry->mas_id = mas_id; ++ __entry->cur_lim_bw = cur_lim_bw; ++ ), ++ ++ TP_printk("Master=%d cur_lim_bw=%llu", ++ __entry->mas_id, ++ (unsigned long long)__entry->cur_lim_bw) ++); ++ ++TRACE_EVENT(bus_avail_bw, ++ ++ TP_PROTO(unsigned long long cur_bimc_bw, unsigned long long cur_mdp_bw), ++ ++ TP_ARGS(cur_bimc_bw, cur_mdp_bw), ++ ++ TP_STRUCT__entry( ++ __field(u64, cur_bimc_bw) ++ __field(u64, cur_mdp_bw) ++ ), ++ ++ TP_fast_assign( ++ __entry->cur_bimc_bw = cur_bimc_bw; ++ __entry->cur_mdp_bw = cur_mdp_bw; ++ ), ++ ++ TP_printk("cur_bimc_bw = %llu cur_mdp_bw = %llu", ++ (unsigned long long)__entry->cur_bimc_bw, ++ (unsigned long long)__entry->cur_mdp_bw) ++); ++ ++TRACE_EVENT(bus_rules_matches, ++ ++ TP_PROTO(int node_id, int rule_id, unsigned long long node_ab, ++ unsigned long long node_ib, unsigned long long node_clk), ++ ++ TP_ARGS(node_id, rule_id, node_ab, node_ib, node_clk), ++ ++ TP_STRUCT__entry( ++ __field(int, node_id) ++ __field(int, rule_id) ++ __field(u64, node_ab) ++ __field(u64, node_ib) ++ __field(u64, node_clk) ++ ), ++ ++ TP_fast_assign( ++ __entry->node_id = node_id; ++ __entry->rule_id = rule_id; ++ __entry->node_ab = node_ab; ++ __entry->node_ib = node_ib; ++ __entry->node_clk = node_clk; ++ ), ++ ++ TP_printk("Rule match node%d rule%d node-ab%llu:ib%llu:clk%llu", ++ __entry->node_id, __entry->rule_id, ++ (unsigned long long)__entry->node_ab, ++ (unsigned long long)__entry->node_ib, ++ (unsigned long long)__entry->node_clk) ++); ++ ++TRACE_EVENT(bus_bke_params, ++ ++ TP_PROTO(u32 gc, u32 gp, u32 thl, u32 thm, u32 thh), ++ ++ TP_ARGS(gc, gp, thl, thm, thh), ++ ++ TP_STRUCT__entry( ++ __field(u32, gc) ++ __field(u32, gp) ++ __field(u32, thl) ++ __field(u32, thm) ++ __field(u32, thh) ++ ), ++ ++ TP_fast_assign( ++ __entry->gc = gc; ++ __entry->gp = gp; ++ __entry->thl = thl; ++ __entry->thm = thm; ++ __entry->thh = thh; ++ ), ++ ++ TP_printk("BKE Params GC=0x%x GP=0x%x THL=0x%x THM=0x%x THH=0x%x", ++ __entry->gc, __entry->gp, __entry->thl, __entry->thm, ++ __entry->thh) ++); ++ ++#endif ++#define TRACE_INCLUDE_FILE trace_msm_bus ++#include diff --git a/target/linux/ipq806x/patches-4.9/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch b/target/linux/ipq806x/patches-4.14/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch similarity index 93% rename from target/linux/ipq806x/patches-4.9/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch rename to target/linux/ipq806x/patches-4.14/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch index ee19f363b..86cb45461 100644 --- a/target/linux/ipq806x/patches-4.9/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch +++ b/target/linux/ipq806x/patches-4.14/400-mtd-ubi-add-quirk-to-autoload-ubi-on-rt-ac58u.patch @@ -17,7 +17,7 @@ Signed-off-by: Christian Lamparter --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c -@@ -1225,6 +1225,9 @@ static void __init ubi_auto_attach(void) +@@ -1170,6 +1170,9 @@ static void __init ubi_auto_attach(void) mtd = open_mtd_device("ubi"); if (IS_ERR(mtd)) mtd = open_mtd_device("data"); diff --git a/target/linux/ipq806x/patches-4.9/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch b/target/linux/ipq806x/patches-4.14/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch similarity index 95% rename from target/linux/ipq806x/patches-4.9/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch rename to target/linux/ipq806x/patches-4.14/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch index d2721875a..a52fe2832 100644 --- a/target/linux/ipq806x/patches-4.9/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch +++ b/target/linux/ipq806x/patches-4.14/605-net-IPQ4019-needs-rfs-vlan_tag-callbacks-in.patch @@ -24,7 +24,7 @@ Reviewed-by: Grant Grundler --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -725,6 +725,16 @@ struct xps_map { +@@ -713,6 +713,16 @@ struct xps_map { #define XPS_MIN_MAP_ALLOC ((L1_CACHE_ALIGN(offsetof(struct xps_map, queues[1])) \ - sizeof(struct xps_map)) / sizeof(u16)) @@ -41,7 +41,7 @@ Reviewed-by: Grant Grundler /* * This structure holds all XPS maps for device. Maps are indexed by CPU. */ -@@ -1251,6 +1261,9 @@ struct net_device_ops { +@@ -1239,6 +1249,9 @@ struct net_device_ops { const struct sk_buff *skb, u16 rxq_index, u32 flow_id); diff --git a/target/linux/ipq806x/patches-4.9/700-net-add-qualcomm-mdio-and-phy.patch b/target/linux/ipq806x/patches-4.14/700-net-add-qualcomm-mdio-and-phy.patch similarity index 99% rename from target/linux/ipq806x/patches-4.9/700-net-add-qualcomm-mdio-and-phy.patch rename to target/linux/ipq806x/patches-4.14/700-net-add-qualcomm-mdio-and-phy.patch index 003524312..c6e715510 100644 --- a/target/linux/ipq806x/patches-4.9/700-net-add-qualcomm-mdio-and-phy.patch +++ b/target/linux/ipq806x/patches-4.14/700-net-add-qualcomm-mdio-and-phy.patch @@ -10,9 +10,9 @@ Subject: [PATCH 30/38] NET: add qualcomm mdio and PHY --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -408,6 +408,20 @@ config XILINX_GMII2RGMII - the Reduced Gigabit Media Independent Interface(RGMII) between - Ethernet physical media devices and the Gigabit Ethernet controller. +@@ -481,6 +481,20 @@ config XILINX_GMII2RGMII + the Reduced Gigabit Media Independent Interface(RGMII) between + Ethernet physical media devices and the Gigabit Ethernet controller. +config MDIO_IPQ40XX + tristate "Qualcomm Atheros ipq40xx MDIO interface" @@ -33,15 +33,15 @@ Subject: [PATCH 30/38] NET: add qualcomm mdio and PHY config MICREL_KS8995MA --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -32,6 +32,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += md - obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o +@@ -48,6 +48,7 @@ obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o + obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o +obj-$(CONFIG_MDIO_IPQ40XX) += mdio-ipq40xx.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o -@@ -40,6 +41,7 @@ obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +@@ -60,6 +61,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o diff --git a/target/linux/ipq806x/patches-4.9/701-dts-ipq4019-add-mdio-node.patch b/target/linux/ipq806x/patches-4.14/701-dts-ipq4019-add-mdio-node.patch similarity index 95% rename from target/linux/ipq806x/patches-4.9/701-dts-ipq4019-add-mdio-node.patch rename to target/linux/ipq806x/patches-4.14/701-dts-ipq4019-add-mdio-node.patch index 676da7214..857d5c393 100644 --- a/target/linux/ipq806x/patches-4.9/701-dts-ipq4019-add-mdio-node.patch +++ b/target/linux/ipq806x/patches-4.14/701-dts-ipq4019-add-mdio-node.patch @@ -15,8 +15,8 @@ so the info might change. --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -315,6 +315,34 @@ - reg = <0x4ab000 0x4>; +@@ -415,6 +415,34 @@ + status = "disabled"; }; + mdio@90000 { diff --git a/target/linux/ipq806x/patches-4.9/702-dts-ipq4019-add-PHY-switch-nodes.patch b/target/linux/ipq806x/patches-4.14/702-dts-ipq4019-add-PHY-switch-nodes.patch similarity index 98% rename from target/linux/ipq806x/patches-4.9/702-dts-ipq4019-add-PHY-switch-nodes.patch rename to target/linux/ipq806x/patches-4.14/702-dts-ipq4019-add-PHY-switch-nodes.patch index 79031d398..bf28090c8 100644 --- a/target/linux/ipq806x/patches-4.9/702-dts-ipq4019-add-PHY-switch-nodes.patch +++ b/target/linux/ipq806x/patches-4.14/702-dts-ipq4019-add-PHY-switch-nodes.patch @@ -14,7 +14,7 @@ Signed-off-by: Christian Lamparter --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -343,6 +343,29 @@ +@@ -443,6 +443,29 @@ }; }; diff --git a/target/linux/ipq806x/patches-4.9/710-net-add-qualcomm-essedma-ethernet-driver.patch b/target/linux/ipq806x/patches-4.14/710-net-add-qualcomm-essedma-ethernet-driver.patch similarity index 99% rename from target/linux/ipq806x/patches-4.9/710-net-add-qualcomm-essedma-ethernet-driver.patch rename to target/linux/ipq806x/patches-4.14/710-net-add-qualcomm-essedma-ethernet-driver.patch index eb84124b5..a6673346c 100644 --- a/target/linux/ipq806x/patches-4.9/710-net-add-qualcomm-essedma-ethernet-driver.patch +++ b/target/linux/ipq806x/patches-4.14/710-net-add-qualcomm-essedma-ethernet-driver.patch @@ -11,9 +11,9 @@ Signed-off-by: Christian Lamparter --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig -@@ -37,4 +37,13 @@ config QCOM_EMAC - low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 - Precision Clock Synchronization Protocol. +@@ -61,4 +61,13 @@ config QCOM_EMAC + + source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" +config ESSEDMA + tristate "Qualcomm Atheros ESS Edma support" @@ -27,11 +27,12 @@ Signed-off-by: Christian Lamparter endif # NET_VENDOR_QUALCOMM --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_QCA7000) += qcaspi.o - qcaspi-objs := qca_spi.o qca_framing.o qca_7k.o qca_debug.o +@@ -11,4 +11,5 @@ qcauart-objs := qca_uart.o obj-y += emac/ + +obj-$(CONFIG_ESSEDMA) += essedma/ + obj-$(CONFIG_RMNET) += rmnet/ --- /dev/null +++ b/drivers/net/ethernet/qualcomm/essedma/Makefile @@ -0,0 +1,9 @@ diff --git a/target/linux/ipq806x/patches-4.9/711-dts-ipq4019-add-ethernet-essedma-node.patch b/target/linux/ipq806x/patches-4.14/711-dts-ipq4019-add-ethernet-essedma-node.patch similarity index 99% rename from target/linux/ipq806x/patches-4.9/711-dts-ipq4019-add-ethernet-essedma-node.patch rename to target/linux/ipq806x/patches-4.14/711-dts-ipq4019-add-ethernet-essedma-node.patch index 771ff2275..0472cfa77 100644 --- a/target/linux/ipq806x/patches-4.9/711-dts-ipq4019-add-ethernet-essedma-node.patch +++ b/target/linux/ipq806x/patches-4.14/711-dts-ipq4019-add-ethernet-essedma-node.patch @@ -25,7 +25,7 @@ Signed-off-by: Christian Lamparter }; cpus { -@@ -366,6 +368,64 @@ +@@ -466,6 +468,64 @@ status = "disabled"; }; diff --git a/target/linux/ipq806x/patches-4.9/712-net-essedma-disable-default-vlan.patch b/target/linux/ipq806x/patches-4.14/712-net-essedma-disable-default-vlan.patch similarity index 98% rename from target/linux/ipq806x/patches-4.9/712-net-essedma-disable-default-vlan.patch rename to target/linux/ipq806x/patches-4.14/712-net-essedma-disable-default-vlan.patch index 2a1cade85..e94339bd5 100644 --- a/target/linux/ipq806x/patches-4.9/712-net-essedma-disable-default-vlan.patch +++ b/target/linux/ipq806x/patches-4.14/712-net-essedma-disable-default-vlan.patch @@ -48,7 +48,7 @@ Signed-off-by: Christian Lamparter if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -404,8 +404,7 @@ +@@ -474,8 +474,7 @@ qcom,page-mode = <0>; qcom,rx_head_buf_size = <1540>; qcom,mdio_supported; @@ -58,7 +58,7 @@ Signed-off-by: Christian Lamparter interrupts = <0 65 IRQ_TYPE_EDGE_RISING 0 66 IRQ_TYPE_EDGE_RISING 0 67 IRQ_TYPE_EDGE_RISING -@@ -443,7 +442,7 @@ +@@ -513,7 +512,7 @@ gmac0: gmac0 { local-mac-address = [00 00 00 00 00 00]; diff --git a/target/linux/ipq806x/patches-4.9/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.14/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch similarity index 99% rename from target/linux/ipq806x/patches-4.9/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch rename to target/linux/ipq806x/patches-4.14/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch index 3636c5ca0..47291fea0 100644 --- a/target/linux/ipq806x/patches-4.9/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch +++ b/target/linux/ipq806x/patches-4.14/820-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch @@ -24,7 +24,7 @@ Changed: --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig -@@ -194,6 +194,17 @@ config USB_MXS_PHY +@@ -188,6 +188,17 @@ config USB_MXS_PHY MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. diff --git a/target/linux/ipq806x/patches-4.9/830-usb-dwc3-register-qca-ipq4019-dwc3-in-dwc3-of-simple.patch b/target/linux/ipq806x/patches-4.14/830-usb-dwc3-register-qca-ipq4019-dwc3-in-dwc3-of-simple.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/830-usb-dwc3-register-qca-ipq4019-dwc3-in-dwc3-of-simple.patch rename to target/linux/ipq806x/patches-4.14/830-usb-dwc3-register-qca-ipq4019-dwc3-in-dwc3-of-simple.patch diff --git a/target/linux/ipq806x/patches-4.14/850-soc-add-qualcomm-syscon.patch b/target/linux/ipq806x/patches-4.14/850-soc-add-qualcomm-syscon.patch new file mode 100644 index 000000000..59e277c34 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/850-soc-add-qualcomm-syscon.patch @@ -0,0 +1,177 @@ +From: Christian Lamparter +Subject: SoC: add qualcomm syscon +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_st + obj-$(CONFIG_QCOM_SMP2P) += smp2p.o + obj-$(CONFIG_QCOM_SMSM) += smsm.o + obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o ++obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -78,6 +78,13 @@ config QCOM_SMSM + Say yes here to support the Qualcomm Shared Memory State Machine. + The state machine is represented by bits in shared memory. + ++config QCOM_TCSR ++ tristate "QCOM Top Control and Status Registers" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable TCSR support. The TCSR provides control ++ functions for various peripherals. ++ + config QCOM_WCNSS_CTRL + tristate "Qualcomm WCNSS control driver" + depends on ARCH_QCOM +--- /dev/null ++++ b/drivers/soc/qcom/qcom_tcsr.c +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 2 as published by the free Software foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TCSR_USB_PORT_SEL 0xb0 ++#define TCSR_USB_HSPHY_CONFIG 0xC ++ ++#define TCSR_ESS_INTERFACE_SEL_OFFSET 0x0 ++#define TCSR_ESS_INTERFACE_SEL_MASK 0xf ++ ++#define TCSR_WIFI0_GLB_CFG_OFFSET 0x0 ++#define TCSR_WIFI1_GLB_CFG_OFFSET 0x4 ++#define TCSR_PNOC_SNOC_MEMTYPE_M0_M2 0x4 ++ ++static int tcsr_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ const struct device_node *node = pdev->dev.of_node; ++ void __iomem *base; ++ u32 val; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) { ++ dev_err(&pdev->dev, "setting usb port select = %d\n", val); ++ writel(val, base + TCSR_USB_PORT_SEL); ++ } ++ ++ if (!of_property_read_u32(node, "qcom,usb-hsphy-mode-select", &val)) { ++ dev_info(&pdev->dev, "setting usb hs phy mode select = %x\n", val); ++ writel(val, base + TCSR_USB_HSPHY_CONFIG); ++ } ++ ++ if (!of_property_read_u32(node, "qcom,ess-interface-select", &val)) { ++ u32 tmp = 0; ++ dev_info(&pdev->dev, "setting ess interface select = %x\n", val); ++ tmp = readl(base + TCSR_ESS_INTERFACE_SEL_OFFSET); ++ tmp = tmp & (~TCSR_ESS_INTERFACE_SEL_MASK); ++ tmp = tmp | (val&TCSR_ESS_INTERFACE_SEL_MASK); ++ writel(tmp, base + TCSR_ESS_INTERFACE_SEL_OFFSET); ++ } ++ ++ if (!of_property_read_u32(node, "qcom,wifi_glb_cfg", &val)) { ++ dev_info(&pdev->dev, "setting wifi_glb_cfg = %x\n", val); ++ writel(val, base + TCSR_WIFI0_GLB_CFG_OFFSET); ++ writel(val, base + TCSR_WIFI1_GLB_CFG_OFFSET); ++ } ++ ++ if (!of_property_read_u32(node, "qcom,wifi_noc_memtype_m0_m2", &val)) { ++ dev_info(&pdev->dev, ++ "setting wifi_noc_memtype_m0_m2 = %x\n", val); ++ writel(val, base + TCSR_PNOC_SNOC_MEMTYPE_M0_M2); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id tcsr_dt_match[] = { ++ { .compatible = "qcom,tcsr", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, tcsr_dt_match); ++ ++static struct platform_driver tcsr_driver = { ++ .driver = { ++ .name = "tcsr", ++ .owner = THIS_MODULE, ++ .of_match_table = tcsr_dt_match, ++ }, ++ .probe = tcsr_probe, ++}; ++ ++module_platform_driver(tcsr_driver); ++ ++MODULE_AUTHOR("Andy Gross "); ++MODULE_DESCRIPTION("QCOM TCSR driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,tcsr.h +@@ -0,0 +1,48 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DT_BINDINGS_QCOM_TCSR_H ++#define __DT_BINDINGS_QCOM_TCSR_H ++ ++#define TCSR_USB_SELECT_USB3_P0 0x1 ++#define TCSR_USB_SELECT_USB3_P1 0x2 ++#define TCSR_USB_SELECT_USB3_DUAL 0x3 ++ ++/* IPQ40xx HS PHY Mode Select */ ++#define TCSR_USB_HSPHY_HOST_MODE 0x00E700E7 ++#define TCSR_USB_HSPHY_DEVICE_MODE 0x00C700E7 ++ ++/* IPQ40xx ess interface mode select */ ++#define TCSR_ESS_PSGMII 0 ++#define TCSR_ESS_PSGMII_RGMII5 1 ++#define TCSR_ESS_PSGMII_RMII0 2 ++#define TCSR_ESS_PSGMII_RMII1 4 ++#define TCSR_ESS_PSGMII_RMII0_RMII1 6 ++#define TCSR_ESS_PSGMII_RGMII4 9 ++ ++/* ++ * IPQ40xx WiFi Global Config ++ * Bit 30:AXID_EN ++ * Enable AXI master bus Axid translating to confirm all txn submitted by order ++ * Bit 24: Use locally generated socslv_wxi_bvalid ++ * 1: use locally generate socslv_wxi_bvalid for performance. ++ * 0: use SNOC socslv_wxi_bvalid. ++ */ ++#define TCSR_WIFI_GLB_CFG 0x41000000 ++ ++/* IPQ40xx MEM_TYPE_SEL_M0_M2 Select Bit 26:24 - 2 NORMAL */ ++#define TCSR_WIFI_NOC_MEMTYPE_M0_M2 0x02222222 ++ ++/* TCSR A/B REG */ ++#define IPQ806X_TCSR_REG_A_ADM_CRCI_MUX_SEL 0 ++#define IPQ806X_TCSR_REG_B_ADM_CRCI_MUX_SEL 1 ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.9/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch b/target/linux/ipq806x/patches-4.14/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch similarity index 98% rename from target/linux/ipq806x/patches-4.9/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch rename to target/linux/ipq806x/patches-4.14/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch index f054927e0..29a1e1e1a 100644 --- a/target/linux/ipq806x/patches-4.9/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch +++ b/target/linux/ipq806x/patches-4.14/863-dts-ipq4019-add-nand-and-qpic-bam-dma-node.patch @@ -120,9 +120,9 @@ Signed-off-by: Ram Chandra Jangir }; --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -580,5 +580,43 @@ - "legacy"; - status = "disabled"; +@@ -595,5 +595,43 @@ + dr_mode = "host"; + }; }; + + qpic_bam: dma@7984000 { diff --git a/target/linux/ipq806x/patches-4.9/864-01-dts-ipq4019-ap-dk04-fix-pinctrl-node-name.patch b/target/linux/ipq806x/patches-4.14/864-01-dts-ipq4019-ap-dk04-fix-pinctrl-node-name.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/864-01-dts-ipq4019-ap-dk04-fix-pinctrl-node-name.patch rename to target/linux/ipq806x/patches-4.14/864-01-dts-ipq4019-ap-dk04-fix-pinctrl-node-name.patch diff --git a/target/linux/ipq806x/patches-4.9/864-02-dts-ipq4019-ap-dk04-remove-xo-and-timer-nodes.patch b/target/linux/ipq806x/patches-4.14/864-02-dts-ipq4019-ap-dk04-remove-xo-and-timer-nodes.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/864-02-dts-ipq4019-ap-dk04-remove-xo-and-timer-nodes.patch rename to target/linux/ipq806x/patches-4.14/864-02-dts-ipq4019-ap-dk04-remove-xo-and-timer-nodes.patch diff --git a/target/linux/ipq806x/patches-4.9/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch b/target/linux/ipq806x/patches-4.14/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch similarity index 93% rename from target/linux/ipq806x/patches-4.9/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch rename to target/linux/ipq806x/patches-4.14/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch index 9d11dc022..6b8b2819e 100644 --- a/target/linux/ipq806x/patches-4.9/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch +++ b/target/linux/ipq806x/patches-4.14/864-03-dts-ipq4019-ap-dk01-add-tcsr-config-to-dtsi.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -15,12 +15,39 @@ +@@ -15,6 +15,7 @@ */ #include "qcom-ipq4019.dtsi" @@ -8,9 +8,10 @@ / { model = "Qualcomm Technologies, Inc. IPQ4019/AP-DK01.1"; - compatible = "qcom,ipq4019"; +@@ -25,6 +26,32 @@ + status = "ok"; + }; - soc { + tcsr@194b000 { + /* select hostmode */ + compatible = "qcom,tcsr"; diff --git a/target/linux/ipq806x/patches-4.9/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch b/target/linux/ipq806x/patches-4.14/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch similarity index 95% rename from target/linux/ipq806x/patches-4.9/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch rename to target/linux/ipq806x/patches-4.14/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch index 02a102e37..85139201a 100644 --- a/target/linux/ipq806x/patches-4.9/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch +++ b/target/linux/ipq806x/patches-4.14/864-04-dts-ipq4019-ap-dk01-add-network-nodes-to-dtsi.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -136,5 +136,29 @@ +@@ -148,5 +148,29 @@ usb2: usb2@60f8800 { status = "ok"; }; diff --git a/target/linux/ipq806x/patches-4.9/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch b/target/linux/ipq806x/patches-4.14/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch similarity index 95% rename from target/linux/ipq806x/patches-4.9/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch rename to target/linux/ipq806x/patches-4.14/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch index 0d4b80b30..5cbb79cb1 100644 --- a/target/linux/ipq806x/patches-4.9/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch +++ b/target/linux/ipq806x/patches-4.14/864-05-dts-ipq4019-ap-dk01-remove-spi-chip-node-from-dtsi.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -89,14 +89,6 @@ +@@ -93,14 +93,6 @@ pinctrl-names = "default"; status = "ok"; cs-gpios = <&tlmm 54 0>; diff --git a/target/linux/ipq806x/patches-4.9/864-06-dts-ipq4019-fix-max-cpu-speed.patch b/target/linux/ipq806x/patches-4.14/864-06-dts-ipq4019-fix-max-cpu-speed.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/864-06-dts-ipq4019-fix-max-cpu-speed.patch rename to target/linux/ipq806x/patches-4.14/864-06-dts-ipq4019-fix-max-cpu-speed.patch diff --git a/target/linux/ipq806x/patches-4.9/864-07-dts-ipq4019-ap-dk01.1-c1-add-spi-and-ram-nodes.patch b/target/linux/ipq806x/patches-4.14/864-07-dts-ipq4019-ap-dk01.1-c1-add-spi-and-ram-nodes.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/864-07-dts-ipq4019-ap-dk01.1-c1-add-spi-and-ram-nodes.patch rename to target/linux/ipq806x/patches-4.14/864-07-dts-ipq4019-ap-dk01.1-c1-add-spi-and-ram-nodes.patch diff --git a/target/linux/ipq806x/patches-4.9/864-08-dts-ipq4019-ap-dk01.1-c1-add-compatible-string.patch b/target/linux/ipq806x/patches-4.14/864-08-dts-ipq4019-ap-dk01.1-c1-add-compatible-string.patch similarity index 100% rename from target/linux/ipq806x/patches-4.9/864-08-dts-ipq4019-ap-dk01.1-c1-add-compatible-string.patch rename to target/linux/ipq806x/patches-4.14/864-08-dts-ipq4019-ap-dk01.1-c1-add-compatible-string.patch diff --git a/target/linux/ipq806x/patches-4.14/999-boost-rpm-clocks.patch b/target/linux/ipq806x/patches-4.14/999-boost-rpm-clocks.patch new file mode 100644 index 000000000..527357593 --- /dev/null +++ b/target/linux/ipq806x/patches-4.14/999-boost-rpm-clocks.patch @@ -0,0 +1,49 @@ +From 7edd55da016c307f10f87b8e31a9b926ae706a9f Mon Sep 17 00:00:00 2001 +From: dissent1 +Date: Tue, 20 Jun 2017 11:23:20 +0300 +Subject: [PATCH] clk: qcom: Boost all rpm clocks to max rate + +This hack just boosts all RPM clocks to max values for better performance. + +Based on https://source.codeaurora.org/quic/la/kernel/msm/commit/?h=linaro/release/qcomlt-4.9-next&id=f87198c71c88d5ce7303467c3ffcd54786d98f55 +--- + drivers/clk/qcom/clk-rpm.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c +index 1762236515..06c76d9248 100644 +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -12,6 +12,7 @@ + * GNU General Public License for more details. + */ + ++#include + #include + #include + #include +@@ -448,6 +449,7 @@ static int rpm_clk_probe(struct platform_device *pdev) + struct qcom_rpm *rpm; + struct clk_rpm **rpm_clks; + const struct rpm_clk_desc *desc; ++ struct clk *clk; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { +@@ -484,9 +486,14 @@ static int rpm_clk_probe(struct platform_device *pdev) + if (!rpm_clks[i]) + continue; + +- ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); +- if (ret) ++ clk = devm_clk_register(&pdev->dev, &rpm_clks[i]->hw); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); + goto err; ++ } ++ ++ clk_set_rate(clk, INT_MAX); ++ clk_prepare_enable(clk); + } + + ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, diff --git a/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch b/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch new file mode 100644 index 000000000..b9056e961 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch @@ -0,0 +1,135 @@ +From ee15faffef11309219aa87a24efc86f6dd13f7cb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 26 Oct 2015 17:11:32 -0700 +Subject: clk: qcom: common: Add API to register board clocks backwards + compatibly + +We want to put the XO board clocks into the dt files, but we also +need to be backwards compatible with an older dtb. Add an API to +the common code to do this. This also makes a place for us to +handle the case when the RPM clock driver is enabled and we don't +want to register the fixed factor clock. + +Cc: Georgi Djakov +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/common.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/common.h | 4 +++ + 2 files changed, 91 insertions(+) + +--- a/drivers/clk/qcom/common.c ++++ b/drivers/clk/qcom/common.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include "common.h" + #include "clk-rcg.h" +@@ -88,6 +89,92 @@ static void qcom_cc_gdsc_unregister(void + gdsc_unregister(data); + } + ++/* ++ * Backwards compatibility with old DTs. Register a pass-through factor 1/1 ++ * clock to translate 'path' clk into 'name' clk and regsiter the 'path' ++ * clk as a fixed rate clock if it isn't present. ++ */ ++static int _qcom_cc_register_board_clk(struct device *dev, const char *path, ++ const char *name, unsigned long rate, ++ bool add_factor) ++{ ++ struct device_node *node = NULL; ++ struct device_node *clocks_node; ++ struct clk_fixed_factor *factor; ++ struct clk_fixed_rate *fixed; ++ struct clk *clk; ++ struct clk_init_data init_data = { }; ++ ++ clocks_node = of_find_node_by_path("/clocks"); ++ if (clocks_node) ++ node = of_find_node_by_name(clocks_node, path); ++ of_node_put(clocks_node); ++ ++ if (!node) { ++ fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); ++ if (!fixed) ++ return -EINVAL; ++ ++ fixed->fixed_rate = rate; ++ fixed->hw.init = &init_data; ++ ++ init_data.name = path; ++ init_data.flags = CLK_IS_ROOT; ++ init_data.ops = &clk_fixed_rate_ops; ++ ++ clk = devm_clk_register(dev, &fixed->hw); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ of_node_put(node); ++ ++ if (add_factor) { ++ factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL); ++ if (!factor) ++ return -EINVAL; ++ ++ factor->mult = factor->div = 1; ++ factor->hw.init = &init_data; ++ ++ init_data.name = name; ++ init_data.parent_names = &path; ++ init_data.num_parents = 1; ++ init_data.flags = 0; ++ init_data.ops = &clk_fixed_factor_ops; ++ ++ clk = devm_clk_register(dev, &factor->hw); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ ++ return 0; ++} ++ ++int qcom_cc_register_board_clk(struct device *dev, const char *path, ++ const char *name, unsigned long rate) ++{ ++ bool add_factor = true; ++ struct device_node *node; ++ ++ /* The RPM clock driver will add the factor clock if present */ ++ if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { ++ node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); ++ if (of_device_is_available(node)) ++ add_factor = false; ++ of_node_put(node); ++ } ++ ++ return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); ++} ++EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk); ++ ++int qcom_cc_register_sleep_clk(struct device *dev) ++{ ++ return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src", ++ 32768, true); ++} ++EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk); ++ + int qcom_cc_really_probe(struct platform_device *pdev, + const struct qcom_cc_desc *desc, struct regmap *regmap) + { +--- a/drivers/clk/qcom/common.h ++++ b/drivers/clk/qcom/common.h +@@ -37,6 +37,10 @@ extern const struct freq_tbl *qcom_find_ + extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, + u8 src); + ++extern int qcom_cc_register_board_clk(struct device *dev, const char *path, ++ const char *name, unsigned long rate); ++extern int qcom_cc_register_sleep_clk(struct device *dev); ++ + extern struct regmap *qcom_cc_map(struct platform_device *pdev, + const struct qcom_cc_desc *desc); + extern int qcom_cc_really_probe(struct platform_device *pdev, diff --git a/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch b/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch new file mode 100644 index 000000000..626112547 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch @@ -0,0 +1,35 @@ +From add479eeb1a208a31ab913ae7c97506a81383079 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel +Date: Thu, 25 Feb 2016 10:45:12 +0100 +Subject: clk: qcom: Make reset_control_ops const + +The qcom_reset_ops structure is never modified. Make it const. + +Signed-off-by: Philipp Zabel +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/reset.c | 2 +- + drivers/clk/qcom/reset.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/reset.c ++++ b/drivers/clk/qcom/reset.c +@@ -55,7 +55,7 @@ qcom_reset_deassert(struct reset_control + return regmap_update_bits(rst->regmap, map->reg, mask, 0); + } + +-struct reset_control_ops qcom_reset_ops = { ++const struct reset_control_ops qcom_reset_ops = { + .reset = qcom_reset, + .assert = qcom_reset_assert, + .deassert = qcom_reset_deassert, +--- a/drivers/clk/qcom/reset.h ++++ b/drivers/clk/qcom/reset.h +@@ -32,6 +32,6 @@ struct qcom_reset_controller { + #define to_qcom_reset_controller(r) \ + container_of(r, struct qcom_reset_controller, rcdev); + +-extern struct reset_control_ops qcom_reset_ops; ++extern const struct reset_control_ops qcom_reset_ops; + + #endif diff --git a/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch b/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch new file mode 100644 index 000000000..4932e702a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/003-mv-cxo-pxo-xo-into-DT.patch @@ -0,0 +1,172 @@ +From a085f877a882b465fce74188c9d8efd12bd5acd4 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 26 Oct 2015 18:10:09 -0700 +Subject: clk: qcom: Move cxo/pxo/xo into dt files + +Put these clocks into the dt files instead of registering them +from C code. This provides a few benefits. It allows us to +specify the frequency of these clocks at the board level instead +of hard-coding them in the driver. It allows us to insert an RPM +clock in between the consumers of the crystals and the actual +clock. And finally, it helps us transition the GCC driver to use +RPM clocks when that configuration is enabled. + +Cc: Georgi Djakov +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/gcc-apq8084.c | 16 +++++++--------- + drivers/clk/qcom/gcc-ipq806x.c | 14 ++++++-------- + drivers/clk/qcom/gcc-msm8660.c | 15 +++++++-------- + drivers/clk/qcom/gcc-msm8960.c | 14 ++++++-------- + drivers/clk/qcom/gcc-msm8974.c | 17 +++++++---------- + 5 files changed, 33 insertions(+), 43 deletions(-) + +--- a/drivers/clk/qcom/gcc-apq8084.c ++++ b/drivers/clk/qcom/gcc-apq8084.c +@@ -3607,18 +3607,16 @@ MODULE_DEVICE_TABLE(of, gcc_apq8084_matc + + static int gcc_apq8084_probe(struct platform_device *pdev) + { +- struct clk *clk; ++ int ret; + struct device *dev = &pdev->dev; + +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "xo_board", "xo", 19200000); ++ if (ret) ++ return ret; + +- clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL, +- CLK_IS_ROOT, 32768); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_sleep_clk(dev); ++ if (ret) ++ return ret; + + return qcom_cc_probe(pdev, &gcc_apq8084_desc); + } +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -3023,19 +3023,17 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc + + static int gcc_ipq806x_probe(struct platform_device *pdev) + { +- struct clk *clk; + struct device *dev = &pdev->dev; + struct regmap *regmap; + int ret; + +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 25000000); ++ if (ret) ++ return ret; + +- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 25000000); ++ if (ret) ++ return ret; + + ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); + if (ret) +--- a/drivers/clk/qcom/gcc-msm8660.c ++++ b/drivers/clk/qcom/gcc-msm8660.c +@@ -2720,17 +2720,16 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_matc + + static int gcc_msm8660_probe(struct platform_device *pdev) + { +- struct clk *clk; ++ int ret; + struct device *dev = &pdev->dev; + +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000); ++ if (ret) ++ return ret; + +- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000); ++ if (ret) ++ return ret; + + return qcom_cc_probe(pdev, &gcc_msm8660_desc); + } +--- a/drivers/clk/qcom/gcc-msm8960.c ++++ b/drivers/clk/qcom/gcc-msm8960.c +@@ -3503,7 +3503,6 @@ MODULE_DEVICE_TABLE(of, gcc_msm8960_matc + + static int gcc_msm8960_probe(struct platform_device *pdev) + { +- struct clk *clk; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct platform_device *tsens; +@@ -3513,14 +3512,13 @@ static int gcc_msm8960_probe(struct plat + if (!match) + return -EINVAL; + +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000); ++ if (ret) ++ return ret; + +- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000); ++ if (ret) ++ return ret; + + ret = qcom_cc_probe(pdev, match->data); + if (ret) +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -2717,7 +2717,7 @@ static void msm8974_pro_clock_override(v + + static int gcc_msm8974_probe(struct platform_device *pdev) + { +- struct clk *clk; ++ int ret; + struct device *dev = &pdev->dev; + bool pro; + const struct of_device_id *id; +@@ -2730,16 +2730,13 @@ static int gcc_msm8974_probe(struct plat + if (pro) + msm8974_pro_clock_override(); + +- /* Temporary until RPM clocks supported */ +- clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- /* Should move to DT node? */ +- clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL, +- CLK_IS_ROOT, 32768); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); ++ ret = qcom_cc_register_board_clk(dev, "xo_board", "xo", 19200000); ++ if (ret) ++ return ret; ++ ++ ret = qcom_cc_register_sleep_clk(dev); ++ if (ret) ++ return ret; + + return qcom_cc_probe(pdev, &gcc_msm8974_desc); + } diff --git a/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch b/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch new file mode 100644 index 000000000..b3cef7b3f --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/005-mfd-qcom_rpm-Add-missing-of_node_put-after-calling-of_parse_phandle.patch @@ -0,0 +1,25 @@ +From 349290fc9e761aaef6d6882721189f668ec5ff49 Mon Sep 17 00:00:00 2001 +From: Peter Chen +Date: Fri, 15 Jul 2016 17:38:46 +0800 +Subject: mfd: qcom_rpm: Add missing of_node_put after calling of_parse_phandle + +of_node_put needs to be called when the device node which is got +from of_parse_phandle has finished using. + +Signed-off-by: Peter Chen +Reviewed-by: Bjorn Andersson +Signed-off-by: Lee Jones +--- + drivers/mfd/qcom_rpm.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mfd/qcom_rpm.c ++++ b/drivers/mfd/qcom_rpm.c +@@ -538,6 +538,7 @@ static int qcom_rpm_probe(struct platfor + } + + rpm->ipc_regmap = syscon_node_to_regmap(syscon_np); ++ of_node_put(syscon_np); + if (IS_ERR(rpm->ipc_regmap)) + return PTR_ERR(rpm->ipc_regmap); + diff --git a/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch b/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch new file mode 100644 index 000000000..96b768d97 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/006-mfd-qcom_rpm-Handle-message-RAM-clock.patch @@ -0,0 +1,90 @@ +From 3526403353c2a1b94c3181f900582626d23c339b Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 18 Aug 2016 20:40:45 +0200 +Subject: mfd: qcom_rpm: Handle message RAM clock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MSM8660, APQ8060, IPQ806x and MSM8960 have a GCC clock +to the message RAM used by the RPM. This needs to be enabled +for messages to pass through. This is a crude solution that +simply prepare/enable at probe() and disable/unprepare +at remove(). More elaborate PM is probably possible to +add later. + +The construction uses IS_ERR() to gracefully handle the +platforms that do not provide a message RAM clock. It will +bail out of probe only if the clock is hitting a probe +deferral situation. + +Of course this requires the proper device tree set-up: + +rpm: rpm@104000 { + compatible = "qcom,rpm-msm8660"; + clocks = <&gcc RPM_MSG_RAM_H_CLK>; + clock-names = "ram"; + ... +}; + +I have provided this in the MSM8660 device tree, and will +provide patches for the other targets. + +Cc: Björn Andersson +Signed-off-by: Linus Walleij +Signed-off-by: Lee Jones +--- + drivers/mfd/qcom_rpm.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/mfd/qcom_rpm.c ++++ b/drivers/mfd/qcom_rpm.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include + +@@ -48,6 +49,7 @@ struct qcom_rpm { + struct regmap *ipc_regmap; + unsigned ipc_offset; + unsigned ipc_bit; ++ struct clk *ramclk; + + struct completion ack; + struct mutex lock; +@@ -503,6 +505,20 @@ static int qcom_rpm_probe(struct platfor + mutex_init(&rpm->lock); + init_completion(&rpm->ack); + ++ /* Enable message RAM clock */ ++ rpm->ramclk = devm_clk_get(&pdev->dev, "ram"); ++ if (IS_ERR(rpm->ramclk)) { ++ ret = PTR_ERR(rpm->ramclk); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ /* ++ * Fall through in all other cases, as the clock is ++ * optional. (Does not exist on all platforms.) ++ */ ++ rpm->ramclk = NULL; ++ } ++ clk_prepare_enable(rpm->ramclk); /* Accepts NULL */ ++ + irq_ack = platform_get_irq_byname(pdev, "ack"); + if (irq_ack < 0) { + dev_err(&pdev->dev, "required ack interrupt missing\n"); +@@ -621,7 +637,11 @@ static int qcom_rpm_probe(struct platfor + + static int qcom_rpm_remove(struct platform_device *pdev) + { ++ struct qcom_rpm *rpm = dev_get_drvdata(&pdev->dev); ++ + of_platform_depopulate(&pdev->dev); ++ clk_disable_unprepare(rpm->ramclk); ++ + return 0; + } + diff --git a/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch new file mode 100644 index 000000000..0534e78a2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch @@ -0,0 +1,205 @@ +From 2165bf524da5f5e496d1cdb8c5afae1345ecce1e Mon Sep 17 00:00:00 2001 +From: Damien Riegel +Date: Mon, 16 Nov 2015 12:27:59 -0500 +Subject: watchdog: core: add restart handler support + +Many watchdog drivers implement the same code to register a restart +handler. This patch provides a generic way to set such a function. + +The patch adds a new restart watchdog operation. If a restart priority +greater than 0 is needed, the driver can call +watchdog_set_restart_priority to set it. + +Suggested-by: Vivien Didelot +Signed-off-by: Damien Riegel +Reviewed-by: Guenter Roeck +Reviewed-by: Vivien Didelot +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + Documentation/watchdog/watchdog-kernel-api.txt | 19 ++++++++++ + drivers/watchdog/watchdog_core.c | 48 ++++++++++++++++++++++++++ + include/linux/watchdog.h | 6 ++++ + 3 files changed, 73 insertions(+) + +--- a/Documentation/watchdog/watchdog-kernel-api.txt ++++ b/Documentation/watchdog/watchdog-kernel-api.txt +@@ -53,6 +53,7 @@ struct watchdog_device { + unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; ++ struct notifier_block restart_nb; + void *driver_data; + struct mutex lock; + unsigned long status; +@@ -75,6 +76,10 @@ It contains following fields: + * timeout: the watchdog timer's timeout value (in seconds). + * min_timeout: the watchdog timer's minimum timeout value (in seconds). + * max_timeout: the watchdog timer's maximum timeout value (in seconds). ++* restart_nb: notifier block that is registered for machine restart, for ++ internal use only. If a watchdog is capable of restarting the machine, it ++ should define ops->restart. Priority can be changed through ++ watchdog_set_restart_priority. + * bootstatus: status of the device after booting (reported with watchdog + WDIOF_* status bits). + * driver_data: a pointer to the drivers private data of a watchdog device. +@@ -100,6 +105,7 @@ struct watchdog_ops { + unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); ++ int (*restart)(struct watchdog_device *); + void (*ref)(struct watchdog_device *); + void (*unref)(struct watchdog_device *); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); +@@ -164,6 +170,8 @@ they are supported. These optional routi + (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the + watchdog's info structure). + * get_timeleft: this routines returns the time that's left before a reset. ++* restart: this routine restarts the machine. It returns 0 on success or a ++ negative errno code for failure. + * ref: the operation that calls kref_get on the kref of a dynamically + allocated watchdog_device struct. + * unref: the operation that calls kref_put on the kref of a dynamically +@@ -231,3 +239,14 @@ the device tree (if the module timeout p + to set the default timeout value as timeout value in the watchdog_device and + then use this function to set the user "preferred" timeout value. + This routine returns zero on success and a negative errno code for failure. ++ ++To change the priority of the restart handler the following helper should be ++used: ++ ++void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); ++ ++User should follow the following guidelines for setting the priority: ++* 0: should be called in last resort, has limited restart capabilities ++* 128: default restart handler, use if no other handler is expected to be ++ available, and/or if restart is sufficient to restart the entire system ++* 255: highest priority, will preempt all other restart handlers +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -32,6 +32,7 @@ + #include /* For standard types */ + #include /* For the -ENODEV/... values */ + #include /* For printk/panic/... */ ++#include /* For restart handler */ + #include /* For watchdog specific items */ + #include /* For __init/__exit/... */ + #include /* For ida_* macros */ +@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdo + } + EXPORT_SYMBOL_GPL(watchdog_init_timeout); + ++static int watchdog_restart_notifier(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ struct watchdog_device *wdd = container_of(nb, struct watchdog_device, ++ restart_nb); ++ ++ int ret; ++ ++ ret = wdd->ops->restart(wdd); ++ if (ret) ++ return NOTIFY_BAD; ++ ++ return NOTIFY_DONE; ++} ++ ++/** ++ * watchdog_set_restart_priority - Change priority of restart handler ++ * @wdd: watchdog device ++ * @priority: priority of the restart handler, should follow these guidelines: ++ * 0: use watchdog's restart function as last resort, has limited restart ++ * capabilies ++ * 128: default restart handler, use if no other handler is expected to be ++ * available and/or if restart is sufficient to restart the entire system ++ * 255: preempt all other handlers ++ * ++ * If a wdd->ops->restart function is provided when watchdog_register_device is ++ * called, it will be registered as a restart handler with the priority given ++ * here. ++ */ ++void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority) ++{ ++ wdd->restart_nb.priority = priority; ++} ++EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); ++ + static int __watchdog_register_device(struct watchdog_device *wdd) + { + int ret, id = -1, devno; +@@ -202,6 +238,15 @@ static int __watchdog_register_device(st + return ret; + } + ++ if (wdd->ops->restart) { ++ wdd->restart_nb.notifier_call = watchdog_restart_notifier; ++ ++ ret = register_restart_handler(&wdd->restart_nb); ++ if (ret) ++ dev_warn(wdd->dev, "Cannot register restart handler (%d)\n", ++ ret); ++ } ++ + return 0; + } + +@@ -238,6 +283,9 @@ static void __watchdog_unregister_device + if (wdd == NULL) + return; + ++ if (wdd->ops->restart) ++ unregister_restart_handler(&wdd->restart_nb); ++ + devno = wdd->cdev.dev; + ret = watchdog_dev_unregister(wdd); + if (ret) +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + + struct watchdog_ops; +@@ -26,6 +27,7 @@ struct watchdog_device; + * @status: The routine that shows the status of the watchdog device. + * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). + * @get_timeleft:The routine that gets the time left before a reset (in seconds). ++ * @restart: The routine for restarting the machine. + * @ref: The ref operation for dyn. allocated watchdog_device structs + * @unref: The unref operation for dyn. allocated watchdog_device structs + * @ioctl: The routines that handles extra ioctl calls. +@@ -45,6 +47,7 @@ struct watchdog_ops { + unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); ++ int (*restart)(struct watchdog_device *); + void (*ref)(struct watchdog_device *); + void (*unref)(struct watchdog_device *); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); +@@ -62,6 +65,7 @@ struct watchdog_ops { + * @timeout: The watchdog devices timeout value (in seconds). + * @min_timeout:The watchdog devices minimum timeout value (in seconds). + * @max_timeout:The watchdog devices maximum timeout value (in seconds). ++ * @restart_nb: The notifier block to register a restart function. + * @driver-data:Pointer to the drivers private data. + * @lock: Lock for watchdog core internal use only. + * @status: Field that contains the devices internal status bits. +@@ -88,6 +92,7 @@ struct watchdog_device { + unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; ++ struct notifier_block restart_nb; + void *driver_data; + struct mutex lock; + unsigned long status; +@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata + } + + /* drivers/watchdog/watchdog_core.c */ ++void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); + extern int watchdog_init_timeout(struct watchdog_device *wdd, + unsigned int timeout_parm, struct device *dev); + extern int watchdog_register_device(struct watchdog_device *); diff --git a/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch new file mode 100644 index 000000000..2afa4e566 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch @@ -0,0 +1,160 @@ +From e131319669e0ef5e6fcd75174daeffa40492135c Mon Sep 17 00:00:00 2001 +From: Damien Riegel +Date: Fri, 20 Nov 2015 16:54:51 -0500 +Subject: watchdog: core: add reboot notifier support + +Many watchdog drivers register a reboot notifier in order to stop the +watchdog on system reboot. Thus we can factorize this code in the +watchdog core. + +For that purpose, a new notifier block is added in watchdog_device for +internal use only, as well as a new watchdog_stop_on_reboot helper +function. + +If this helper is called, watchdog core registers the related notifier +block and will stop the watchdog when SYS_HALT or SYS_DOWN is received. + +Since this operation can be critical on some platforms, abort the device +registration if the reboot notifier registration fails. + +Suggested-by: Vivien Didelot +Signed-off-by: Damien Riegel +Reviewed-by: Vivien Didelot +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + Documentation/watchdog/watchdog-kernel-api.txt | 8 ++++++ + drivers/watchdog/watchdog_core.c | 37 ++++++++++++++++++++++++++ + include/linux/watchdog.h | 9 +++++++ + 3 files changed, 54 insertions(+) + +--- a/Documentation/watchdog/watchdog-kernel-api.txt ++++ b/Documentation/watchdog/watchdog-kernel-api.txt +@@ -53,6 +53,7 @@ struct watchdog_device { + unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; ++ struct notifier_block reboot_nb; + struct notifier_block restart_nb; + void *driver_data; + struct mutex lock; +@@ -76,6 +77,9 @@ It contains following fields: + * timeout: the watchdog timer's timeout value (in seconds). + * min_timeout: the watchdog timer's minimum timeout value (in seconds). + * max_timeout: the watchdog timer's maximum timeout value (in seconds). ++* reboot_nb: notifier block that is registered for reboot notifications, for ++ internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core ++ will stop the watchdog on such notifications. + * restart_nb: notifier block that is registered for machine restart, for + internal use only. If a watchdog is capable of restarting the machine, it + should define ops->restart. Priority can be changed through +@@ -240,6 +244,10 @@ to set the default timeout value as time + then use this function to set the user "preferred" timeout value. + This routine returns zero on success and a negative errno code for failure. + ++To disable the watchdog on reboot, the user must call the following helper: ++ ++static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd); ++ + To change the priority of the restart handler the following helper should be + used: + +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -138,6 +138,25 @@ int watchdog_init_timeout(struct watchdo + } + EXPORT_SYMBOL_GPL(watchdog_init_timeout); + ++static int watchdog_reboot_notifier(struct notifier_block *nb, ++ unsigned long code, void *data) ++{ ++ struct watchdog_device *wdd = container_of(nb, struct watchdog_device, ++ reboot_nb); ++ ++ if (code == SYS_DOWN || code == SYS_HALT) { ++ if (watchdog_active(wdd)) { ++ int ret; ++ ++ ret = wdd->ops->stop(wdd); ++ if (ret) ++ return NOTIFY_BAD; ++ } ++ } ++ ++ return NOTIFY_DONE; ++} ++ + static int watchdog_restart_notifier(struct notifier_block *nb, + unsigned long action, void *data) + { +@@ -238,6 +257,21 @@ static int __watchdog_register_device(st + return ret; + } + ++ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { ++ wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; ++ ++ ret = register_reboot_notifier(&wdd->reboot_nb); ++ if (ret) { ++ dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", ++ ret); ++ watchdog_dev_unregister(wdd); ++ device_destroy(watchdog_class, devno); ++ ida_simple_remove(&watchdog_ida, wdd->id); ++ wdd->dev = NULL; ++ return ret; ++ } ++ } ++ + if (wdd->ops->restart) { + wdd->restart_nb.notifier_call = watchdog_restart_notifier; + +@@ -286,6 +320,9 @@ static void __watchdog_unregister_device + if (wdd->ops->restart) + unregister_restart_handler(&wdd->restart_nb); + ++ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) ++ unregister_reboot_notifier(&wdd->reboot_nb); ++ + devno = wdd->cdev.dev; + ret = watchdog_dev_unregister(wdd); + if (ret) +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -65,6 +65,7 @@ struct watchdog_ops { + * @timeout: The watchdog devices timeout value (in seconds). + * @min_timeout:The watchdog devices minimum timeout value (in seconds). + * @max_timeout:The watchdog devices maximum timeout value (in seconds). ++ * @reboot_nb: The notifier block to stop watchdog on reboot. + * @restart_nb: The notifier block to register a restart function. + * @driver-data:Pointer to the drivers private data. + * @lock: Lock for watchdog core internal use only. +@@ -92,6 +93,7 @@ struct watchdog_device { + unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; ++ struct notifier_block reboot_nb; + struct notifier_block restart_nb; + void *driver_data; + struct mutex lock; +@@ -102,6 +104,7 @@ struct watchdog_device { + #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ + #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ + #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ ++#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */ + struct list_head deferred; + }; + +@@ -121,6 +124,12 @@ static inline void watchdog_set_nowayout + set_bit(WDOG_NO_WAY_OUT, &wdd->status); + } + ++/* Use the following function to stop the watchdog on reboot */ ++static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd) ++{ ++ set_bit(WDOG_STOP_ON_REBOOT, &wdd->status); ++} ++ + /* Use the following function to check if a timeout value is invalid */ + static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) + { diff --git a/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch new file mode 100644 index 000000000..1906a1f4e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch @@ -0,0 +1,111 @@ +From 906d7a5cfeda508e7361f021605579a00cd82815 Mon Sep 17 00:00:00 2001 +From: Pratyush Anand +Date: Thu, 17 Dec 2015 17:53:58 +0530 +Subject: watchdog: Use static struct class watchdog_class in stead of pointer + +We need few sysfs attributes to know different status of a watchdog device. +To do that, we need to associate .dev_groups with watchdog_class. So +convert it from pointer to static. +Putting this static struct in watchdog_dev.c, so that static device +attributes defined in that file can be attached to it. + +Signed-off-by: Pratyush Anand +Suggested-by: Guenter Roeck +Reviewed-by: Guenter Roeck +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/watchdog_core.c | 15 ++------------- + drivers/watchdog/watchdog_core.h | 2 +- + drivers/watchdog/watchdog_dev.c | 26 ++++++++++++++++++++++---- + 3 files changed, 25 insertions(+), 18 deletions(-) + +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -370,19 +370,9 @@ static int __init watchdog_deferred_regi + + static int __init watchdog_init(void) + { +- int err; +- +- watchdog_class = class_create(THIS_MODULE, "watchdog"); +- if (IS_ERR(watchdog_class)) { +- pr_err("couldn't create class\n"); ++ watchdog_class = watchdog_dev_init(); ++ if (IS_ERR(watchdog_class)) + return PTR_ERR(watchdog_class); +- } +- +- err = watchdog_dev_init(); +- if (err < 0) { +- class_destroy(watchdog_class); +- return err; +- } + + watchdog_deferred_registration(); + return 0; +@@ -391,7 +381,6 @@ static int __init watchdog_init(void) + static void __exit watchdog_exit(void) + { + watchdog_dev_exit(); +- class_destroy(watchdog_class); + ida_destroy(&watchdog_ida); + } + +--- a/drivers/watchdog/watchdog_core.h ++++ b/drivers/watchdog/watchdog_core.h +@@ -33,5 +33,5 @@ + */ + extern int watchdog_dev_register(struct watchdog_device *); + extern int watchdog_dev_unregister(struct watchdog_device *); +-extern int __init watchdog_dev_init(void); ++extern struct class * __init watchdog_dev_init(void); + extern void __exit watchdog_dev_exit(void); +--- a/drivers/watchdog/watchdog_dev.c ++++ b/drivers/watchdog/watchdog_dev.c +@@ -581,18 +581,35 @@ int watchdog_dev_unregister(struct watch + return 0; + } + ++static struct class watchdog_class = { ++ .name = "watchdog", ++ .owner = THIS_MODULE, ++}; ++ + /* + * watchdog_dev_init: init dev part of watchdog core + * + * Allocate a range of chardev nodes to use for watchdog devices + */ + +-int __init watchdog_dev_init(void) ++struct class * __init watchdog_dev_init(void) + { +- int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); +- if (err < 0) ++ int err; ++ ++ err = class_register(&watchdog_class); ++ if (err < 0) { ++ pr_err("couldn't register class\n"); ++ return ERR_PTR(err); ++ } ++ ++ err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); ++ if (err < 0) { + pr_err("watchdog: unable to allocate char dev region\n"); +- return err; ++ class_unregister(&watchdog_class); ++ return ERR_PTR(err); ++ } ++ ++ return &watchdog_class; + } + + /* +@@ -604,4 +621,5 @@ int __init watchdog_dev_init(void) + void __exit watchdog_dev_exit(void) + { + unregister_chrdev_region(watchdog_devt, MAX_DOGS); ++ class_unregister(&watchdog_class); + } diff --git a/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch new file mode 100644 index 000000000..fd4b38a9f --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch @@ -0,0 +1,260 @@ +From 33b711269ade3f6bc9d9d15e4343e6fa922d999b Mon Sep 17 00:00:00 2001 +From: Pratyush Anand +Date: Thu, 17 Dec 2015 17:53:59 +0530 +Subject: watchdog: Read device status through sysfs attributes + +This patch adds following attributes to watchdog device's sysfs interface +to read its different status. + +* state - reads whether device is active or not +* identity - reads Watchdog device's identity string. +* timeout - reads current timeout. +* timeleft - reads timeleft before watchdog generates a reset +* bootstatus - reads status of the watchdog device at boot +* status - reads watchdog device's internal status bits +* nowayout - reads whether nowayout feature was set or not + +Testing with iTCO_wdt: + # cd /sys/class/watchdog/watchdog1/ + # ls +bootstatus dev device identity nowayout power state +subsystem timeleft timeout uevent + # cat identity +iTCO_wdt + # cat timeout +30 + # cat state +inactive + # echo > /dev/watchdog1 + # cat timeleft +26 + # cat state +active + # cat bootstatus +0 + # cat nowayout +0 + +Signed-off-by: Pratyush Anand +Reviewed-by: Guenter Roeck +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + Documentation/ABI/testing/sysfs-class-watchdog | 51 +++++++++++ + drivers/watchdog/Kconfig | 7 ++ + drivers/watchdog/watchdog_core.c | 2 +- + drivers/watchdog/watchdog_dev.c | 114 +++++++++++++++++++++++++ + 4 files changed, 173 insertions(+), 1 deletion(-) + create mode 100644 Documentation/ABI/testing/sysfs-class-watchdog + +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-class-watchdog +@@ -0,0 +1,51 @@ ++What: /sys/class/watchdog/watchdogn/bootstatus ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It contains status of the watchdog ++ device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of ++ ioctl interface. ++ ++What: /sys/class/watchdog/watchdogn/identity ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It contains identity string of ++ watchdog device. ++ ++What: /sys/class/watchdog/watchdogn/nowayout ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. While reading, it gives '1' if that ++ device supports nowayout feature else, it gives '0'. ++ ++What: /sys/class/watchdog/watchdogn/state ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It gives active/inactive status of ++ watchdog device. ++ ++What: /sys/class/watchdog/watchdogn/status ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It contains watchdog device's ++ internal status bits. It is equivalent to WDIOC_GETSTATUS ++ of ioctl interface. ++ ++What: /sys/class/watchdog/watchdogn/timeleft ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It contains value of time left for ++ reset generation. It is equivalent to WDIOC_GETTIMELEFT of ++ ioctl interface. ++ ++What: /sys/class/watchdog/watchdogn/timeout ++Date: August 2015 ++Contact: Wim Van Sebroeck ++Description: ++ It is a read only file. It is read to know about current ++ value of timeout programmed. +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -46,6 +46,13 @@ config WATCHDOG_NOWAYOUT + get killed. If you say Y here, the watchdog cannot be stopped once + it has been started. + ++config WATCHDOG_SYSFS ++ bool "Read different watchdog information through sysfs" ++ default n ++ help ++ Say Y here if you want to enable watchdog device status read through ++ sysfs attributes. ++ + # + # General Watchdog drivers + # +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -249,7 +249,7 @@ static int __watchdog_register_device(st + + devno = wdd->cdev.dev; + wdd->dev = device_create(watchdog_class, wdd->parent, devno, +- NULL, "watchdog%d", wdd->id); ++ wdd, "watchdog%d", wdd->id); + if (IS_ERR(wdd->dev)) { + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, id); +--- a/drivers/watchdog/watchdog_dev.c ++++ b/drivers/watchdog/watchdog_dev.c +@@ -247,6 +247,119 @@ out_timeleft: + return err; + } + ++#ifdef CONFIG_WATCHDOG_SYSFS ++static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); ++} ++static DEVICE_ATTR_RO(nowayout); ++ ++static ssize_t status_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ssize_t status; ++ unsigned int val; ++ ++ status = watchdog_get_status(wdd, &val); ++ if (!status) ++ status = sprintf(buf, "%u\n", val); ++ ++ return status; ++} ++static DEVICE_ATTR_RO(status); ++ ++static ssize_t bootstatus_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", wdd->bootstatus); ++} ++static DEVICE_ATTR_RO(bootstatus); ++ ++static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ssize_t status; ++ unsigned int val; ++ ++ status = watchdog_get_timeleft(wdd, &val); ++ if (!status) ++ status = sprintf(buf, "%u\n", val); ++ ++ return status; ++} ++static DEVICE_ATTR_RO(timeleft); ++ ++static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", wdd->timeout); ++} ++static DEVICE_ATTR_RO(timeout); ++ ++static ssize_t identity_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", wdd->info->identity); ++} ++static DEVICE_ATTR_RO(identity); ++ ++static ssize_t state_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ ++ if (watchdog_active(wdd)) ++ return sprintf(buf, "active\n"); ++ ++ return sprintf(buf, "inactive\n"); ++} ++static DEVICE_ATTR_RO(state); ++ ++static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, ++ int n) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct watchdog_device *wdd = dev_get_drvdata(dev); ++ umode_t mode = attr->mode; ++ ++ if (attr == &dev_attr_status.attr && !wdd->ops->status) ++ mode = 0; ++ else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) ++ mode = 0; ++ ++ return mode; ++} ++static struct attribute *wdt_attrs[] = { ++ &dev_attr_state.attr, ++ &dev_attr_identity.attr, ++ &dev_attr_timeout.attr, ++ &dev_attr_timeleft.attr, ++ &dev_attr_bootstatus.attr, ++ &dev_attr_status.attr, ++ &dev_attr_nowayout.attr, ++ NULL, ++}; ++ ++static const struct attribute_group wdt_group = { ++ .attrs = wdt_attrs, ++ .is_visible = wdt_is_visible, ++}; ++__ATTRIBUTE_GROUPS(wdt); ++#else ++#define wdt_groups NULL ++#endif ++ + /* + * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined + * @wdd: the watchdog device to do the ioctl on +@@ -584,6 +697,7 @@ int watchdog_dev_unregister(struct watch + static struct class watchdog_class = { + .name = "watchdog", + .owner = THIS_MODULE, ++ .dev_groups = wdt_groups, + }; + + /* diff --git a/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch new file mode 100644 index 000000000..059ebdd82 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch @@ -0,0 +1,261 @@ +From 32ecc6392654a0db34b310e8924b5b2c3b8bf503 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 25 Dec 2015 16:01:40 -0800 +Subject: watchdog: Create watchdog device in watchdog_dev.c + +The watchdog character device is currently created in watchdog_dev.c, +and the watchdog device in watchdog_core.c. This results in +cross-dependencies, since device creation needs to know the watchdog +character device number as well as the watchdog class, both of which +reside in watchdog_dev.c. + +Create the watchdog device in watchdog_dev.c to simplify the code. + +Inspired by earlier patch set from Damien Riegel. + +Cc: Damien Riegel +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/watchdog_core.c | 33 ++++-------------- + drivers/watchdog/watchdog_core.h | 4 +-- + drivers/watchdog/watchdog_dev.c | 73 +++++++++++++++++++++++++++++++++------- + 3 files changed, 69 insertions(+), 41 deletions(-) + +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -42,7 +42,6 @@ + #include "watchdog_core.h" /* For watchdog_dev_register/... */ + + static DEFINE_IDA(watchdog_ida); +-static struct class *watchdog_class; + + /* + * Deferred Registration infrastructure. +@@ -194,7 +193,7 @@ EXPORT_SYMBOL_GPL(watchdog_set_restart_p + + static int __watchdog_register_device(struct watchdog_device *wdd) + { +- int ret, id = -1, devno; ++ int ret, id = -1; + + if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) + return -EINVAL; +@@ -247,16 +246,6 @@ static int __watchdog_register_device(st + } + } + +- devno = wdd->cdev.dev; +- wdd->dev = device_create(watchdog_class, wdd->parent, devno, +- wdd, "watchdog%d", wdd->id); +- if (IS_ERR(wdd->dev)) { +- watchdog_dev_unregister(wdd); +- ida_simple_remove(&watchdog_ida, id); +- ret = PTR_ERR(wdd->dev); +- return ret; +- } +- + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { + wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; + +@@ -265,9 +254,7 @@ static int __watchdog_register_device(st + dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", + ret); + watchdog_dev_unregister(wdd); +- device_destroy(watchdog_class, devno); + ida_simple_remove(&watchdog_ida, wdd->id); +- wdd->dev = NULL; + return ret; + } + } +@@ -311,9 +298,6 @@ EXPORT_SYMBOL_GPL(watchdog_register_devi + + static void __watchdog_unregister_device(struct watchdog_device *wdd) + { +- int ret; +- int devno; +- + if (wdd == NULL) + return; + +@@ -323,13 +307,8 @@ static void __watchdog_unregister_device + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) + unregister_reboot_notifier(&wdd->reboot_nb); + +- devno = wdd->cdev.dev; +- ret = watchdog_dev_unregister(wdd); +- if (ret) +- pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); +- device_destroy(watchdog_class, devno); ++ watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, wdd->id); +- wdd->dev = NULL; + } + + /** +@@ -370,9 +349,11 @@ static int __init watchdog_deferred_regi + + static int __init watchdog_init(void) + { +- watchdog_class = watchdog_dev_init(); +- if (IS_ERR(watchdog_class)) +- return PTR_ERR(watchdog_class); ++ int err; ++ ++ err = watchdog_dev_init(); ++ if (err < 0) ++ return err; + + watchdog_deferred_registration(); + return 0; +--- a/drivers/watchdog/watchdog_core.h ++++ b/drivers/watchdog/watchdog_core.h +@@ -32,6 +32,6 @@ + * Functions/procedures to be called by the core + */ + extern int watchdog_dev_register(struct watchdog_device *); +-extern int watchdog_dev_unregister(struct watchdog_device *); +-extern struct class * __init watchdog_dev_init(void); ++extern void watchdog_dev_unregister(struct watchdog_device *); ++extern int __init watchdog_dev_init(void); + extern void __exit watchdog_dev_exit(void); +--- a/drivers/watchdog/watchdog_dev.c ++++ b/drivers/watchdog/watchdog_dev.c +@@ -628,17 +628,18 @@ static struct miscdevice watchdog_miscde + }; + + /* +- * watchdog_dev_register: register a watchdog device ++ * watchdog_cdev_register: register watchdog character device + * @wdd: watchdog device ++ * @devno: character device number + * +- * Register a watchdog device including handling the legacy ++ * Register a watchdog character device including handling the legacy + * /dev/watchdog node. /dev/watchdog is actually a miscdevice and + * thus we set it up like that. + */ + +-int watchdog_dev_register(struct watchdog_device *wdd) ++static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) + { +- int err, devno; ++ int err; + + if (wdd->id == 0) { + old_wdd = wdd; +@@ -656,7 +657,6 @@ int watchdog_dev_register(struct watchdo + } + + /* Fill in the data structures */ +- devno = MKDEV(MAJOR(watchdog_devt), wdd->id); + cdev_init(&wdd->cdev, &watchdog_fops); + wdd->cdev.owner = wdd->ops->owner; + +@@ -674,13 +674,14 @@ int watchdog_dev_register(struct watchdo + } + + /* +- * watchdog_dev_unregister: unregister a watchdog device ++ * watchdog_cdev_unregister: unregister watchdog character device + * @watchdog: watchdog device + * +- * Unregister the watchdog and if needed the legacy /dev/watchdog device. ++ * Unregister watchdog character device and if needed the legacy ++ * /dev/watchdog device. + */ + +-int watchdog_dev_unregister(struct watchdog_device *wdd) ++static void watchdog_cdev_unregister(struct watchdog_device *wdd) + { + mutex_lock(&wdd->lock); + set_bit(WDOG_UNREGISTERED, &wdd->status); +@@ -691,7 +692,6 @@ int watchdog_dev_unregister(struct watch + misc_deregister(&watchdog_miscdev); + old_wdd = NULL; + } +- return 0; + } + + static struct class watchdog_class = { +@@ -701,29 +701,76 @@ static struct class watchdog_class = { + }; + + /* ++ * watchdog_dev_register: register a watchdog device ++ * @wdd: watchdog device ++ * ++ * Register a watchdog device including handling the legacy ++ * /dev/watchdog node. /dev/watchdog is actually a miscdevice and ++ * thus we set it up like that. ++ */ ++ ++int watchdog_dev_register(struct watchdog_device *wdd) ++{ ++ struct device *dev; ++ dev_t devno; ++ int ret; ++ ++ devno = MKDEV(MAJOR(watchdog_devt), wdd->id); ++ ++ ret = watchdog_cdev_register(wdd, devno); ++ if (ret) ++ return ret; ++ ++ dev = device_create(&watchdog_class, wdd->parent, devno, wdd, ++ "watchdog%d", wdd->id); ++ if (IS_ERR(dev)) { ++ watchdog_cdev_unregister(wdd); ++ return PTR_ERR(dev); ++ } ++ wdd->dev = dev; ++ ++ return ret; ++} ++ ++/* ++ * watchdog_dev_unregister: unregister a watchdog device ++ * @watchdog: watchdog device ++ * ++ * Unregister watchdog device and if needed the legacy ++ * /dev/watchdog device. ++ */ ++ ++void watchdog_dev_unregister(struct watchdog_device *wdd) ++{ ++ watchdog_cdev_unregister(wdd); ++ device_destroy(&watchdog_class, wdd->dev->devt); ++ wdd->dev = NULL; ++} ++ ++/* + * watchdog_dev_init: init dev part of watchdog core + * + * Allocate a range of chardev nodes to use for watchdog devices + */ + +-struct class * __init watchdog_dev_init(void) ++int __init watchdog_dev_init(void) + { + int err; + + err = class_register(&watchdog_class); + if (err < 0) { + pr_err("couldn't register class\n"); +- return ERR_PTR(err); ++ return err; + } + + err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); + if (err < 0) { + pr_err("watchdog: unable to allocate char dev region\n"); + class_unregister(&watchdog_class); +- return ERR_PTR(err); ++ return err; + } + +- return &watchdog_class; ++ return 0; + } + + /* diff --git a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch new file mode 100644 index 000000000..214dabfb9 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch @@ -0,0 +1,969 @@ +From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 25 Dec 2015 16:01:42 -0800 +Subject: watchdog: Separate and maintain variables based on variable lifetime + +All variables required by the watchdog core to manage a watchdog are +currently stored in struct watchdog_device. The lifetime of those +variables is determined by the watchdog driver. However, the lifetime +of variables used by the watchdog core differs from the lifetime of +struct watchdog_device. To remedy this situation, watchdog drivers +can implement ref and unref callbacks, to be used by the watchdog +core to lock struct watchdog_device in memory. + +While this solves the immediate problem, it depends on watchdog drivers +to actually implement the ref/unref callbacks. This is error prone, +often not implemented in the first place, or not implemented correctly. + +To solve the problem without requiring driver support, split the variables +in struct watchdog_device into two data structures - one for variables +associated with the watchdog driver, one for variables associated with +the watchdog core. With this approach, the watchdog core can keep track +of its variable lifetime and no longer depends on ref/unref callbacks +in the driver. As a side effect, some of the variables originally in +struct watchdog_driver are now private to the watchdog core and no longer +visible in watchdog drivers. + +As a side effect of the changes made, an ioctl will now always fail +with -ENODEV after a watchdog device was unregistered with the character +device still open. Previously, it would only fail with -ENODEV in some +situations. Also, ioctl operations are now atomic from driver perspective. +With this change, it is now guaranteed that the driver will not unregister +a watchdog between a timeout change and the subsequent ping. + +The 'ref' and 'unref' callbacks in struct watchdog_driver are no longer +used and marked as deprecated. + +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + Documentation/watchdog/watchdog-kernel-api.txt | 45 +-- + drivers/watchdog/watchdog_core.c | 2 - + drivers/watchdog/watchdog_dev.c | 383 +++++++++++++------------ + include/linux/watchdog.h | 22 +- + 4 files changed, 218 insertions(+), 234 deletions(-) + +--- a/Documentation/watchdog/watchdog-kernel-api.txt ++++ b/Documentation/watchdog/watchdog-kernel-api.txt +@@ -44,7 +44,6 @@ The watchdog device structure looks like + + struct watchdog_device { + int id; +- struct cdev cdev; + struct device *dev; + struct device *parent; + const struct watchdog_info *info; +@@ -56,7 +55,7 @@ struct watchdog_device { + struct notifier_block reboot_nb; + struct notifier_block restart_nb; + void *driver_data; +- struct mutex lock; ++ struct watchdog_core_data *wd_data; + unsigned long status; + struct list_head deferred; + }; +@@ -66,8 +65,6 @@ It contains following fields: + /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old + /dev/watchdog miscdev. The id is set automatically when calling + watchdog_register_device. +-* cdev: cdev for the dynamic /dev/watchdog device nodes. This +- field is also populated by watchdog_register_device. + * dev: device under the watchdog class (created by watchdog_register_device). + * parent: set this to the parent device (or NULL) before calling + watchdog_register_device. +@@ -89,11 +86,10 @@ It contains following fields: + * driver_data: a pointer to the drivers private data of a watchdog device. + This data should only be accessed via the watchdog_set_drvdata and + watchdog_get_drvdata routines. +-* lock: Mutex for WatchDog Timer Driver Core internal use only. ++* wd_data: a pointer to watchdog core internal data. + * status: this field contains a number of status bits that give extra + information about the status of the device (Like: is the watchdog timer +- running/active, is the nowayout bit set, is the device opened via +- the /dev/watchdog interface or not, ...). ++ running/active, or is the nowayout bit set). + * deferred: entry in wtd_deferred_reg_list which is used to + register early initialized watchdogs. + +@@ -110,8 +106,8 @@ struct watchdog_ops { + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); + int (*restart)(struct watchdog_device *); +- void (*ref)(struct watchdog_device *); +- void (*unref)(struct watchdog_device *); ++ void (*ref)(struct watchdog_device *) __deprecated; ++ void (*unref)(struct watchdog_device *) __deprecated; + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + }; + +@@ -120,20 +116,6 @@ driver's operations. This module owner w + the watchdog is active. (This to avoid a system crash when you unload the + module and /dev/watchdog is still open). + +-If the watchdog_device struct is dynamically allocated, just locking the module +-is not enough and a driver also needs to define the ref and unref operations to +-ensure the structure holding the watchdog_device does not go away. +- +-The simplest (and usually sufficient) implementation of this is to: +-1) Add a kref struct to the same structure which is holding the watchdog_device +-2) Define a release callback for the kref which frees the struct holding both +-3) Call kref_init on this kref *before* calling watchdog_register_device() +-4) Define a ref operation calling kref_get on this kref +-5) Define a unref operation calling kref_put on this kref +-6) When it is time to cleanup: +- * Do not kfree() the struct holding both, the last kref_put will do this! +- * *After* calling watchdog_unregister_device() call kref_put on the kref +- + Some operations are mandatory and some are optional. The mandatory operations + are: + * start: this is a pointer to the routine that starts the watchdog timer +@@ -176,34 +158,21 @@ they are supported. These optional routi + * get_timeleft: this routines returns the time that's left before a reset. + * restart: this routine restarts the machine. It returns 0 on success or a + negative errno code for failure. +-* ref: the operation that calls kref_get on the kref of a dynamically +- allocated watchdog_device struct. +-* unref: the operation that calls kref_put on the kref of a dynamically +- allocated watchdog_device struct. + * ioctl: if this routine is present then it will be called first before we do + our own internal ioctl call handling. This routine should return -ENOIOCTLCMD + if a command is not supported. The parameters that are passed to the ioctl + call are: watchdog_device, cmd and arg. + ++The 'ref' and 'unref' operations are no longer used and deprecated. ++ + The status bits should (preferably) be set with the set_bit and clear_bit alike + bit-operations. The status bits that are defined are: + * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device + is active or not. When the watchdog is active after booting, then you should + set this status bit (Note: when you register the watchdog timer device with + this bit set, then opening /dev/watchdog will skip the start operation) +-* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device +- was opened via /dev/watchdog. +- (This bit should only be used by the WatchDog Timer Driver Core). +-* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character +- has been sent (so that we can support the magic close feature). +- (This bit should only be used by the WatchDog Timer Driver Core). + * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. + If this bit is set then the watchdog timer will not be able to stop. +-* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core +- after calling watchdog_unregister_device, and then checked before calling +- any watchdog_ops, so that you can be sure that no operations (other then +- unref) will get called after unregister, even if userspace still holds a +- reference to /dev/watchdog + + To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog + timer device) you can either: +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -210,8 +210,6 @@ static int __watchdog_register_device(st + * corrupted in a later stage then we expect a kernel panic! + */ + +- mutex_init(&wdd->lock); +- + /* Use alias for watchdog id if possible */ + if (wdd->parent) { + ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); +--- a/drivers/watchdog/watchdog_dev.c ++++ b/drivers/watchdog/watchdog_dev.c +@@ -32,27 +32,51 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +-#include /* For module stuff/... */ +-#include /* For standard types (like size_t) */ ++#include /* For character device */ + #include /* For the -ENODEV/... values */ +-#include /* For printk/panic/... */ + #include /* For file operations */ +-#include /* For watchdog specific items */ +-#include /* For handling misc devices */ + #include /* For __init/__exit/... */ ++#include /* For printk/panic/... */ ++#include /* For data references */ ++#include /* For handling misc devices */ ++#include /* For module stuff/... */ ++#include /* For mutexes */ ++#include /* For memory functions */ ++#include /* For standard types (like size_t) */ ++#include /* For watchdog specific items */ + #include /* For copy_to_user/put_user/... */ + + #include "watchdog_core.h" + ++/* ++ * struct watchdog_core_data - watchdog core internal data ++ * @kref: Reference count. ++ * @cdev: The watchdog's Character device. ++ * @wdd: Pointer to watchdog device. ++ * @lock: Lock for watchdog core. ++ * @status: Watchdog core internal status bits. ++ */ ++struct watchdog_core_data { ++ struct kref kref; ++ struct cdev cdev; ++ struct watchdog_device *wdd; ++ struct mutex lock; ++ unsigned long status; /* Internal status bits */ ++#define _WDOG_DEV_OPEN 0 /* Opened ? */ ++#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ ++}; ++ + /* the dev_t structure to store the dynamically allocated watchdog devices */ + static dev_t watchdog_devt; +-/* the watchdog device behind /dev/watchdog */ +-static struct watchdog_device *old_wdd; ++/* Reference to watchdog device behind /dev/watchdog */ ++static struct watchdog_core_data *old_wd_data; + + /* + * watchdog_ping: ping the watchdog. + * @wdd: the watchdog device to ping + * ++ * The caller must hold wd_data->lock. ++ * + * If the watchdog has no own ping operation then it needs to be + * restarted via the start operation. This wrapper function does + * exactly that. +@@ -61,25 +85,16 @@ static struct watchdog_device *old_wdd; + + static int watchdog_ping(struct watchdog_device *wdd) + { +- int err = 0; +- +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_ping; +- } ++ int err; + + if (!watchdog_active(wdd)) +- goto out_ping; ++ return 0; + + if (wdd->ops->ping) + err = wdd->ops->ping(wdd); /* ping the watchdog */ + else + err = wdd->ops->start(wdd); /* restart watchdog */ + +-out_ping: +- mutex_unlock(&wdd->lock); + return err; + } + +@@ -87,6 +102,8 @@ out_ping: + * watchdog_start: wrapper to start the watchdog. + * @wdd: the watchdog device to start + * ++ * The caller must hold wd_data->lock. ++ * + * Start the watchdog if it is not active and mark it active. + * This function returns zero on success or a negative errno code for + * failure. +@@ -94,24 +111,15 @@ out_ping: + + static int watchdog_start(struct watchdog_device *wdd) + { +- int err = 0; +- +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_start; +- } ++ int err; + + if (watchdog_active(wdd)) +- goto out_start; ++ return 0; + + err = wdd->ops->start(wdd); + if (err == 0) + set_bit(WDOG_ACTIVE, &wdd->status); + +-out_start: +- mutex_unlock(&wdd->lock); + return err; + } + +@@ -119,6 +127,8 @@ out_start: + * watchdog_stop: wrapper to stop the watchdog. + * @wdd: the watchdog device to stop + * ++ * The caller must hold wd_data->lock. ++ * + * Stop the watchdog if it is still active and unmark it active. + * This function returns zero on success or a negative errno code for + * failure. +@@ -127,93 +137,58 @@ out_start: + + static int watchdog_stop(struct watchdog_device *wdd) + { +- int err = 0; +- +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_stop; +- } ++ int err; + + if (!watchdog_active(wdd)) +- goto out_stop; ++ return 0; + + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { + dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); +- err = -EBUSY; +- goto out_stop; ++ return -EBUSY; + } + + err = wdd->ops->stop(wdd); + if (err == 0) + clear_bit(WDOG_ACTIVE, &wdd->status); + +-out_stop: +- mutex_unlock(&wdd->lock); + return err; + } + + /* + * watchdog_get_status: wrapper to get the watchdog status + * @wdd: the watchdog device to get the status from +- * @status: the status of the watchdog device ++ * ++ * The caller must hold wd_data->lock. + * + * Get the watchdog's status flags. + */ + +-static int watchdog_get_status(struct watchdog_device *wdd, +- unsigned int *status) ++static unsigned int watchdog_get_status(struct watchdog_device *wdd) + { +- int err = 0; +- +- *status = 0; + if (!wdd->ops->status) +- return -EOPNOTSUPP; +- +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_status; +- } +- +- *status = wdd->ops->status(wdd); ++ return 0; + +-out_status: +- mutex_unlock(&wdd->lock); +- return err; ++ return wdd->ops->status(wdd); + } + + /* + * watchdog_set_timeout: set the watchdog timer timeout + * @wdd: the watchdog device to set the timeout for + * @timeout: timeout to set in seconds ++ * ++ * The caller must hold wd_data->lock. + */ + + static int watchdog_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) + { +- int err; +- + if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) + return -EOPNOTSUPP; + + if (watchdog_timeout_invalid(wdd, timeout)) + return -EINVAL; + +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_timeout; +- } +- +- err = wdd->ops->set_timeout(wdd, timeout); +- +-out_timeout: +- mutex_unlock(&wdd->lock); +- return err; ++ return wdd->ops->set_timeout(wdd, timeout); + } + + /* +@@ -221,30 +196,22 @@ out_timeout: + * @wdd: the watchdog device to get the remaining time from + * @timeleft: the time that's left + * ++ * The caller must hold wd_data->lock. ++ * + * Get the time before a watchdog will reboot (if not pinged). + */ + + static int watchdog_get_timeleft(struct watchdog_device *wdd, + unsigned int *timeleft) + { +- int err = 0; +- + *timeleft = 0; ++ + if (!wdd->ops->get_timeleft) + return -EOPNOTSUPP; + +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_timeleft; +- } +- + *timeleft = wdd->ops->get_timeleft(wdd); + +-out_timeleft: +- mutex_unlock(&wdd->lock); +- return err; ++ return 0; + } + + #ifdef CONFIG_WATCHDOG_SYSFS +@@ -261,14 +228,14 @@ static ssize_t status_show(struct device + char *buf) + { + struct watchdog_device *wdd = dev_get_drvdata(dev); +- ssize_t status; +- unsigned int val; ++ struct watchdog_core_data *wd_data = wdd->wd_data; ++ unsigned int status; + +- status = watchdog_get_status(wdd, &val); +- if (!status) +- status = sprintf(buf, "%u\n", val); ++ mutex_lock(&wd_data->lock); ++ status = watchdog_get_status(wdd); ++ mutex_unlock(&wd_data->lock); + +- return status; ++ return sprintf(buf, "%u\n", status); + } + static DEVICE_ATTR_RO(status); + +@@ -285,10 +252,13 @@ static ssize_t timeleft_show(struct devi + char *buf) + { + struct watchdog_device *wdd = dev_get_drvdata(dev); ++ struct watchdog_core_data *wd_data = wdd->wd_data; + ssize_t status; + unsigned int val; + ++ mutex_lock(&wd_data->lock); + status = watchdog_get_timeleft(wdd, &val); ++ mutex_unlock(&wd_data->lock); + if (!status) + status = sprintf(buf, "%u\n", val); + +@@ -365,28 +335,17 @@ __ATTRIBUTE_GROUPS(wdt); + * @wdd: the watchdog device to do the ioctl on + * @cmd: watchdog command + * @arg: argument pointer ++ * ++ * The caller must hold wd_data->lock. + */ + + static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, + unsigned long arg) + { +- int err; +- + if (!wdd->ops->ioctl) + return -ENOIOCTLCMD; + +- mutex_lock(&wdd->lock); +- +- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { +- err = -ENODEV; +- goto out_ioctl; +- } +- +- err = wdd->ops->ioctl(wdd, cmd, arg); +- +-out_ioctl: +- mutex_unlock(&wdd->lock); +- return err; ++ return wdd->ops->ioctl(wdd, cmd, arg); + } + + /* +@@ -404,10 +363,11 @@ out_ioctl: + static ssize_t watchdog_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) + { +- struct watchdog_device *wdd = file->private_data; ++ struct watchdog_core_data *wd_data = file->private_data; ++ struct watchdog_device *wdd; ++ int err; + size_t i; + char c; +- int err; + + if (len == 0) + return 0; +@@ -416,18 +376,25 @@ static ssize_t watchdog_write(struct fil + * Note: just in case someone wrote the magic character + * five months ago... + */ +- clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); ++ clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != len; i++) { + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') +- set_bit(WDOG_ALLOW_RELEASE, &wdd->status); ++ set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); + } + + /* someone wrote to us, so we send the watchdog a keepalive ping */ +- err = watchdog_ping(wdd); ++ ++ err = -ENODEV; ++ mutex_lock(&wd_data->lock); ++ wdd = wd_data->wdd; ++ if (wdd) ++ err = watchdog_ping(wdd); ++ mutex_unlock(&wd_data->lock); ++ + if (err < 0) + return err; + +@@ -447,71 +414,94 @@ static ssize_t watchdog_write(struct fil + static long watchdog_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) + { +- struct watchdog_device *wdd = file->private_data; ++ struct watchdog_core_data *wd_data = file->private_data; + void __user *argp = (void __user *)arg; ++ struct watchdog_device *wdd; + int __user *p = argp; + unsigned int val; + int err; + ++ mutex_lock(&wd_data->lock); ++ ++ wdd = wd_data->wdd; ++ if (!wdd) { ++ err = -ENODEV; ++ goto out_ioctl; ++ } ++ + err = watchdog_ioctl_op(wdd, cmd, arg); + if (err != -ENOIOCTLCMD) +- return err; ++ goto out_ioctl; + + switch (cmd) { + case WDIOC_GETSUPPORT: +- return copy_to_user(argp, wdd->info, ++ err = copy_to_user(argp, wdd->info, + sizeof(struct watchdog_info)) ? -EFAULT : 0; ++ break; + case WDIOC_GETSTATUS: +- err = watchdog_get_status(wdd, &val); +- if (err == -ENODEV) +- return err; +- return put_user(val, p); ++ val = watchdog_get_status(wdd); ++ err = put_user(val, p); ++ break; + case WDIOC_GETBOOTSTATUS: +- return put_user(wdd->bootstatus, p); ++ err = put_user(wdd->bootstatus, p); ++ break; + case WDIOC_SETOPTIONS: +- if (get_user(val, p)) +- return -EFAULT; ++ if (get_user(val, p)) { ++ err = -EFAULT; ++ break; ++ } + if (val & WDIOS_DISABLECARD) { + err = watchdog_stop(wdd); + if (err < 0) +- return err; ++ break; + } +- if (val & WDIOS_ENABLECARD) { ++ if (val & WDIOS_ENABLECARD) + err = watchdog_start(wdd); +- if (err < 0) +- return err; +- } +- return 0; ++ break; + case WDIOC_KEEPALIVE: +- if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) +- return -EOPNOTSUPP; +- return watchdog_ping(wdd); ++ if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { ++ err = -EOPNOTSUPP; ++ break; ++ } ++ err = watchdog_ping(wdd); ++ break; + case WDIOC_SETTIMEOUT: +- if (get_user(val, p)) +- return -EFAULT; ++ if (get_user(val, p)) { ++ err = -EFAULT; ++ break; ++ } + err = watchdog_set_timeout(wdd, val); + if (err < 0) +- return err; ++ break; + /* If the watchdog is active then we send a keepalive ping + * to make sure that the watchdog keep's running (and if + * possible that it takes the new timeout) */ + err = watchdog_ping(wdd); + if (err < 0) +- return err; ++ break; + /* Fall */ + case WDIOC_GETTIMEOUT: + /* timeout == 0 means that we don't know the timeout */ +- if (wdd->timeout == 0) +- return -EOPNOTSUPP; +- return put_user(wdd->timeout, p); ++ if (wdd->timeout == 0) { ++ err = -EOPNOTSUPP; ++ break; ++ } ++ err = put_user(wdd->timeout, p); ++ break; + case WDIOC_GETTIMELEFT: + err = watchdog_get_timeleft(wdd, &val); +- if (err) +- return err; +- return put_user(val, p); ++ if (err < 0) ++ break; ++ err = put_user(val, p); ++ break; + default: +- return -ENOTTY; ++ err = -ENOTTY; ++ break; + } ++ ++out_ioctl: ++ mutex_unlock(&wd_data->lock); ++ return err; + } + + /* +@@ -526,45 +516,59 @@ static long watchdog_ioctl(struct file * + + static int watchdog_open(struct inode *inode, struct file *file) + { +- int err = -EBUSY; ++ struct watchdog_core_data *wd_data; + struct watchdog_device *wdd; ++ int err; + + /* Get the corresponding watchdog device */ + if (imajor(inode) == MISC_MAJOR) +- wdd = old_wdd; ++ wd_data = old_wd_data; + else +- wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); ++ wd_data = container_of(inode->i_cdev, struct watchdog_core_data, ++ cdev); + + /* the watchdog is single open! */ +- if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) ++ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) + return -EBUSY; + ++ wdd = wd_data->wdd; ++ + /* + * If the /dev/watchdog device is open, we don't want the module + * to be unloaded. + */ +- if (!try_module_get(wdd->ops->owner)) +- goto out; ++ if (!try_module_get(wdd->ops->owner)) { ++ err = -EBUSY; ++ goto out_clear; ++ } + + err = watchdog_start(wdd); + if (err < 0) + goto out_mod; + +- file->private_data = wdd; ++ file->private_data = wd_data; + +- if (wdd->ops->ref) +- wdd->ops->ref(wdd); ++ kref_get(&wd_data->kref); + + /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ + return nonseekable_open(inode, file); + + out_mod: +- module_put(wdd->ops->owner); +-out: +- clear_bit(WDOG_DEV_OPEN, &wdd->status); ++ module_put(wd_data->wdd->ops->owner); ++out_clear: ++ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); + return err; + } + ++static void watchdog_core_data_release(struct kref *kref) ++{ ++ struct watchdog_core_data *wd_data; ++ ++ wd_data = container_of(kref, struct watchdog_core_data, kref); ++ ++ kfree(wd_data); ++} ++ + /* + * watchdog_release: release the watchdog device. + * @inode: inode of device +@@ -577,9 +581,16 @@ out: + + static int watchdog_release(struct inode *inode, struct file *file) + { +- struct watchdog_device *wdd = file->private_data; ++ struct watchdog_core_data *wd_data = file->private_data; ++ struct watchdog_device *wdd; + int err = -EBUSY; + ++ mutex_lock(&wd_data->lock); ++ ++ wdd = wd_data->wdd; ++ if (!wdd) ++ goto done; ++ + /* + * We only stop the watchdog if we received the magic character + * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then +@@ -587,29 +598,24 @@ static int watchdog_release(struct inode + */ + if (!test_bit(WDOG_ACTIVE, &wdd->status)) + err = 0; +- else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || ++ else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || + !(wdd->info->options & WDIOF_MAGICCLOSE)) + err = watchdog_stop(wdd); + + /* If the watchdog was not stopped, send a keepalive ping */ + if (err < 0) { +- mutex_lock(&wdd->lock); +- if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) +- dev_crit(wdd->dev, "watchdog did not stop!\n"); +- mutex_unlock(&wdd->lock); ++ dev_crit(wdd->dev, "watchdog did not stop!\n"); + watchdog_ping(wdd); + } + +- /* Allow the owner module to be unloaded again */ +- module_put(wdd->ops->owner); +- + /* make sure that /dev/watchdog can be re-opened */ +- clear_bit(WDOG_DEV_OPEN, &wdd->status); +- +- /* Note wdd may be gone after this, do not use after this! */ +- if (wdd->ops->unref) +- wdd->ops->unref(wdd); ++ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); + ++done: ++ mutex_unlock(&wd_data->lock); ++ /* Allow the owner module to be unloaded again */ ++ module_put(wd_data->cdev.owner); ++ kref_put(&wd_data->kref, watchdog_core_data_release); + return 0; + } + +@@ -639,10 +645,20 @@ static struct miscdevice watchdog_miscde + + static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) + { ++ struct watchdog_core_data *wd_data; + int err; + ++ wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); ++ if (!wd_data) ++ return -ENOMEM; ++ kref_init(&wd_data->kref); ++ mutex_init(&wd_data->lock); ++ ++ wd_data->wdd = wdd; ++ wdd->wd_data = wd_data; ++ + if (wdd->id == 0) { +- old_wdd = wdd; ++ old_wd_data = wd_data; + watchdog_miscdev.parent = wdd->parent; + err = misc_register(&watchdog_miscdev); + if (err != 0) { +@@ -651,23 +667,25 @@ static int watchdog_cdev_register(struct + if (err == -EBUSY) + pr_err("%s: a legacy watchdog module is probably present.\n", + wdd->info->identity); +- old_wdd = NULL; ++ old_wd_data = NULL; ++ kfree(wd_data); + return err; + } + } + + /* Fill in the data structures */ +- cdev_init(&wdd->cdev, &watchdog_fops); +- wdd->cdev.owner = wdd->ops->owner; ++ cdev_init(&wd_data->cdev, &watchdog_fops); ++ wd_data->cdev.owner = wdd->ops->owner; + + /* Add the device */ +- err = cdev_add(&wdd->cdev, devno, 1); ++ err = cdev_add(&wd_data->cdev, devno, 1); + if (err) { + pr_err("watchdog%d unable to add device %d:%d\n", + wdd->id, MAJOR(watchdog_devt), wdd->id); + if (wdd->id == 0) { + misc_deregister(&watchdog_miscdev); +- old_wdd = NULL; ++ old_wd_data = NULL; ++ kref_put(&wd_data->kref, watchdog_core_data_release); + } + } + return err; +@@ -683,15 +701,20 @@ static int watchdog_cdev_register(struct + + static void watchdog_cdev_unregister(struct watchdog_device *wdd) + { +- mutex_lock(&wdd->lock); +- set_bit(WDOG_UNREGISTERED, &wdd->status); +- mutex_unlock(&wdd->lock); ++ struct watchdog_core_data *wd_data = wdd->wd_data; + +- cdev_del(&wdd->cdev); ++ cdev_del(&wd_data->cdev); + if (wdd->id == 0) { + misc_deregister(&watchdog_miscdev); +- old_wdd = NULL; ++ old_wd_data = NULL; + } ++ ++ mutex_lock(&wd_data->lock); ++ wd_data->wdd = NULL; ++ wdd->wd_data = NULL; ++ mutex_unlock(&wd_data->lock); ++ ++ kref_put(&wd_data->kref, watchdog_core_data_release); + } + + static struct class watchdog_class = { +@@ -742,9 +765,9 @@ int watchdog_dev_register(struct watchdo + + void watchdog_dev_unregister(struct watchdog_device *wdd) + { +- watchdog_cdev_unregister(wdd); + device_destroy(&watchdog_class, wdd->dev->devt); + wdd->dev = NULL; ++ watchdog_cdev_unregister(wdd); + } + + /* +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -17,6 +17,7 @@ + + struct watchdog_ops; + struct watchdog_device; ++struct watchdog_core_data; + + /** struct watchdog_ops - The watchdog-devices operations + * +@@ -28,8 +29,6 @@ struct watchdog_device; + * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). + * @get_timeleft:The routine that gets the time left before a reset (in seconds). + * @restart: The routine for restarting the machine. +- * @ref: The ref operation for dyn. allocated watchdog_device structs +- * @unref: The unref operation for dyn. allocated watchdog_device structs + * @ioctl: The routines that handles extra ioctl calls. + * + * The watchdog_ops structure contains a list of low-level operations +@@ -48,15 +47,14 @@ struct watchdog_ops { + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); + int (*restart)(struct watchdog_device *); +- void (*ref)(struct watchdog_device *); +- void (*unref)(struct watchdog_device *); ++ void (*ref)(struct watchdog_device *) __deprecated; ++ void (*unref)(struct watchdog_device *) __deprecated; + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + }; + + /** struct watchdog_device - The structure that defines a watchdog device + * + * @id: The watchdog's ID. (Allocated by watchdog_register_device) +- * @cdev: The watchdog's Character device. + * @dev: The device for our watchdog + * @parent: The parent bus device + * @info: Pointer to a watchdog_info structure. +@@ -67,8 +65,8 @@ struct watchdog_ops { + * @max_timeout:The watchdog devices maximum timeout value (in seconds). + * @reboot_nb: The notifier block to stop watchdog on reboot. + * @restart_nb: The notifier block to register a restart function. +- * @driver-data:Pointer to the drivers private data. +- * @lock: Lock for watchdog core internal use only. ++ * @driver_data:Pointer to the drivers private data. ++ * @wd_data: Pointer to watchdog core internal data. + * @status: Field that contains the devices internal status bits. + * @deferred: entry in wtd_deferred_reg_list which is used to + * register early initialized watchdogs. +@@ -84,7 +82,6 @@ struct watchdog_ops { + */ + struct watchdog_device { + int id; +- struct cdev cdev; + struct device *dev; + struct device *parent; + const struct watchdog_info *info; +@@ -96,15 +93,12 @@ struct watchdog_device { + struct notifier_block reboot_nb; + struct notifier_block restart_nb; + void *driver_data; +- struct mutex lock; ++ struct watchdog_core_data *wd_data; + unsigned long status; + /* Bit numbers for status flags */ + #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ +-#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ +-#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ +-#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ +-#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ +-#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */ ++#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ ++#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ + struct list_head deferred; + }; + diff --git a/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch new file mode 100644 index 000000000..9640cd475 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch @@ -0,0 +1,117 @@ +From 0254e953537c92df3e7d0176f401a211e944fd61 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Sun, 3 Jan 2016 15:11:58 -0800 +Subject: watchdog: Drop pointer to watchdog device from struct watchdog_device + +The lifetime of the watchdog device pointer is different from the lifetime +of its character device. Remove it entirely to avoid race conditions. + +Signed-off-by: Guenter Roeck +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + Documentation/watchdog/watchdog-kernel-api.txt | 2 -- + drivers/watchdog/watchdog_core.c | 8 ++++---- + drivers/watchdog/watchdog_dev.c | 9 ++++----- + include/linux/watchdog.h | 2 -- + 4 files changed, 8 insertions(+), 13 deletions(-) + +--- a/Documentation/watchdog/watchdog-kernel-api.txt ++++ b/Documentation/watchdog/watchdog-kernel-api.txt +@@ -44,7 +44,6 @@ The watchdog device structure looks like + + struct watchdog_device { + int id; +- struct device *dev; + struct device *parent; + const struct watchdog_info *info; + const struct watchdog_ops *ops; +@@ -65,7 +64,6 @@ It contains following fields: + /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old + /dev/watchdog miscdev. The id is set automatically when calling + watchdog_register_device. +-* dev: device under the watchdog class (created by watchdog_register_device). + * parent: set this to the parent device (or NULL) before calling + watchdog_register_device. + * info: a pointer to a watchdog_info structure. This structure gives some +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -249,8 +249,8 @@ static int __watchdog_register_device(st + + ret = register_reboot_notifier(&wdd->reboot_nb); + if (ret) { +- dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n", +- ret); ++ pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", ++ wdd->id, ret); + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, wdd->id); + return ret; +@@ -262,8 +262,8 @@ static int __watchdog_register_device(st + + ret = register_restart_handler(&wdd->restart_nb); + if (ret) +- dev_warn(wdd->dev, "Cannot register restart handler (%d)\n", +- ret); ++ pr_warn("watchog%d: Cannot register restart handler (%d)\n", ++ wdd->id, ret); + } + + return 0; +--- a/drivers/watchdog/watchdog_dev.c ++++ b/drivers/watchdog/watchdog_dev.c +@@ -143,7 +143,8 @@ static int watchdog_stop(struct watchdog + return 0; + + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { +- dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); ++ pr_info("watchdog%d: nowayout prevents watchdog being stopped!\n", ++ wdd->id); + return -EBUSY; + } + +@@ -604,7 +605,7 @@ static int watchdog_release(struct inode + + /* If the watchdog was not stopped, send a keepalive ping */ + if (err < 0) { +- dev_crit(wdd->dev, "watchdog did not stop!\n"); ++ pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id); + watchdog_ping(wdd); + } + +@@ -750,7 +751,6 @@ int watchdog_dev_register(struct watchdo + watchdog_cdev_unregister(wdd); + return PTR_ERR(dev); + } +- wdd->dev = dev; + + return ret; + } +@@ -765,8 +765,7 @@ int watchdog_dev_register(struct watchdo + + void watchdog_dev_unregister(struct watchdog_device *wdd) + { +- device_destroy(&watchdog_class, wdd->dev->devt); +- wdd->dev = NULL; ++ device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); + watchdog_cdev_unregister(wdd); + } + +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -55,7 +55,6 @@ struct watchdog_ops { + /** struct watchdog_device - The structure that defines a watchdog device + * + * @id: The watchdog's ID. (Allocated by watchdog_register_device) +- * @dev: The device for our watchdog + * @parent: The parent bus device + * @info: Pointer to a watchdog_info structure. + * @ops: Pointer to the list of watchdog operations. +@@ -82,7 +81,6 @@ struct watchdog_ops { + */ + struct watchdog_device { + int id; +- struct device *dev; + struct device *parent; + const struct watchdog_info *info; + const struct watchdog_ops *ops; diff --git a/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch new file mode 100644 index 000000000..ba56a8653 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch @@ -0,0 +1,26 @@ +From 62cd1c40ce1c7c16835b599751c7a002eb5bbdf5 Mon Sep 17 00:00:00 2001 +From: Tomas Winkler +Date: Sun, 3 Jan 2016 13:32:37 +0200 +Subject: watchdog: kill unref/ref ops + +ref/unref ops are not called at all so even marked them as deprecated +is misleading, we need to just drop the API. + +Signed-off-by: Tomas Winkler +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + include/linux/watchdog.h | 2 -- + 1 file changed, 2 deletions(-) + +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -47,8 +47,6 @@ struct watchdog_ops { + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); + int (*restart)(struct watchdog_device *); +- void (*ref)(struct watchdog_device *) __deprecated; +- void (*unref)(struct watchdog_device *) __deprecated; + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + }; + diff --git a/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch new file mode 100644 index 000000000..d36dedcd1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch @@ -0,0 +1,113 @@ +From 80969a68ffed12f82e2a29908306ff43a6861a61 Mon Sep 17 00:00:00 2001 +From: Damien Riegel +Date: Mon, 16 Nov 2015 12:28:09 -0500 +Subject: watchdog: qcom-wdt: use core restart handler + +Get rid of the custom restart handler by using the one provided by the +watchdog core. + +Signed-off-by: Damien Riegel +Reviewed-by: Guenter Roeck +Reviewed-by: Vivien Didelot +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/qcom-wdt.c | 49 ++++++++++++++++++--------------------------- + 1 file changed, 19 insertions(+), 30 deletions(-) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -17,7 +17,6 @@ + #include + #include + #include +-#include + #include + + #define WDT_RST 0x38 +@@ -28,7 +27,6 @@ struct qcom_wdt { + struct watchdog_device wdd; + struct clk *clk; + unsigned long rate; +- struct notifier_block restart_nb; + void __iomem *base; + }; + +@@ -72,25 +70,9 @@ static int qcom_wdt_set_timeout(struct w + return qcom_wdt_start(wdd); + } + +-static const struct watchdog_ops qcom_wdt_ops = { +- .start = qcom_wdt_start, +- .stop = qcom_wdt_stop, +- .ping = qcom_wdt_ping, +- .set_timeout = qcom_wdt_set_timeout, +- .owner = THIS_MODULE, +-}; +- +-static const struct watchdog_info qcom_wdt_info = { +- .options = WDIOF_KEEPALIVEPING +- | WDIOF_MAGICCLOSE +- | WDIOF_SETTIMEOUT, +- .identity = KBUILD_MODNAME, +-}; +- +-static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, +- void *data) ++static int qcom_wdt_restart(struct watchdog_device *wdd) + { +- struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); ++ struct qcom_wdt *wdt = to_qcom_wdt(wdd); + u32 timeout; + + /* +@@ -110,9 +92,25 @@ static int qcom_wdt_restart(struct notif + wmb(); + + msleep(150); +- return NOTIFY_DONE; ++ return 0; + } + ++static const struct watchdog_ops qcom_wdt_ops = { ++ .start = qcom_wdt_start, ++ .stop = qcom_wdt_stop, ++ .ping = qcom_wdt_ping, ++ .set_timeout = qcom_wdt_set_timeout, ++ .restart = qcom_wdt_restart, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct watchdog_info qcom_wdt_info = { ++ .options = WDIOF_KEEPALIVEPING ++ | WDIOF_MAGICCLOSE ++ | WDIOF_SETTIMEOUT, ++ .identity = KBUILD_MODNAME, ++}; ++ + static int qcom_wdt_probe(struct platform_device *pdev) + { + struct qcom_wdt *wdt; +@@ -187,14 +185,6 @@ static int qcom_wdt_probe(struct platfor + goto err_clk_unprepare; + } + +- /* +- * WDT restart notifier has priority 0 (use as a last resort) +- */ +- wdt->restart_nb.notifier_call = qcom_wdt_restart; +- ret = register_restart_handler(&wdt->restart_nb); +- if (ret) +- dev_err(&pdev->dev, "failed to setup restart handler\n"); +- + platform_set_drvdata(pdev, wdt); + return 0; + +@@ -207,7 +197,6 @@ static int qcom_wdt_remove(struct platfo + { + struct qcom_wdt *wdt = platform_get_drvdata(pdev); + +- unregister_restart_handler(&wdt->restart_nb); + watchdog_unregister_device(&wdt->wdd); + clk_disable_unprepare(wdt->clk); + return 0; diff --git a/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch new file mode 100644 index 000000000..31371c2c8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch @@ -0,0 +1,25 @@ +From 0933b453f1c7104d873aacf8524f8ac380a7ed08 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Thu, 24 Dec 2015 14:22:04 -0800 +Subject: watchdog: qcom-wdt: Do not set 'dev' in struct watchdog_device + +The 'dev' pointer in struct watchdog_device is set by the watchdog core +when registering the watchdog device and not by the driver. It points to +the watchdog device, not its parent. + +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/qcom-wdt.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -164,7 +164,6 @@ static int qcom_wdt_probe(struct platfor + goto err_clk_unprepare; + } + +- wdt->wdd.dev = &pdev->dev; + wdt->wdd.info = &qcom_wdt_info; + wdt->wdd.ops = &qcom_wdt_ops; + wdt->wdd.min_timeout = 1; diff --git a/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch new file mode 100644 index 000000000..7ceff32ca --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch @@ -0,0 +1,51 @@ +rom 4d8b229d5ea610affe672e919021e9d02cd877da Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 26 Feb 2016 17:32:49 -0800 +Subject: watchdog: Add 'action' and 'data' parameters to restart handler + callback + +The 'action' (or restart mode) and data parameters may be used by restart +handlers, so they should be passed to the restart callback functions. + +Cc: Sylvain Lemieux +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/qcom-wdt.c | 3 ++- + drivers/watchdog/watchdog_core.c | 2 +- + include/linux/watchdog.h | 2 +- + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct w + return qcom_wdt_start(wdd); + } + +-static int qcom_wdt_restart(struct watchdog_device *wdd) ++static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, ++ void *data) + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + u32 timeout; +--- a/drivers/watchdog/watchdog_core.c ++++ b/drivers/watchdog/watchdog_core.c +@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(str + + int ret; + +- ret = wdd->ops->restart(wdd); ++ ret = wdd->ops->restart(wdd, action, data); + if (ret) + return NOTIFY_BAD; + +--- a/include/linux/watchdog.h ++++ b/include/linux/watchdog.h +@@ -46,7 +46,7 @@ struct watchdog_ops { + unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); +- int (*restart)(struct watchdog_device *); ++ int (*restart)(struct watchdog_device *, unsigned long, void *); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + }; + diff --git a/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch new file mode 100644 index 000000000..f7fcaeeda --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch @@ -0,0 +1,46 @@ +From b6ef36d2c1e391adc1fe1b2dd2a0f887a9f3052b Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Mon, 4 Apr 2016 17:37:46 -0700 +Subject: watchdog: qcom: Report reboot reason + +The Qualcom watchdog timer block reports if the system was reset by the +watchdog. Pass the information to user space. + +Reviewed-by: Grant Grundler +Tested-by: Grant Grundler +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/qcom-wdt.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -21,6 +21,7 @@ + + #define WDT_RST 0x38 + #define WDT_EN 0x40 ++#define WDT_STS 0x44 + #define WDT_BITE_TIME 0x5C + + struct qcom_wdt { +@@ -108,7 +109,8 @@ static const struct watchdog_ops qcom_wd + static const struct watchdog_info qcom_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE +- | WDIOF_SETTIMEOUT, ++ | WDIOF_SETTIMEOUT ++ | WDIOF_CARDRESET, + .identity = KBUILD_MODNAME, + }; + +@@ -171,6 +173,9 @@ static int qcom_wdt_probe(struct platfor + wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + wdt->wdd.parent = &pdev->dev; + ++ if (readl(wdt->base + WDT_STS) & 1) ++ wdt->wdd.bootstatus = WDIOF_CARDRESET; ++ + /* + * If 'timeout-sec' unspecified in devicetree, assume a 30 second + * default, unless the max timeout is less than 30 seconds, then use diff --git a/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch new file mode 100644 index 000000000..82dd87543 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch @@ -0,0 +1,162 @@ +From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001 +From: Matthew McClintock +Date: Wed, 29 Jun 2016 10:50:01 -0700 +Subject: watchdog: qcom: add option for standalone watchdog not in timer block + +Commit 0dfd582e026a ("watchdog: qcom: use timer devicetree +binding") moved to use the watchdog as a subset timer +register block. Some devices have the watchdog completely +standalone with slightly different register offsets as +well so let's account for the differences here. + +The existing "kpss-standalone" compatible string doesn't +make it entirely clear exactly what the device is so +rename to "kpss-wdt" to reflect watchdog timer +functionality. Also update ipq4019 DTS with an SoC +specific compatible. + +Signed-off-by: Matthew McClintock +Signed-off-by: Thomas Pedersen +Reviewed-by: Guenter Roeck +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + .../devicetree/bindings/watchdog/qcom-wdt.txt | 2 + + arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +- + drivers/watchdog/qcom-wdt.c | 64 ++++++++++++++++------ + 3 files changed, 51 insertions(+), 17 deletions(-) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -18,19 +18,42 @@ + #include + #include + #include ++#include + +-#define WDT_RST 0x38 +-#define WDT_EN 0x40 +-#define WDT_STS 0x44 +-#define WDT_BITE_TIME 0x5C ++enum wdt_reg { ++ WDT_RST, ++ WDT_EN, ++ WDT_STS, ++ WDT_BITE_TIME, ++}; ++ ++static const u32 reg_offset_data_apcs_tmr[] = { ++ [WDT_RST] = 0x38, ++ [WDT_EN] = 0x40, ++ [WDT_STS] = 0x44, ++ [WDT_BITE_TIME] = 0x5C, ++}; ++ ++static const u32 reg_offset_data_kpss[] = { ++ [WDT_RST] = 0x4, ++ [WDT_EN] = 0x8, ++ [WDT_STS] = 0xC, ++ [WDT_BITE_TIME] = 0x14, ++}; + + struct qcom_wdt { + struct watchdog_device wdd; + struct clk *clk; + unsigned long rate; + void __iomem *base; ++ const u32 *layout; + }; + ++static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg) ++{ ++ return wdt->base + wdt->layout[reg]; ++} ++ + static inline + struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) + { +@@ -41,10 +64,10 @@ static int qcom_wdt_start(struct watchdo + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(0, wdt->base + WDT_EN); +- writel(1, wdt->base + WDT_RST); +- writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); +- writel(1, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); ++ writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); ++ writel(1, wdt_addr(wdt, WDT_EN)); + return 0; + } + +@@ -52,7 +75,7 @@ static int qcom_wdt_stop(struct watchdog + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(0, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); + return 0; + } + +@@ -60,7 +83,7 @@ static int qcom_wdt_ping(struct watchdog + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(1, wdt->base + WDT_RST); ++ writel(1, wdt_addr(wdt, WDT_RST)); + return 0; + } + +@@ -83,10 +106,10 @@ static int qcom_wdt_restart(struct watch + */ + timeout = 128 * wdt->rate / 1000; + +- writel(0, wdt->base + WDT_EN); +- writel(1, wdt->base + WDT_RST); +- writel(timeout, wdt->base + WDT_BITE_TIME); +- writel(1, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); ++ writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); ++ writel(1, wdt_addr(wdt, WDT_EN)); + + /* + * Actually make sure the above sequence hits hardware before sleeping. +@@ -119,9 +142,16 @@ static int qcom_wdt_probe(struct platfor + struct qcom_wdt *wdt; + struct resource *res; + struct device_node *np = pdev->dev.of_node; ++ const u32 *regs; + u32 percpu_offset; + int ret; + ++ regs = of_device_get_match_data(&pdev->dev); ++ if (!regs) { ++ dev_err(&pdev->dev, "Unsupported QCOM WDT module\n"); ++ return -ENODEV; ++ } ++ + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; +@@ -172,6 +202,7 @@ static int qcom_wdt_probe(struct platfor + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + wdt->wdd.parent = &pdev->dev; ++ wdt->layout = regs; + + if (readl(wdt->base + WDT_STS) & 1) + wdt->wdd.bootstatus = WDIOF_CARDRESET; +@@ -208,8 +239,9 @@ static int qcom_wdt_remove(struct platfo + } + + static const struct of_device_id qcom_wdt_of_table[] = { +- { .compatible = "qcom,kpss-timer" }, +- { .compatible = "qcom,scss-timer" }, ++ { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr }, ++ { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr }, ++ { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss }, + { }, + }; + MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); diff --git a/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch new file mode 100644 index 000000000..bdc3f9989 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch @@ -0,0 +1,60 @@ +From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001 +From: Matthew McClintock +Date: Tue, 28 Jun 2016 11:35:21 -0700 +Subject: watchdog: qcom: configure BARK time in addition to BITE time + +For certain parts and some versions of TZ, TZ will reset the chip +when a BARK is triggered even though it was not configured here. So +by default let's configure this BARK time as well. + +Signed-off-by: Matthew McClintock +Reviewed-by: Guenter Roeck +Signed-off-by: Thomas Pedersen +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/qcom-wdt.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -24,6 +24,7 @@ enum wdt_reg { + WDT_RST, + WDT_EN, + WDT_STS, ++ WDT_BARK_TIME, + WDT_BITE_TIME, + }; + +@@ -31,6 +32,7 @@ static const u32 reg_offset_data_apcs_tm + [WDT_RST] = 0x38, + [WDT_EN] = 0x40, + [WDT_STS] = 0x44, ++ [WDT_BARK_TIME] = 0x4C, + [WDT_BITE_TIME] = 0x5C, + }; + +@@ -38,6 +40,7 @@ static const u32 reg_offset_data_kpss[] + [WDT_RST] = 0x4, + [WDT_EN] = 0x8, + [WDT_STS] = 0xC, ++ [WDT_BARK_TIME] = 0x10, + [WDT_BITE_TIME] = 0x14, + }; + +@@ -66,6 +69,7 @@ static int qcom_wdt_start(struct watchdo + + writel(0, wdt_addr(wdt, WDT_EN)); + writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME)); + writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); + writel(1, wdt_addr(wdt, WDT_EN)); + return 0; +@@ -108,6 +112,7 @@ static int qcom_wdt_restart(struct watch + + writel(0, wdt_addr(wdt, WDT_EN)); + writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(timeout, wdt_addr(wdt, WDT_BARK_TIME)); + writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); + writel(1, wdt_addr(wdt, WDT_EN)); + diff --git a/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch b/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch new file mode 100644 index 000000000..c78202b32 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/011-1-fix-bld-errs-hwmon-sch56xx-Drop-watchdog-driver-data-reference-count-callbacks.patch @@ -0,0 +1,95 @@ +From 3b8d058cfe6a3b14abee324f4c4b33e64bf61aeb Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 25 Dec 2015 16:01:45 -0800 +Subject: hwmon: (sch56xx) Drop watchdog driver data reference count callbacks + +Reference counting is now implemented in the watchdog core and no longer +required in watchdog drivers. + +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/hwmon/sch56xx-common.c | 31 +------------------------------ + 1 file changed, 1 insertion(+), 30 deletions(-) + +--- a/drivers/hwmon/sch56xx-common.c ++++ b/drivers/hwmon/sch56xx-common.c +@@ -30,7 +30,6 @@ + #include + #include + #include +-#include + #include + #include "sch56xx-common.h" + +@@ -67,7 +66,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog can + struct sch56xx_watchdog_data { + u16 addr; + struct mutex *io_lock; +- struct kref kref; + struct watchdog_info wdinfo; + struct watchdog_device wddev; + u8 watchdog_preset; +@@ -258,15 +256,6 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12 + * Watchdog routines + */ + +-/* Release our data struct when we're unregistered *and* +- all references to our watchdog device are released */ +-static void watchdog_release_resources(struct kref *r) +-{ +- struct sch56xx_watchdog_data *data = +- container_of(r, struct sch56xx_watchdog_data, kref); +- kfree(data); +-} +- + static int watchdog_set_timeout(struct watchdog_device *wddev, + unsigned int timeout) + { +@@ -395,28 +384,12 @@ static int watchdog_stop(struct watchdog + return 0; + } + +-static void watchdog_ref(struct watchdog_device *wddev) +-{ +- struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); +- +- kref_get(&data->kref); +-} +- +-static void watchdog_unref(struct watchdog_device *wddev) +-{ +- struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); +- +- kref_put(&data->kref, watchdog_release_resources); +-} +- + static const struct watchdog_ops watchdog_ops = { + .owner = THIS_MODULE, + .start = watchdog_start, + .stop = watchdog_stop, + .ping = watchdog_trigger, + .set_timeout = watchdog_set_timeout, +- .ref = watchdog_ref, +- .unref = watchdog_unref, + }; + + struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, +@@ -448,7 +421,6 @@ struct sch56xx_watchdog_data *sch56xx_wa + + data->addr = addr; + data->io_lock = io_lock; +- kref_init(&data->kref); + + strlcpy(data->wdinfo.identity, "sch56xx watchdog", + sizeof(data->wdinfo.identity)); +@@ -494,8 +466,7 @@ EXPORT_SYMBOL(sch56xx_watchdog_register) + void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) + { + watchdog_unregister_device(&data->wddev); +- kref_put(&data->kref, watchdog_release_resources); +- /* Don't touch data after this it may have been free-ed! */ ++ kfree(data); + } + EXPORT_SYMBOL(sch56xx_watchdog_unregister); + diff --git a/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch b/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch new file mode 100644 index 000000000..501bd1363 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/011-2-fix-bld-errs-watchdog-da9052_wdt-Drop-reference-counting.patch @@ -0,0 +1,87 @@ +From 756d1e9247dff6d416b0c9e073247f5e808bb5fa Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 25 Dec 2015 16:01:43 -0800 +Subject: watchdog: da9052_wdt: Drop reference counting + +Reference counting is now implemented in the watchdog core and no longer +required in watchdog drivers. + +Since it was implememented a no-op, and since the local memory is allocated +with devm_kzalloc(), the reference counting code in the driver really did +not really work anyway, and this patch effectively fixes a bug which could +cause a crash on unloading if the watchdog device was still open. + +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/da9052_wdt.c | 24 ------------------------ + 1 file changed, 24 deletions(-) + +--- a/drivers/watchdog/da9052_wdt.c ++++ b/drivers/watchdog/da9052_wdt.c +@@ -31,7 +31,6 @@ + struct da9052_wdt_data { + struct watchdog_device wdt; + struct da9052 *da9052; +- struct kref kref; + unsigned long jpast; + }; + +@@ -51,10 +50,6 @@ static const struct { + }; + + +-static void da9052_wdt_release_resources(struct kref *r) +-{ +-} +- + static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) + { +@@ -104,20 +99,6 @@ static int da9052_wdt_set_timeout(struct + return 0; + } + +-static void da9052_wdt_ref(struct watchdog_device *wdt_dev) +-{ +- struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); +- +- kref_get(&driver_data->kref); +-} +- +-static void da9052_wdt_unref(struct watchdog_device *wdt_dev) +-{ +- struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); +- +- kref_put(&driver_data->kref, da9052_wdt_release_resources); +-} +- + static int da9052_wdt_start(struct watchdog_device *wdt_dev) + { + return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); +@@ -170,8 +151,6 @@ static const struct watchdog_ops da9052_ + .stop = da9052_wdt_stop, + .ping = da9052_wdt_ping, + .set_timeout = da9052_wdt_set_timeout, +- .ref = da9052_wdt_ref, +- .unref = da9052_wdt_unref, + }; + + +@@ -198,8 +177,6 @@ static int da9052_wdt_probe(struct platf + da9052_wdt->parent = &pdev->dev; + watchdog_set_drvdata(da9052_wdt, driver_data); + +- kref_init(&driver_data->kref); +- + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_TWDSCALE, 0); + if (ret < 0) { +@@ -225,7 +202,6 @@ static int da9052_wdt_remove(struct plat + struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&driver_data->wdt); +- kref_put(&driver_data->kref, da9052_wdt_release_resources); + + return 0; + } diff --git a/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch b/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch new file mode 100644 index 000000000..3cf723a9b --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/011-3-fix-bld-errs-watchdog-da9055_wdt-Drop-reference-counting.patch @@ -0,0 +1,80 @@ +From 43f676ace2e0591718ff493d290bc49b35ec2ffc Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Fri, 25 Dec 2015 16:01:44 -0800 +Subject: watchdog: da9055_wdt: Drop reference counting + +Reference counting is now implemented in the watchdog core and no longer +required in watchdog drivers. + +Since it was implememented a no-op, and since the local memory is allocated +with devm_kzalloc(), the reference counting code in the driver really did +not really work anyway, and this patch effectively fixes a bug which could +cause a crash on unloading if the watchdog device was still open. + +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/da9055_wdt.c | 24 ------------------------ + 1 file changed, 24 deletions(-) + +--- a/drivers/watchdog/da9055_wdt.c ++++ b/drivers/watchdog/da9055_wdt.c +@@ -35,7 +35,6 @@ MODULE_PARM_DESC(nowayout, + struct da9055_wdt_data { + struct watchdog_device wdt; + struct da9055 *da9055; +- struct kref kref; + }; + + static const struct { +@@ -99,24 +98,6 @@ static int da9055_wdt_ping(struct watchd + DA9055_WATCHDOG_MASK, 1); + } + +-static void da9055_wdt_release_resources(struct kref *r) +-{ +-} +- +-static void da9055_wdt_ref(struct watchdog_device *wdt_dev) +-{ +- struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); +- +- kref_get(&driver_data->kref); +-} +- +-static void da9055_wdt_unref(struct watchdog_device *wdt_dev) +-{ +- struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); +- +- kref_put(&driver_data->kref, da9055_wdt_release_resources); +-} +- + static int da9055_wdt_start(struct watchdog_device *wdt_dev) + { + return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); +@@ -138,8 +119,6 @@ static const struct watchdog_ops da9055_ + .stop = da9055_wdt_stop, + .ping = da9055_wdt_ping, + .set_timeout = da9055_wdt_set_timeout, +- .ref = da9055_wdt_ref, +- .unref = da9055_wdt_unref, + }; + + static int da9055_wdt_probe(struct platform_device *pdev) +@@ -165,8 +144,6 @@ static int da9055_wdt_probe(struct platf + watchdog_set_nowayout(da9055_wdt, nowayout); + watchdog_set_drvdata(da9055_wdt, driver_data); + +- kref_init(&driver_data->kref); +- + ret = da9055_wdt_stop(da9055_wdt); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); +@@ -189,7 +166,6 @@ static int da9055_wdt_remove(struct plat + struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&driver_data->wdt); +- kref_put(&driver_data->kref, da9055_wdt_release_resources); + + return 0; + } diff --git a/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch new file mode 100644 index 000000000..83b97bf5c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch @@ -0,0 +1,746 @@ +From patchwork Wed Nov 2 15:56:56 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks +From: Georgi Djakov +X-Patchwork-Id: 9409419 +Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org> +To: sboyd@codeaurora.org, mturquette@baylibre.com +Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, + robh+dt@kernel.org, mark.rutland@arm.com, + linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + georgi.djakov@linaro.org +Date: Wed, 2 Nov 2016 17:56:56 +0200 + +This adds initial support for clocks controlled by the Resource +Power Manager (RPM) processor on some Qualcomm SoCs, which use +the qcom_smd_rpm driver to communicate with RPM. +Such platforms are msm8916, apq8084 and msm8974. + +The RPM is a dedicated hardware engine for managing the shared +SoC resources in order to keep the lowest power profile. It +communicates with other hardware subsystems via shared memory +and accepts clock requests, aggregates the requests and turns +the clocks on/off or scales them on demand. + +This driver is based on the codeaurora.org driver: +https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c + +Signed-off-by: Georgi Djakov +--- + .../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++ + drivers/clk/qcom/Kconfig | 16 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++ + include/dt-bindings/clock/qcom,rpmcc.h | 45 ++ + 5 files changed, 669 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt + create mode 100644 drivers/clk/qcom/clk-smd-rpm.c + create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h + +-- +To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -0,0 +1,36 @@ ++Qualcomm RPM Clock Controller Binding ++------------------------------------------------ ++The RPM is a dedicated hardware engine for managing the shared ++SoC resources in order to keep the lowest power profile. It ++communicates with other hardware subsystems via shared memory ++and accepts clock requests, aggregates the requests and turns ++the clocks on/off or scales them on demand. ++ ++Required properties : ++- compatible : shall contain only one of the following. The generic ++ compatible "qcom,rpmcc" should be also included. ++ ++ "qcom,rpmcc-msm8916", "qcom,rpmcc" ++ ++- #clock-cells : shall contain 1 ++ ++Example: ++ smd { ++ compatible = "qcom,smd"; ++ ++ rpm { ++ interrupts = <0 168 1>; ++ qcom,ipc = <&apcs 8 0>; ++ qcom,smd-edge = <15>; ++ ++ rpm_requests { ++ compatible = "qcom,rpm-msm8916"; ++ qcom,smd-channels = "rpm_requests"; ++ ++ rpmcc: clock-controller { ++ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; ++ #clock-cells = <1>; ++ }; ++ }; ++ }; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -2,6 +2,9 @@ config QCOM_GDSC + bool + select PM_GENERIC_DOMAINS if PM + ++config QCOM_RPMCC ++ bool ++ + config COMMON_CLK_QCOM + tristate "Support for Qualcomm's clock controllers" + depends on OF +@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config QCOM_CLK_SMD_RPM ++ tristate "RPM over SMD based Clock Controller" ++ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM ++ select QCOM_RPMCC ++ help ++ The RPM (Resource Power Manager) is a dedicated hardware engine for ++ managing the shared SoC resources in order to keep the lowest power ++ profile. It communicates with other hardware subsystems via shared ++ memory and accepts clock requests, aggregates the requests and turns ++ the clocks on/off or scales them on demand. ++ Say Y if you want to support the clocks exposed by the RPM on ++ platforms such as apq8016, apq8084, msm8974 etc. ++ + config APQ_GCC_8084 + tristate "APQ8084 Global Clock Controller" + select QCOM_GDSC +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o ++obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-smd-rpm.c +@@ -0,0 +1,571 @@ ++/* ++ * Copyright (c) 2016, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 ++#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 ++#define QCOM_RPM_SMD_KEY_RATE 0x007a484b ++#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 ++#define QCOM_RPM_SMD_KEY_STATE 0x54415453 ++#define QCOM_RPM_SCALING_ENABLE_ID 0x2 ++ ++#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ ++ key) \ ++ static struct clk_smd_rpm _platform##_##_active; \ ++ static struct clk_smd_rpm _platform##_##_name = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .rpm_key = (key), \ ++ .peer = &_platform##_##_active, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_smd_rpm _platform##_##_active = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .active_only = true, \ ++ .rpm_key = (key), \ ++ .peer = &_platform##_##_name, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ ++ stat_id, r, key) \ ++ static struct clk_smd_rpm _platform##_##_active; \ ++ static struct clk_smd_rpm _platform##_##_name = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .rpm_key = (key), \ ++ .branch = true, \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_smd_rpm _platform##_##_active = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .active_only = true, \ ++ .rpm_key = (key), \ ++ .branch = true, \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ ++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ ++ 0, QCOM_RPM_SMD_KEY_RATE) ++ ++#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ ++ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) ++ ++#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ ++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ ++ 0, QCOM_RPM_SMD_KEY_STATE) ++ ++#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ ++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ ++ QCOM_RPM_KEY_SOFTWARE_ENABLE) ++ ++#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ ++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ ++ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) ++ ++#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) ++ ++struct clk_smd_rpm { ++ const int rpm_res_type; ++ const int rpm_key; ++ const int rpm_clk_id; ++ const int rpm_status_id; ++ const bool active_only; ++ bool enabled; ++ bool branch; ++ struct clk_smd_rpm *peer; ++ struct clk_hw hw; ++ unsigned long rate; ++ struct qcom_smd_rpm *rpm; ++}; ++ ++struct clk_smd_rpm_req { ++ __le32 key; ++ __le32 nbytes; ++ __le32 value; ++}; ++ ++struct rpm_cc { ++ struct qcom_rpm *rpm; ++ struct clk_hw_onecell_data data; ++ struct clk_hw *hws[]; ++}; ++ ++struct rpm_smd_clk_desc { ++ struct clk_smd_rpm **clks; ++ size_t num_clks; ++}; ++ ++static DEFINE_MUTEX(rpm_smd_clk_lock); ++ ++static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) ++{ ++ int ret; ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(INT_MAX), ++ }; ++ ++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++ if (ret) ++ return ret; ++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, ++ unsigned long rate) ++{ ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ ++ }; ++ ++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++} ++ ++static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, ++ unsigned long rate) ++{ ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ ++ }; ++ ++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++} ++ ++static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, ++ unsigned long *active, unsigned long *sleep) ++{ ++ *active = rate; ++ ++ /* ++ * Active-only clocks don't care what the rate is during sleep. So, ++ * they vote for zero. ++ */ ++ if (r->active_only) ++ *sleep = 0; ++ else ++ *sleep = *active; ++} ++ ++static int clk_smd_rpm_prepare(struct clk_hw *hw) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret = 0; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ /* Don't send requests to the RPM if the rate has not been set. */ ++ if (!r->rate) ++ goto out; ++ ++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ++ if (r->branch) ++ active_rate = !!active_rate; ++ ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ if (r->branch) ++ sleep_rate = !!sleep_rate; ++ ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ /* Undo the active set vote and restore it */ ++ ret = clk_smd_rpm_set_rate_active(r, peer_rate); ++ ++out: ++ if (!ret) ++ r->enabled = true; ++ ++ mutex_unlock(&rpm_smd_clk_lock); ++ ++ return ret; ++} ++ ++static void clk_smd_rpm_unprepare(struct clk_hw *hw) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ if (!r->rate) ++ goto out; ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, &peer_rate, ++ &peer_sleep_rate); ++ ++ active_rate = r->branch ? !!peer_rate : peer_rate; ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->enabled = false; ++ ++out: ++ mutex_unlock(&rpm_smd_clk_lock); ++} ++ ++static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long active_rate, sleep_rate; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ int ret = 0; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ if (!r->enabled) ++ goto out; ++ ++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->rate = rate; ++ ++out: ++ mutex_unlock(&rpm_smd_clk_lock); ++ ++ return ret; ++} ++ ++static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate is requested. ++ */ ++ return rate; ++} ++ ++static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate was set. ++ */ ++ return r->rate; ++} ++ ++static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) ++{ ++ int ret; ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(1), ++ }; ++ ++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ QCOM_SMD_RPM_MISC_CLK, ++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); ++ if (ret) { ++ pr_err("RPM clock scaling (sleep set) not enabled!\n"); ++ return ret; ++ } ++ ++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ QCOM_SMD_RPM_MISC_CLK, ++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); ++ if (ret) { ++ pr_err("RPM clock scaling (active set) not enabled!\n"); ++ return ret; ++ } ++ ++ pr_debug("%s: RPM clock scaling is enabled\n", __func__); ++ return 0; ++} ++ ++static const struct clk_ops clk_smd_rpm_ops = { ++ .prepare = clk_smd_rpm_prepare, ++ .unprepare = clk_smd_rpm_unprepare, ++ .set_rate = clk_smd_rpm_set_rate, ++ .round_rate = clk_smd_rpm_round_rate, ++ .recalc_rate = clk_smd_rpm_recalc_rate, ++}; ++ ++static const struct clk_ops clk_smd_rpm_branch_ops = { ++ .prepare = clk_smd_rpm_prepare, ++ .unprepare = clk_smd_rpm_unprepare, ++ .round_rate = clk_smd_rpm_round_rate, ++ .recalc_rate = clk_smd_rpm_recalc_rate, ++}; ++ ++/* msm8916 */ ++DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); ++DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); ++DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); ++DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); ++ ++static struct clk_smd_rpm *msm8916_clks[] = { ++ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, ++ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, ++ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, ++ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, ++ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, ++ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, ++ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, ++ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, ++ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, ++ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, ++ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, ++ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, ++ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, ++ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, ++ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, ++ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, ++ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, ++ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, ++ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, ++ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, ++ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, ++ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, ++ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, ++ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, ++}; ++ ++static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { ++ .clks = msm8916_clks, ++ .num_clks = ARRAY_SIZE(msm8916_clks), ++}; ++ ++static const struct of_device_id rpm_smd_clk_match_table[] = { ++ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); ++ ++static int rpm_smd_clk_probe(struct platform_device *pdev) ++{ ++ struct clk_hw **hws; ++ struct rpm_cc *rcc; ++ struct clk_hw_onecell_data *data; ++ int ret; ++ size_t num_clks, i; ++ struct qcom_smd_rpm *rpm; ++ struct clk_smd_rpm **rpm_smd_clks; ++ const struct rpm_smd_clk_desc *desc; ++ ++ rpm = dev_get_drvdata(pdev->dev.parent); ++ if (!rpm) { ++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); ++ return -ENODEV; ++ } ++ ++ desc = of_device_get_match_data(&pdev->dev); ++ if (!desc) ++ return -EINVAL; ++ ++ rpm_smd_clks = desc->clks; ++ num_clks = desc->num_clks; ++ ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, ++ GFP_KERNEL); ++ if (!rcc) ++ return -ENOMEM; ++ ++ hws = rcc->hws; ++ data = &rcc->data; ++ data->num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_smd_clks[i]) { ++ continue; ++ } ++ ++ rpm_smd_clks[i]->rpm = rpm; ++ ++ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); ++ if (ret) ++ goto err; ++ } ++ ++ ret = clk_smd_rpm_enable_scaling(rpm); ++ if (ret) ++ goto err; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_smd_clks[i]) { ++ data->hws[i] = ERR_PTR(-ENOENT); ++ continue; ++ } ++ ++ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); ++ if (ret) ++ goto err; ++ } ++ ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, ++ data); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); ++ return ret; ++} ++ ++static int rpm_smd_clk_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ return 0; ++} ++ ++static struct platform_driver rpm_smd_clk_driver = { ++ .driver = { ++ .name = "qcom-clk-smd-rpm", ++ .of_match_table = rpm_smd_clk_match_table, ++ }, ++ .probe = rpm_smd_clk_probe, ++ .remove = rpm_smd_clk_remove, ++}; ++ ++static int __init rpm_smd_clk_init(void) ++{ ++ return platform_driver_register(&rpm_smd_clk_driver); ++} ++core_initcall(rpm_smd_clk_init); ++ ++static void __exit rpm_smd_clk_exit(void) ++{ ++ platform_driver_unregister(&rpm_smd_clk_driver); ++} ++module_exit(rpm_smd_clk_exit); ++ ++MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-clk-smd-rpm"); +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2015 Linaro Limited ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H ++#define _DT_BINDINGS_CLK_MSM_RPMCC_H ++ ++/* msm8916 */ ++#define RPM_SMD_XO_CLK_SRC 0 ++#define RPM_SMD_XO_A_CLK_SRC 1 ++#define RPM_SMD_PCNOC_CLK 2 ++#define RPM_SMD_PCNOC_A_CLK 3 ++#define RPM_SMD_SNOC_CLK 4 ++#define RPM_SMD_SNOC_A_CLK 5 ++#define RPM_SMD_BIMC_CLK 6 ++#define RPM_SMD_BIMC_A_CLK 7 ++#define RPM_SMD_QDSS_CLK 8 ++#define RPM_SMD_QDSS_A_CLK 9 ++#define RPM_SMD_BB_CLK1 10 ++#define RPM_SMD_BB_CLK1_A 11 ++#define RPM_SMD_BB_CLK2 12 ++#define RPM_SMD_BB_CLK2_A 13 ++#define RPM_SMD_RF_CLK1 14 ++#define RPM_SMD_RF_CLK1_A 15 ++#define RPM_SMD_RF_CLK2 16 ++#define RPM_SMD_RF_CLK2_A 17 ++#define RPM_SMD_BB_CLK1_PIN 18 ++#define RPM_SMD_BB_CLK1_A_PIN 19 ++#define RPM_SMD_BB_CLK2_PIN 20 ++#define RPM_SMD_BB_CLK2_A_PIN 21 ++#define RPM_SMD_RF_CLK1_PIN 22 ++#define RPM_SMD_RF_CLK1_A_PIN 23 ++#define RPM_SMD_RF_CLK2_PIN 24 ++#define RPM_SMD_RF_CLK2_A_PIN 25 ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch new file mode 100644 index 000000000..8a38ead56 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch @@ -0,0 +1,586 @@ +From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 2 Nov 2016 17:56:57 +0200 +Subject: clk: qcom: Add support for RPM Clocks + +This adds initial support for clocks controlled by the Resource +Power Manager (RPM) processor on some Qualcomm SoCs, which use +the qcom_rpm driver to communicate with RPM. +Such platforms are apq8064 and msm8960. + +Signed-off-by: Georgi Djakov +Acked-by: Rob Herring +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + + drivers/clk/qcom/Kconfig | 13 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++ + include/dt-bindings/clock/qcom,rpmcc.h | 24 + + 5 files changed, 528 insertions(+) + create mode 100644 drivers/clk/qcom/clk-rpm.c + +--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -11,6 +11,7 @@ Required properties : + compatible "qcom,rpmcc" should be also included. + + "qcom,rpmcc-msm8916", "qcom,rpmcc" ++ "qcom,rpmcc-apq8064", "qcom,rpmcc" + + - #clock-cells : shall contain 1 + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config QCOM_CLK_RPM ++ tristate "RPM based Clock Controller" ++ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM ++ select QCOM_RPMCC ++ help ++ The RPM (Resource Power Manager) is a dedicated hardware engine for ++ managing the shared SoC resources in order to keep the lowest power ++ profile. It communicates with other hardware subsystems via shared ++ memory and accepts clock requests, aggregates the requests and turns ++ the clocks on/off or scales them on demand. ++ Say Y if you want to support the clocks exposed by the RPM on ++ platforms such as ipq806x, msm8660, msm8960 etc. ++ + config QCOM_CLK_SMD_RPM + tristate "RPM over SMD based Clock Controller" + depends on COMMON_CLK_QCOM && QCOM_SMD_RPM +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897 + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ++obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -0,0 +1,489 @@ ++/* ++ * Copyright (c) 2016, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 ++#define QCOM_RPM_SCALING_ENABLE_ID 0x2 ++ ++#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_active, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_name, \ ++ .active_only = true, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .active_only = true, \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "cxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .active_only = true, \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "cxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) ++ ++struct clk_rpm { ++ const int rpm_clk_id; ++ const bool active_only; ++ unsigned long rate; ++ bool enabled; ++ bool branch; ++ struct clk_rpm *peer; ++ struct clk_hw hw; ++ struct qcom_rpm *rpm; ++}; ++ ++struct rpm_cc { ++ struct qcom_rpm *rpm; ++ struct clk_hw_onecell_data data; ++ struct clk_hw *hws[]; ++}; ++ ++struct rpm_clk_desc { ++ struct clk_rpm **clks; ++ size_t num_clks; ++}; ++ ++static DEFINE_MUTEX(rpm_clk_lock); ++ ++static int clk_rpm_handoff(struct clk_rpm *r) ++{ ++ int ret; ++ u32 value = INT_MAX; ++ ++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, ++ r->rpm_clk_id, &value, 1); ++ if (ret) ++ return ret; ++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, ++ r->rpm_clk_id, &value, 1); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) ++{ ++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ ++ ++ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, ++ r->rpm_clk_id, &value, 1); ++} ++ ++static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) ++{ ++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ ++ ++ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, ++ r->rpm_clk_id, &value, 1); ++} ++ ++static void to_active_sleep(struct clk_rpm *r, unsigned long rate, ++ unsigned long *active, unsigned long *sleep) ++{ ++ *active = rate; ++ ++ /* ++ * Active-only clocks don't care what the rate is during sleep. So, ++ * they vote for zero. ++ */ ++ if (r->active_only) ++ *sleep = 0; ++ else ++ *sleep = *active; ++} ++ ++static int clk_rpm_prepare(struct clk_hw *hw) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret = 0; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ /* Don't send requests to the RPM if the rate has not been set. */ ++ if (!r->rate) ++ goto out; ++ ++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ++ if (r->branch) ++ active_rate = !!active_rate; ++ ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ if (r->branch) ++ sleep_rate = !!sleep_rate; ++ ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ /* Undo the active set vote and restore it */ ++ ret = clk_rpm_set_rate_active(r, peer_rate); ++ ++out: ++ if (!ret) ++ r->enabled = true; ++ ++ mutex_unlock(&rpm_clk_lock); ++ ++ return ret; ++} ++ ++static void clk_rpm_unprepare(struct clk_hw *hw) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ if (!r->rate) ++ goto out; ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, &peer_rate, ++ &peer_sleep_rate); ++ ++ active_rate = r->branch ? !!peer_rate : peer_rate; ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->enabled = false; ++ ++out: ++ mutex_unlock(&rpm_clk_lock); ++} ++ ++static int clk_rpm_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long active_rate, sleep_rate; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ int ret = 0; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ if (!r->enabled) ++ goto out; ++ ++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->rate = rate; ++ ++out: ++ mutex_unlock(&rpm_clk_lock); ++ ++ return ret; ++} ++ ++static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate is requested. ++ */ ++ return rate; ++} ++ ++static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate was set. ++ */ ++ return r->rate; ++} ++ ++static const struct clk_ops clk_rpm_ops = { ++ .prepare = clk_rpm_prepare, ++ .unprepare = clk_rpm_unprepare, ++ .set_rate = clk_rpm_set_rate, ++ .round_rate = clk_rpm_round_rate, ++ .recalc_rate = clk_rpm_recalc_rate, ++}; ++ ++static const struct clk_ops clk_rpm_branch_ops = { ++ .prepare = clk_rpm_prepare, ++ .unprepare = clk_rpm_unprepare, ++ .round_rate = clk_rpm_round_rate, ++ .recalc_rate = clk_rpm_recalc_rate, ++}; ++ ++/* apq8064 */ ++DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); ++DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); ++DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); ++DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); ++DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); ++ ++static struct clk_rpm *apq8064_clks[] = { ++ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, ++ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, ++ [RPM_CFPB_CLK] = &apq8064_cfpb_clk, ++ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, ++ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, ++ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, ++ [RPM_EBI1_CLK] = &apq8064_ebi1_clk, ++ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, ++ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, ++ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, ++ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, ++ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, ++ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, ++ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, ++ [RPM_SFPB_CLK] = &apq8064_sfpb_clk, ++ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, ++ [RPM_QDSS_CLK] = &apq8064_qdss_clk, ++ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, ++}; ++ ++static const struct rpm_clk_desc rpm_clk_apq8064 = { ++ .clks = apq8064_clks, ++ .num_clks = ARRAY_SIZE(apq8064_clks), ++}; ++ ++static const struct of_device_id rpm_clk_match_table[] = { ++ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpm_clk_match_table); ++ ++static int rpm_clk_probe(struct platform_device *pdev) ++{ ++ struct clk_hw **hws; ++ struct rpm_cc *rcc; ++ struct clk_hw_onecell_data *data; ++ int ret; ++ size_t num_clks, i; ++ struct qcom_rpm *rpm; ++ struct clk_rpm **rpm_clks; ++ const struct rpm_clk_desc *desc; ++ ++ rpm = dev_get_drvdata(pdev->dev.parent); ++ if (!rpm) { ++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); ++ return -ENODEV; ++ } ++ ++ desc = of_device_get_match_data(&pdev->dev); ++ if (!desc) ++ return -EINVAL; ++ ++ rpm_clks = desc->clks; ++ num_clks = desc->num_clks; ++ ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, ++ GFP_KERNEL); ++ if (!rcc) ++ return -ENOMEM; ++ ++ hws = rcc->hws; ++ data = &rcc->data; ++ data->num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_clks[i]) ++ continue; ++ ++ rpm_clks[i]->rpm = rpm; ++ ++ ret = clk_rpm_handoff(rpm_clks[i]); ++ if (ret) ++ goto err; ++ } ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_clks[i]) { ++ data->hws[i] = ERR_PTR(-ENOENT); ++ continue; ++ } ++ ++ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); ++ if (ret) ++ goto err; ++ } ++ ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, ++ data); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); ++ return ret; ++} ++ ++static int rpm_clk_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ return 0; ++} ++ ++static struct platform_driver rpm_clk_driver = { ++ .driver = { ++ .name = "qcom-clk-rpm", ++ .of_match_table = rpm_clk_match_table, ++ }, ++ .probe = rpm_clk_probe, ++ .remove = rpm_clk_remove, ++}; ++ ++static int __init rpm_clk_init(void) ++{ ++ return platform_driver_register(&rpm_clk_driver); ++} ++core_initcall(rpm_clk_init); ++ ++static void __exit rpm_clk_exit(void) ++{ ++ platform_driver_unregister(&rpm_clk_driver); ++} ++module_exit(rpm_clk_exit); ++ ++MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-clk-rpm"); +--- a/include/dt-bindings/clock/qcom,rpmcc.h ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -14,6 +14,30 @@ + #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H + #define _DT_BINDINGS_CLK_MSM_RPMCC_H + ++/* apq8064 */ ++#define RPM_PXO_CLK 0 ++#define RPM_PXO_A_CLK 1 ++#define RPM_CXO_CLK 2 ++#define RPM_CXO_A_CLK 3 ++#define RPM_APPS_FABRIC_CLK 4 ++#define RPM_APPS_FABRIC_A_CLK 5 ++#define RPM_CFPB_CLK 6 ++#define RPM_CFPB_A_CLK 7 ++#define RPM_QDSS_CLK 8 ++#define RPM_QDSS_A_CLK 9 ++#define RPM_DAYTONA_FABRIC_CLK 10 ++#define RPM_DAYTONA_FABRIC_A_CLK 11 ++#define RPM_EBI1_CLK 12 ++#define RPM_EBI1_A_CLK 13 ++#define RPM_MM_FABRIC_CLK 14 ++#define RPM_MM_FABRIC_A_CLK 15 ++#define RPM_MMFPB_CLK 16 ++#define RPM_MMFPB_A_CLK 17 ++#define RPM_SYS_FABRIC_CLK 18 ++#define RPM_SYS_FABRIC_A_CLK 19 ++#define RPM_SFPB_CLK 20 ++#define RPM_SFPB_A_CLK 21 ++ + /* msm8916 */ + #define RPM_SMD_XO_CLK_SRC 0 + #define RPM_SMD_XO_A_CLK_SRC 1 diff --git a/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch new file mode 100644 index 000000000..e5c1870ea --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch @@ -0,0 +1,94 @@ +From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 23 Nov 2016 16:52:49 +0200 +Subject: clk: qcom: clk-rpm: Fix clk_hw references + +Fix the clk_hw references to the actual clocks and add a xlate function +to return the hw pointers from the already existing static array. + +Reported-by: Michael Scott +Signed-off-by: Georgi Djakov +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -127,8 +127,8 @@ struct clk_rpm { + + struct rpm_cc { + struct qcom_rpm *rpm; +- struct clk_hw_onecell_data data; +- struct clk_hw *hws[]; ++ struct clk_rpm **clks; ++ size_t num_clks; + }; + + struct rpm_clk_desc { +@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk + }; + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); + ++static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, ++ void *data) ++{ ++ struct rpm_cc *rcc = data; ++ unsigned int idx = clkspec->args[0]; ++ ++ if (idx >= rcc->num_clks) { ++ pr_err("%s: invalid index %u\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); ++} ++ + static int rpm_clk_probe(struct platform_device *pdev) + { +- struct clk_hw **hws; + struct rpm_cc *rcc; +- struct clk_hw_onecell_data *data; + int ret; + size_t num_clks, i; + struct qcom_rpm *rpm; +@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform + rpm_clks = desc->clks; + num_clks = desc->num_clks; + +- rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, +- GFP_KERNEL); ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); + if (!rcc) + return -ENOMEM; + +- hws = rcc->hws; +- data = &rcc->data; +- data->num = num_clks; ++ rcc->clks = rpm_clks; ++ rcc->num_clks = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rpm_clks[i]) +@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform + } + + for (i = 0; i < num_clks; i++) { +- if (!rpm_clks[i]) { +- data->hws[i] = ERR_PTR(-ENOENT); ++ if (!rpm_clks[i]) + continue; +- } + + ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); + if (ret) + goto err; + } + +- ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, +- data); ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, ++ rcc); + if (ret) + goto err; + diff --git a/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch b/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch new file mode 100644 index 000000000..dd941cad7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch @@ -0,0 +1,536 @@ +From 9066073c6c27994a30187abf3b674770b4088348 Mon Sep 17 00:00:00 2001 +From: Rajendra Nayak +Date: Thu, 5 May 2016 14:21:39 +0530 +Subject: thermal: qcom: tsens: Add a skeletal TSENS drivers + +TSENS is Qualcomms' thermal temperature sensor device. It +supports reading temperatures from multiple thermal sensors +present on various QCOM SoCs. +Calibration data is generally read from a non-volatile memory +(eeprom) device. + +Add a skeleton driver with all the necessary abstractions so +a variety of qcom device families which support TSENS can +add driver extensions. + +Also add the required device tree bindings which can be used +to describe the TSENS device in DT. + +Signed-off-by: Rajendra Nayak +Reviewed-by: Lina Iyer +Signed-off-by: Eduardo Valentin +Signed-off-by: Zhang Rui +--- + .../devicetree/bindings/thermal/qcom-tsens.txt | 21 +++ + drivers/thermal/Kconfig | 5 + + drivers/thermal/Makefile | 1 + + drivers/thermal/qcom/Kconfig | 11 ++ + drivers/thermal/qcom/Makefile | 2 + + drivers/thermal/qcom/tsens-common.c | 141 +++++++++++++++ + drivers/thermal/qcom/tsens.c | 195 +++++++++++++++++++++ + drivers/thermal/qcom/tsens.h | 90 ++++++++++ + 8 files changed, 466 insertions(+) + create mode 100644 Documentation/devicetree/bindings/thermal/qcom-tsens.txt + create mode 100644 drivers/thermal/qcom/Kconfig + create mode 100644 drivers/thermal/qcom/Makefile + create mode 100644 drivers/thermal/qcom/tsens-common.c + create mode 100644 drivers/thermal/qcom/tsens.c + create mode 100644 drivers/thermal/qcom/tsens.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt +@@ -0,0 +1,21 @@ ++* QCOM SoC Temperature Sensor (TSENS) ++ ++Required properties: ++- compatible : ++ - "qcom,msm8916-tsens" : For 8916 Family of SoCs ++ - "qcom,msm8974-tsens" : For 8974 Family of SoCs ++ - "qcom,msm8996-tsens" : For 8996 Family of SoCs ++ ++- reg: Address range of the thermal registers ++- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. ++- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify ++nvmem cells ++ ++Example: ++tsens: thermal-sensor@900000 { ++ compatible = "qcom,msm8916-tsens"; ++ reg = <0x4a8000 0x2000>; ++ nvmem-cells = <&tsens_caldata>, <&tsens_calsel>; ++ nvmem-cell-names = "caldata", "calsel"; ++ #thermal-sensor-cells = <1>; ++ }; +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -404,4 +404,9 @@ config BCM2835_THERMAL + help + Support for thermal sensors on Broadcom bcm2835 SoCs. + ++menu "Qualcomm thermal drivers" ++depends on (ARCH_QCOM && OF) || COMPILE_TEST ++source "drivers/thermal/qcom/Kconfig" ++endmenu ++ + endif +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -50,3 +50,4 @@ obj-$(CONFIG_ST_THERMAL) += st/ + obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o + obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o + obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o ++obj-$(CONFIG_QCOM_TSENS) += qcom/ +--- /dev/null ++++ b/drivers/thermal/qcom/Kconfig +@@ -0,0 +1,11 @@ ++config QCOM_TSENS ++ tristate "Qualcomm TSENS Temperature Alarm" ++ depends on THERMAL ++ depends on QCOM_QFPROM ++ depends on ARCH_QCOM || COMPILE_TEST ++ help ++ This enables the thermal sysfs driver for the TSENS device. It shows ++ up in Sysfs as a thermal zone with multiple trip points. Disabling the ++ thermal zone device via the mode file results in disabling the sensor. ++ Also able to set threshold temperature for both hot and cold and update ++ when a threshold is reached. +--- /dev/null ++++ b/drivers/thermal/qcom/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o ++qcom_tsens-y += tsens.o tsens-common.o +--- /dev/null ++++ b/drivers/thermal/qcom/tsens-common.c +@@ -0,0 +1,141 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tsens.h" ++ ++#define S0_ST_ADDR 0x1030 ++#define SN_ADDR_OFFSET 0x4 ++#define SN_ST_TEMP_MASK 0x3ff ++#define CAL_DEGC_PT1 30 ++#define CAL_DEGC_PT2 120 ++#define SLOPE_FACTOR 1000 ++#define SLOPE_DEFAULT 3200 ++ ++char *qfprom_read(struct device *dev, const char *cname) ++{ ++ struct nvmem_cell *cell; ++ ssize_t data; ++ char *ret; ++ ++ cell = nvmem_cell_get(dev, cname); ++ if (IS_ERR(cell)) ++ return ERR_CAST(cell); ++ ++ ret = nvmem_cell_read(cell, &data); ++ nvmem_cell_put(cell); ++ ++ return ret; ++} ++ ++/* ++ * Use this function on devices where slope and offset calculations ++ * depend on calibration data read from qfprom. On others the slope ++ * and offset values are derived from tz->tzp->slope and tz->tzp->offset ++ * resp. ++ */ ++void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, ++ u32 *p2, u32 mode) ++{ ++ int i; ++ int num, den; ++ ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ dev_dbg(tmdev->dev, ++ "sensor%d - data_point1:%#x data_point2:%#x\n", ++ i, p1[i], p2[i]); ++ ++ tmdev->sensor[i].slope = SLOPE_DEFAULT; ++ if (mode == TWO_PT_CALIB) { ++ /* ++ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ ++ * temp_120_degc - temp_30_degc (x2 - x1) ++ */ ++ num = p2[i] - p1[i]; ++ num *= SLOPE_FACTOR; ++ den = CAL_DEGC_PT2 - CAL_DEGC_PT1; ++ tmdev->sensor[i].slope = num / den; ++ } ++ ++ tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - ++ (CAL_DEGC_PT1 * ++ tmdev->sensor[i].slope); ++ dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset); ++ } ++} ++ ++static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) ++{ ++ int degc, num, den; ++ ++ num = (adc_code * SLOPE_FACTOR) - s->offset; ++ den = s->slope; ++ ++ if (num > 0) ++ degc = num + (den / 2); ++ else if (num < 0) ++ degc = num - (den / 2); ++ else ++ degc = num; ++ ++ degc /= den; ++ ++ return degc; ++} ++ ++int get_temp_common(struct tsens_device *tmdev, int id, int *temp) ++{ ++ struct tsens_sensor *s = &tmdev->sensor[id]; ++ u32 code; ++ unsigned int sensor_addr; ++ int last_temp = 0, ret; ++ ++ sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET; ++ ret = regmap_read(tmdev->map, sensor_addr, &code); ++ if (ret) ++ return ret; ++ last_temp = code & SN_ST_TEMP_MASK; ++ ++ *temp = code_to_degc(last_temp, s) * 1000; ++ ++ return 0; ++} ++ ++static const struct regmap_config tsens_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++}; ++ ++int __init init_common(struct tsens_device *tmdev) ++{ ++ void __iomem *base; ++ ++ base = of_iomap(tmdev->dev->of_node, 0); ++ if (IS_ERR(base)) ++ return -EINVAL; ++ ++ tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config); ++ if (!tmdev->map) { ++ iounmap(base); ++ return -ENODEV; ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/thermal/qcom/tsens.c +@@ -0,0 +1,195 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tsens.h" ++ ++static int tsens_get_temp(void *data, int *temp) ++{ ++ const struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ ++ return tmdev->ops->get_temp(tmdev, s->id, temp); ++} ++ ++static int tsens_get_trend(void *data, long *temp) ++{ ++ const struct tsens_sensor *s = data; ++ struct tsens_device *tmdev = s->tmdev; ++ ++ if (tmdev->ops->get_trend) ++ return tmdev->ops->get_trend(tmdev, s->id, temp); ++ ++ return -ENOTSUPP; ++} ++ ++static int tsens_suspend(struct device *dev) ++{ ++ struct tsens_device *tmdev = dev_get_drvdata(dev); ++ ++ if (tmdev->ops && tmdev->ops->suspend) ++ return tmdev->ops->suspend(tmdev); ++ ++ return 0; ++} ++ ++static int tsens_resume(struct device *dev) ++{ ++ struct tsens_device *tmdev = dev_get_drvdata(dev); ++ ++ if (tmdev->ops && tmdev->ops->resume) ++ return tmdev->ops->resume(tmdev); ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); ++ ++static const struct of_device_id tsens_table[] = { ++ { ++ .compatible = "qcom,msm8916-tsens", ++ }, { ++ .compatible = "qcom,msm8974-tsens", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, tsens_table); ++ ++static const struct thermal_zone_of_device_ops tsens_of_ops = { ++ .get_temp = tsens_get_temp, ++ .get_trend = tsens_get_trend, ++}; ++ ++static int tsens_register(struct tsens_device *tmdev) ++{ ++ int i; ++ struct thermal_zone_device *tzd; ++ u32 *hw_id, n = tmdev->num_sensors; ++ ++ hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL); ++ if (!hw_id) ++ return -ENOMEM; ++ ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ tmdev->sensor[i].tmdev = tmdev; ++ tmdev->sensor[i].id = i; ++ tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i, ++ &tmdev->sensor[i], ++ &tsens_of_ops); ++ if (IS_ERR(tzd)) ++ continue; ++ tmdev->sensor[i].tzd = tzd; ++ if (tmdev->ops->enable) ++ tmdev->ops->enable(tmdev, i); ++ } ++ return 0; ++} ++ ++static int tsens_probe(struct platform_device *pdev) ++{ ++ int ret, i; ++ struct device *dev; ++ struct device_node *np; ++ struct tsens_sensor *s; ++ struct tsens_device *tmdev; ++ const struct tsens_data *data; ++ const struct of_device_id *id; ++ ++ if (pdev->dev.of_node) ++ dev = &pdev->dev; ++ else ++ dev = pdev->dev.parent; ++ ++ np = dev->of_node; ++ ++ id = of_match_node(tsens_table, np); ++ if (!id) ++ return -EINVAL; ++ ++ data = id->data; ++ ++ if (data->num_sensors <= 0) { ++ dev_err(dev, "invalid number of sensors\n"); ++ return -EINVAL; ++ } ++ ++ tmdev = devm_kzalloc(dev, sizeof(*tmdev) + ++ data->num_sensors * sizeof(*s), GFP_KERNEL); ++ if (!tmdev) ++ return -ENOMEM; ++ ++ tmdev->dev = dev; ++ tmdev->num_sensors = data->num_sensors; ++ tmdev->ops = data->ops; ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ if (data->hw_ids) ++ tmdev->sensor[i].hw_id = data->hw_ids[i]; ++ else ++ tmdev->sensor[i].hw_id = i; ++ } ++ ++ if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) ++ return -EINVAL; ++ ++ ret = tmdev->ops->init(tmdev); ++ if (ret < 0) { ++ dev_err(dev, "tsens init failed\n"); ++ return ret; ++ } ++ ++ if (tmdev->ops->calibrate) { ++ ret = tmdev->ops->calibrate(tmdev); ++ if (ret < 0) { ++ dev_err(dev, "tsens calibration failed\n"); ++ return ret; ++ } ++ } ++ ++ ret = tsens_register(tmdev); ++ ++ platform_set_drvdata(pdev, tmdev); ++ ++ return ret; ++} ++ ++static int tsens_remove(struct platform_device *pdev) ++{ ++ struct tsens_device *tmdev = platform_get_drvdata(pdev); ++ ++ if (tmdev->ops->disable) ++ tmdev->ops->disable(tmdev); ++ ++ return 0; ++} ++ ++static struct platform_driver tsens_driver = { ++ .probe = tsens_probe, ++ .remove = tsens_remove, ++ .driver = { ++ .name = "qcom-tsens", ++ .pm = &tsens_pm_ops, ++ .of_match_table = tsens_table, ++ }, ++}; ++module_platform_driver(tsens_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); ++MODULE_ALIAS("platform:qcom-tsens"); +--- /dev/null ++++ b/drivers/thermal/qcom/tsens.h +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 __QCOM_TSENS_H__ ++#define __QCOM_TSENS_H__ ++ ++#define ONE_PT_CALIB 0x1 ++#define ONE_PT_CALIB2 0x2 ++#define TWO_PT_CALIB 0x3 ++ ++struct tsens_device; ++ ++struct tsens_sensor { ++ struct tsens_device *tmdev; ++ struct thermal_zone_device *tzd; ++ int offset; ++ int id; ++ int hw_id; ++ int slope; ++ u32 status; ++}; ++ ++/** ++ * struct tsens_ops - operations as supported by the tsens device ++ * @init: Function to initialize the tsens device ++ * @calibrate: Function to calibrate the tsens device ++ * @get_temp: Function which returns the temp in millidegC ++ * @enable: Function to enable (clocks/power) tsens device ++ * @disable: Function to disable the tsens device ++ * @suspend: Function to suspend the tsens device ++ * @resume: Function to resume the tsens device ++ * @get_trend: Function to get the thermal/temp trend ++ */ ++struct tsens_ops { ++ /* mandatory callbacks */ ++ int (*init)(struct tsens_device *); ++ int (*calibrate)(struct tsens_device *); ++ int (*get_temp)(struct tsens_device *, int, int *); ++ /* optional callbacks */ ++ int (*enable)(struct tsens_device *, int); ++ void (*disable)(struct tsens_device *); ++ int (*suspend)(struct tsens_device *); ++ int (*resume)(struct tsens_device *); ++ int (*get_trend)(struct tsens_device *, int, long *); ++}; ++ ++/** ++ * struct tsens_data - tsens instance specific data ++ * @num_sensors: Max number of sensors supported by platform ++ * @ops: operations the tsens instance supports ++ * @hw_ids: Subset of sensors ids supported by platform, if not the first n ++ */ ++struct tsens_data { ++ const u32 num_sensors; ++ const struct tsens_ops *ops; ++ unsigned int *hw_ids; ++}; ++ ++/* Registers to be saved/restored across a context loss */ ++struct tsens_context { ++ int threshold; ++ int control; ++}; ++ ++struct tsens_device { ++ struct device *dev; ++ u32 num_sensors; ++ struct regmap *map; ++ struct regmap_field *status_field; ++ struct tsens_context ctx; ++ bool trdy; ++ const struct tsens_ops *ops; ++ struct tsens_sensor sensor[0]; ++}; ++ ++char *qfprom_read(struct device *, const char *); ++void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); ++int init_common(struct tsens_device *); ++int get_temp_common(struct tsens_device *, int, int *); ++ ++#endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch new file mode 100644 index 000000000..d07619645 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch @@ -0,0 +1,165 @@ +From 840a5bd3ed3fdd62456d4d26c3128ec10496555b Mon Sep 17 00:00:00 2001 +From: Rajendra Nayak +Date: Thu, 5 May 2016 14:21:40 +0530 +Subject: thermal: qcom: tsens-8916: Add support for 8916 family of SoCs + +Add support to calibrate sensors on 8916 family and also add common +functions to read temperature from sensors (This can be reused on +other SoCs having similar TSENS device) +The calibration data is read from eeprom using the generic nvmem +framework apis. + +Based on the original code by Siddartha Mohanadoss and Stephen Boyd. + +Signed-off-by: Rajendra Nayak +Signed-off-by: Eduardo Valentin +Signed-off-by: Zhang Rui +--- + drivers/thermal/qcom/Makefile | 2 +- + drivers/thermal/qcom/tsens-8916.c | 113 ++++++++++++++++++++++++++++++++++++++ + drivers/thermal/qcom/tsens.c | 1 + + drivers/thermal/qcom/tsens.h | 2 + + 4 files changed, 117 insertions(+), 1 deletion(-) + create mode 100644 drivers/thermal/qcom/tsens-8916.c + +--- a/drivers/thermal/qcom/Makefile ++++ b/drivers/thermal/qcom/Makefile +@@ -1,2 +1,2 @@ + obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o +-qcom_tsens-y += tsens.o tsens-common.o ++qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o +--- /dev/null ++++ b/drivers/thermal/qcom/tsens-8916.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include "tsens.h" ++ ++/* eeprom layout data for 8916 */ ++#define BASE0_MASK 0x0000007f ++#define BASE1_MASK 0xfe000000 ++#define BASE0_SHIFT 0 ++#define BASE1_SHIFT 25 ++ ++#define S0_P1_MASK 0x00000f80 ++#define S1_P1_MASK 0x003e0000 ++#define S2_P1_MASK 0xf8000000 ++#define S3_P1_MASK 0x000003e0 ++#define S4_P1_MASK 0x000f8000 ++ ++#define S0_P2_MASK 0x0001f000 ++#define S1_P2_MASK 0x07c00000 ++#define S2_P2_MASK 0x0000001f ++#define S3_P2_MASK 0x00007c00 ++#define S4_P2_MASK 0x01f00000 ++ ++#define S0_P1_SHIFT 7 ++#define S1_P1_SHIFT 17 ++#define S2_P1_SHIFT 27 ++#define S3_P1_SHIFT 5 ++#define S4_P1_SHIFT 15 ++ ++#define S0_P2_SHIFT 12 ++#define S1_P2_SHIFT 22 ++#define S2_P2_SHIFT 0 ++#define S3_P2_SHIFT 10 ++#define S4_P2_SHIFT 20 ++ ++#define CAL_SEL_MASK 0xe0000000 ++#define CAL_SEL_SHIFT 29 ++ ++static int calibrate_8916(struct tsens_device *tmdev) ++{ ++ int base0 = 0, base1 = 0, i; ++ u32 p1[5], p2[5]; ++ int mode = 0; ++ u32 *qfprom_cdata, *qfprom_csel; ++ ++ qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib"); ++ if (IS_ERR(qfprom_cdata)) ++ return PTR_ERR(qfprom_cdata); ++ ++ qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel"); ++ if (IS_ERR(qfprom_csel)) ++ return PTR_ERR(qfprom_csel); ++ ++ mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; ++ dev_dbg(tmdev->dev, "calibration mode is %d\n", mode); ++ ++ switch (mode) { ++ case TWO_PT_CALIB: ++ base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT; ++ p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; ++ p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; ++ p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT; ++ p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; ++ p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; ++ for (i = 0; i < tmdev->num_sensors; i++) ++ p2[i] = ((base1 + p2[i]) << 3); ++ /* Fall through */ ++ case ONE_PT_CALIB2: ++ base0 = (qfprom_cdata[0] & BASE0_MASK); ++ p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; ++ p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; ++ p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; ++ p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; ++ p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; ++ for (i = 0; i < tmdev->num_sensors; i++) ++ p1[i] = (((base0) + p1[i]) << 3); ++ break; ++ default: ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ p1[i] = 500; ++ p2[i] = 780; ++ } ++ break; ++ } ++ ++ compute_intercept_slope(tmdev, p1, p2, mode); ++ ++ return 0; ++} ++ ++const struct tsens_ops ops_8916 = { ++ .init = init_common, ++ .calibrate = calibrate_8916, ++ .get_temp = get_temp_common, ++}; ++ ++const struct tsens_data data_8916 = { ++ .num_sensors = 5, ++ .ops = &ops_8916, ++ .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, ++}; +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -65,6 +65,7 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t + static const struct of_device_id tsens_table[] = { + { + .compatible = "qcom,msm8916-tsens", ++ .data = &data_8916, + }, { + .compatible = "qcom,msm8974-tsens", + }, +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -87,4 +87,6 @@ void compute_intercept_slope(struct tsen + int init_common(struct tsens_device *); + int get_temp_common(struct tsens_device *, int, int *); + ++extern const struct tsens_data data_8916; ++ + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch new file mode 100644 index 000000000..671f46105 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch @@ -0,0 +1,293 @@ +From 5e6703bd2d83548998848865cb9a9a795f31a311 Mon Sep 17 00:00:00 2001 +From: Rajendra Nayak +Date: Thu, 5 May 2016 14:21:41 +0530 +Subject: thermal: qcom: tsens-8974: Add support for 8974 family of SoCs + +Add .calibrate support for 8974 family as part of tsens_ops. + +Based on the original code by Siddartha Mohanadoss and Stephen Boyd. + +Signed-off-by: Rajendra Nayak +Signed-off-by: Eduardo Valentin +Signed-off-by: Zhang Rui +--- + drivers/thermal/qcom/Makefile | 2 +- + drivers/thermal/qcom/tsens-8974.c | 244 ++++++++++++++++++++++++++++++++++++++ + drivers/thermal/qcom/tsens.c | 1 + + drivers/thermal/qcom/tsens.h | 2 +- + 4 files changed, 247 insertions(+), 2 deletions(-) + create mode 100644 drivers/thermal/qcom/tsens-8974.c + +--- a/drivers/thermal/qcom/Makefile ++++ b/drivers/thermal/qcom/Makefile +@@ -1,2 +1,2 @@ + obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o +-qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o ++qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o +--- /dev/null ++++ b/drivers/thermal/qcom/tsens-8974.c +@@ -0,0 +1,244 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include "tsens.h" ++ ++/* eeprom layout data for 8974 */ ++#define BASE1_MASK 0xff ++#define S0_P1_MASK 0x3f00 ++#define S1_P1_MASK 0xfc000 ++#define S2_P1_MASK 0x3f00000 ++#define S3_P1_MASK 0xfc000000 ++#define S4_P1_MASK 0x3f ++#define S5_P1_MASK 0xfc0 ++#define S6_P1_MASK 0x3f000 ++#define S7_P1_MASK 0xfc0000 ++#define S8_P1_MASK 0x3f000000 ++#define S8_P1_MASK_BKP 0x3f ++#define S9_P1_MASK 0x3f ++#define S9_P1_MASK_BKP 0xfc0 ++#define S10_P1_MASK 0xfc0 ++#define S10_P1_MASK_BKP 0x3f000 ++#define CAL_SEL_0_1 0xc0000000 ++#define CAL_SEL_2 0x40000000 ++#define CAL_SEL_SHIFT 30 ++#define CAL_SEL_SHIFT_2 28 ++ ++#define S0_P1_SHIFT 8 ++#define S1_P1_SHIFT 14 ++#define S2_P1_SHIFT 20 ++#define S3_P1_SHIFT 26 ++#define S5_P1_SHIFT 6 ++#define S6_P1_SHIFT 12 ++#define S7_P1_SHIFT 18 ++#define S8_P1_SHIFT 24 ++#define S9_P1_BKP_SHIFT 6 ++#define S10_P1_SHIFT 6 ++#define S10_P1_BKP_SHIFT 12 ++ ++#define BASE2_SHIFT 12 ++#define BASE2_BKP_SHIFT 18 ++#define S0_P2_SHIFT 20 ++#define S0_P2_BKP_SHIFT 26 ++#define S1_P2_SHIFT 26 ++#define S2_P2_BKP_SHIFT 6 ++#define S3_P2_SHIFT 6 ++#define S3_P2_BKP_SHIFT 12 ++#define S4_P2_SHIFT 12 ++#define S4_P2_BKP_SHIFT 18 ++#define S5_P2_SHIFT 18 ++#define S5_P2_BKP_SHIFT 24 ++#define S6_P2_SHIFT 24 ++#define S7_P2_BKP_SHIFT 6 ++#define S8_P2_SHIFT 6 ++#define S8_P2_BKP_SHIFT 12 ++#define S9_P2_SHIFT 12 ++#define S9_P2_BKP_SHIFT 18 ++#define S10_P2_SHIFT 18 ++#define S10_P2_BKP_SHIFT 24 ++ ++#define BASE2_MASK 0xff000 ++#define BASE2_BKP_MASK 0xfc0000 ++#define S0_P2_MASK 0x3f00000 ++#define S0_P2_BKP_MASK 0xfc000000 ++#define S1_P2_MASK 0xfc000000 ++#define S1_P2_BKP_MASK 0x3f ++#define S2_P2_MASK 0x3f ++#define S2_P2_BKP_MASK 0xfc0 ++#define S3_P2_MASK 0xfc0 ++#define S3_P2_BKP_MASK 0x3f000 ++#define S4_P2_MASK 0x3f000 ++#define S4_P2_BKP_MASK 0xfc0000 ++#define S5_P2_MASK 0xfc0000 ++#define S5_P2_BKP_MASK 0x3f000000 ++#define S6_P2_MASK 0x3f000000 ++#define S6_P2_BKP_MASK 0x3f ++#define S7_P2_MASK 0x3f ++#define S7_P2_BKP_MASK 0xfc0 ++#define S8_P2_MASK 0xfc0 ++#define S8_P2_BKP_MASK 0x3f000 ++#define S9_P2_MASK 0x3f000 ++#define S9_P2_BKP_MASK 0xfc0000 ++#define S10_P2_MASK 0xfc0000 ++#define S10_P2_BKP_MASK 0x3f000000 ++ ++#define BKP_SEL 0x3 ++#define BKP_REDUN_SEL 0xe0000000 ++#define BKP_REDUN_SHIFT 29 ++ ++#define BIT_APPEND 0x3 ++ ++static int calibrate_8974(struct tsens_device *tmdev) ++{ ++ int base1 = 0, base2 = 0, i; ++ u32 p1[11], p2[11]; ++ int mode = 0; ++ u32 *calib, *bkp; ++ u32 calib_redun_sel; ++ ++ calib = (u32 *)qfprom_read(tmdev->dev, "calib"); ++ if (IS_ERR(calib)) ++ return PTR_ERR(calib); ++ ++ bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup"); ++ if (IS_ERR(bkp)) ++ return PTR_ERR(bkp); ++ ++ calib_redun_sel = bkp[1] & BKP_REDUN_SEL; ++ calib_redun_sel >>= BKP_REDUN_SHIFT; ++ ++ if (calib_redun_sel == BKP_SEL) { ++ mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; ++ mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; ++ ++ switch (mode) { ++ case TWO_PT_CALIB: ++ base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT; ++ p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT; ++ p2[1] = (bkp[3] & S1_P2_BKP_MASK); ++ p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT; ++ p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT; ++ p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT; ++ p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT; ++ p2[6] = (calib[5] & S6_P2_BKP_MASK); ++ p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT; ++ p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT; ++ p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT; ++ p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT; ++ /* Fall through */ ++ case ONE_PT_CALIB: ++ case ONE_PT_CALIB2: ++ base1 = bkp[0] & BASE1_MASK; ++ p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT; ++ p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT; ++ p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT; ++ p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT; ++ p1[4] = (bkp[1] & S4_P1_MASK); ++ p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT; ++ p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT; ++ p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT; ++ p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT; ++ p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT; ++ p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT; ++ break; ++ } ++ } else { ++ mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; ++ mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; ++ ++ switch (mode) { ++ case TWO_PT_CALIB: ++ base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT; ++ p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT; ++ p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT; ++ p2[2] = (calib[3] & S2_P2_MASK); ++ p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT; ++ p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT; ++ p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT; ++ p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT; ++ p2[7] = (calib[4] & S7_P2_MASK); ++ p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT; ++ p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT; ++ p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT; ++ /* Fall through */ ++ case ONE_PT_CALIB: ++ case ONE_PT_CALIB2: ++ base1 = calib[0] & BASE1_MASK; ++ p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT; ++ p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT; ++ p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT; ++ p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT; ++ p1[4] = (calib[1] & S4_P1_MASK); ++ p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT; ++ p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT; ++ p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT; ++ p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT; ++ p1[9] = (calib[2] & S9_P1_MASK); ++ p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT; ++ break; ++ } ++ } ++ ++ switch (mode) { ++ case ONE_PT_CALIB: ++ for (i = 0; i < tmdev->num_sensors; i++) ++ p1[i] += (base1 << 2) | BIT_APPEND; ++ break; ++ case TWO_PT_CALIB: ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ p2[i] += base2; ++ p2[i] <<= 2; ++ p2[i] |= BIT_APPEND; ++ } ++ /* Fall through */ ++ case ONE_PT_CALIB2: ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ p1[i] += base1; ++ p1[i] <<= 2; ++ p1[i] |= BIT_APPEND; ++ } ++ break; ++ default: ++ for (i = 0; i < tmdev->num_sensors; i++) ++ p2[i] = 780; ++ p1[0] = 502; ++ p1[1] = 509; ++ p1[2] = 503; ++ p1[3] = 509; ++ p1[4] = 505; ++ p1[5] = 509; ++ p1[6] = 507; ++ p1[7] = 510; ++ p1[8] = 508; ++ p1[9] = 509; ++ p1[10] = 508; ++ break; ++ } ++ ++ compute_intercept_slope(tmdev, p1, p2, mode); ++ ++ return 0; ++} ++ ++const struct tsens_ops ops_8974 = { ++ .init = init_common, ++ .calibrate = calibrate_8974, ++ .get_temp = get_temp_common, ++}; ++ ++const struct tsens_data data_8974 = { ++ .num_sensors = 11, ++ .ops = &ops_8974, ++}; +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -68,6 +68,7 @@ static const struct of_device_id tsens_t + .data = &data_8916, + }, { + .compatible = "qcom,msm8974-tsens", ++ .data = &data_8974, + }, + {} + }; +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen + int init_common(struct tsens_device *); + int get_temp_common(struct tsens_device *, int, int *); + +-extern const struct tsens_data data_8916; ++extern const struct tsens_data data_8916, data_8974; + + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch new file mode 100644 index 000000000..05490cd97 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch @@ -0,0 +1,364 @@ +From 20d4fd84bf524ad91e2cc3e4ab4020c27cfc0081 Mon Sep 17 00:00:00 2001 +From: Rajendra Nayak +Date: Thu, 5 May 2016 14:21:43 +0530 +Subject: thermal: qcom: tsens-8960: Add support for 8960 family of SoCs + +8960 family of SoCs have the TSENS device as part of GCC, hence +the driver probes the virtual child device created by GCC and +uses the parent to extract all DT properties and reuses the GCC +regmap. + +Also GCC/TSENS are part of a domain thats not always ON. +Hence add .suspend and .resume hooks to save and restore some of +the inited register context. + +Also 8960 family have some of the TSENS init sequence thats +required to be done by the HLOS driver (some later versions of TSENS +do not export these registers to non-secure world, and hence need +these initializations to be done by secure bootloaders) + +8660 from the same family has just one sensor and hence some register +offset/layout differences which need special handling in the driver. + +Based on the original code from Siddartha Mohanadoss, Stephen Boyd and +Narendran Rajan. + +Signed-off-by: Rajendra Nayak +Signed-off-by: Eduardo Valentin +Signed-off-by: Zhang Rui +--- + drivers/thermal/qcom/Makefile | 2 +- + drivers/thermal/qcom/tsens-8960.c | 292 ++++++++++++++++++++++++++++++++++++++ + drivers/thermal/qcom/tsens.c | 8 +- + drivers/thermal/qcom/tsens.h | 2 +- + 4 files changed, 298 insertions(+), 6 deletions(-) + create mode 100644 drivers/thermal/qcom/tsens-8960.c + +--- a/drivers/thermal/qcom/Makefile ++++ b/drivers/thermal/qcom/Makefile +@@ -1,2 +1,2 @@ + obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o +-qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o ++qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o +--- /dev/null ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -0,0 +1,292 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "tsens.h" ++ ++#define CAL_MDEGC 30000 ++ ++#define CONFIG_ADDR 0x3640 ++#define CONFIG_ADDR_8660 0x3620 ++/* CONFIG_ADDR bitmasks */ ++#define CONFIG 0x9b ++#define CONFIG_MASK 0xf ++#define CONFIG_8660 1 ++#define CONFIG_SHIFT_8660 28 ++#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) ++ ++#define STATUS_CNTL_ADDR_8064 0x3660 ++#define CNTL_ADDR 0x3620 ++/* CNTL_ADDR bitmasks */ ++#define EN BIT(0) ++#define SW_RST BIT(1) ++#define SENSOR0_EN BIT(3) ++#define SLP_CLK_ENA BIT(26) ++#define SLP_CLK_ENA_8660 BIT(24) ++#define MEASURE_PERIOD 1 ++#define SENSOR0_SHIFT 3 ++ ++/* INT_STATUS_ADDR bitmasks */ ++#define MIN_STATUS_MASK BIT(0) ++#define LOWER_STATUS_CLR BIT(1) ++#define UPPER_STATUS_CLR BIT(2) ++#define MAX_STATUS_MASK BIT(3) ++ ++#define THRESHOLD_ADDR 0x3624 ++/* THRESHOLD_ADDR bitmasks */ ++#define THRESHOLD_MAX_LIMIT_SHIFT 24 ++#define THRESHOLD_MIN_LIMIT_SHIFT 16 ++#define THRESHOLD_UPPER_LIMIT_SHIFT 8 ++#define THRESHOLD_LOWER_LIMIT_SHIFT 0 ++ ++/* Initial temperature threshold values */ ++#define LOWER_LIMIT_TH 0x50 ++#define UPPER_LIMIT_TH 0xdf ++#define MIN_LIMIT_TH 0x0 ++#define MAX_LIMIT_TH 0xff ++ ++#define S0_STATUS_ADDR 0x3628 ++#define INT_STATUS_ADDR 0x363c ++#define TRDY_MASK BIT(7) ++#define TIMEOUT_US 100 ++ ++static int suspend_8960(struct tsens_device *tmdev) ++{ ++ int ret; ++ unsigned int mask; ++ struct regmap *map = tmdev->map; ++ ++ ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); ++ if (ret) ++ return ret; ++ ++ if (tmdev->num_sensors > 1) ++ mask = SLP_CLK_ENA | EN; ++ else ++ mask = SLP_CLK_ENA_8660 | EN; ++ ++ ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int resume_8960(struct tsens_device *tmdev) ++{ ++ int ret; ++ struct regmap *map = tmdev->map; ++ ++ ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); ++ if (ret) ++ return ret; ++ ++ /* ++ * Separate CONFIG restore is not needed only for 8660 as ++ * config is part of CTRL Addr and its restored as such ++ */ ++ if (tmdev->num_sensors > 1) { ++ ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); ++ if (ret) ++ return ret; ++ } ++ ++ ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); ++ if (ret) ++ return ret; ++ ++ ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int enable_8960(struct tsens_device *tmdev, int id) ++{ ++ int ret; ++ u32 reg, mask; ++ ++ ret = regmap_read(tmdev->map, CNTL_ADDR, ®); ++ if (ret) ++ return ret; ++ ++ mask = BIT(id + SENSOR0_SHIFT); ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); ++ if (ret) ++ return ret; ++ ++ if (tmdev->num_sensors > 1) ++ reg |= mask | SLP_CLK_ENA | EN; ++ else ++ reg |= mask | SLP_CLK_ENA_8660 | EN; ++ ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void disable_8960(struct tsens_device *tmdev) ++{ ++ int ret; ++ u32 reg_cntl; ++ u32 mask; ++ ++ mask = GENMASK(tmdev->num_sensors - 1, 0); ++ mask <<= SENSOR0_SHIFT; ++ mask |= EN; ++ ++ ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); ++ if (ret) ++ return; ++ ++ reg_cntl &= ~mask; ++ ++ if (tmdev->num_sensors > 1) ++ reg_cntl &= ~SLP_CLK_ENA; ++ else ++ reg_cntl &= ~SLP_CLK_ENA_8660; ++ ++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++} ++ ++static int init_8960(struct tsens_device *tmdev) ++{ ++ int ret, i; ++ u32 reg_cntl; ++ ++ tmdev->map = dev_get_regmap(tmdev->dev, NULL); ++ if (!tmdev->map) ++ return -ENODEV; ++ ++ /* ++ * The status registers for each sensor are discontiguous ++ * because some SoCs have 5 sensors while others have more ++ * but the control registers stay in the same place, i.e ++ * directly after the first 5 status registers. ++ */ ++ for (i = 0; i < tmdev->num_sensors; i++) { ++ if (i >= 5) ++ tmdev->sensor[i].status = S0_STATUS_ADDR + 40; ++ tmdev->sensor[i].status += i * 4; ++ } ++ ++ reg_cntl = SW_RST; ++ ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); ++ if (ret) ++ return ret; ++ ++ if (tmdev->num_sensors > 1) { ++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); ++ reg_cntl &= ~SW_RST; ++ ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, ++ CONFIG_MASK, CONFIG); ++ } else { ++ reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); ++ reg_cntl &= ~CONFIG_MASK_8660; ++ reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; ++ } ++ ++ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ if (ret) ++ return ret; ++ ++ reg_cntl |= EN; ++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int calibrate_8960(struct tsens_device *tmdev) ++{ ++ int i; ++ char *data; ++ ++ ssize_t num_read = tmdev->num_sensors; ++ struct tsens_sensor *s = tmdev->sensor; ++ ++ data = qfprom_read(tmdev->dev, "calib"); ++ if (IS_ERR(data)) ++ data = qfprom_read(tmdev->dev, "calib_backup"); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ for (i = 0; i < num_read; i++, s++) ++ s->offset = data[i]; ++ ++ return 0; ++} ++ ++/* Temperature on y axis and ADC-code on x-axis */ ++static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) ++{ ++ int slope, offset; ++ ++ slope = thermal_zone_get_slope(s->tzd); ++ offset = CAL_MDEGC - slope * s->offset; ++ ++ return adc_code * slope + offset; ++} ++ ++static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) ++{ ++ int ret; ++ u32 code, trdy; ++ const struct tsens_sensor *s = &tmdev->sensor[id]; ++ unsigned long timeout; ++ ++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); ++ do { ++ ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); ++ if (ret) ++ return ret; ++ if (!(trdy & TRDY_MASK)) ++ continue; ++ ret = regmap_read(tmdev->map, s->status, &code); ++ if (ret) ++ return ret; ++ *temp = code_to_mdegC(code, s); ++ return 0; ++ } while (time_before(jiffies, timeout)); ++ ++ return -ETIMEDOUT; ++} ++ ++const struct tsens_ops ops_8960 = { ++ .init = init_8960, ++ .calibrate = calibrate_8960, ++ .get_temp = get_temp_8960, ++ .enable = enable_8960, ++ .disable = disable_8960, ++ .suspend = suspend_8960, ++ .resume = resume_8960, ++}; ++ ++const struct tsens_data data_8960 = { ++ .num_sensors = 11, ++ .ops = &ops_8960, ++}; +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -122,10 +122,10 @@ static int tsens_probe(struct platform_d + np = dev->of_node; + + id = of_match_node(tsens_table, np); +- if (!id) +- return -EINVAL; +- +- data = id->data; ++ if (id) ++ data = id->data; ++ else ++ data = &data_8960; + + if (data->num_sensors <= 0) { + dev_err(dev, "invalid number of sensors\n"); +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen + int init_common(struct tsens_device *); + int get_temp_common(struct tsens_device *, int, int *); + +-extern const struct tsens_data data_8916, data_8974; ++extern const struct tsens_data data_8916, data_8974, data_8960; + + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch b/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch new file mode 100644 index 000000000..39d2173fe --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch @@ -0,0 +1,46 @@ +From 5b97469a55872a30a0d53a1279a8ae8b1c68b52c Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Mon, 4 Jul 2016 15:12:28 +0200 +Subject: thermal: qcom: tsens-8916: mark PM functions __maybe_unused + +The newly added tsens-8916 driver produces warnings when CONFIG_PM +is disabled: + +drivers/thermal/qcom/tsens.c:53:12: error: 'tsens_resume' defined but not used [-Werror=unused-function] + static int tsens_resume(struct device *dev) + ^~~~~~~~~~~~ +drivers/thermal/qcom/tsens.c:43:12: error: 'tsens_suspend' defined but not used [-Werror=unused-function] + static int tsens_suspend(struct device *dev) + ^~~~~~~~~~~~~ + +This marks both functions __maybe_unused to let the compiler +know that they might be used in other configurations, without +adding ugly #ifdef logic. + +Signed-off-by: Arnd Bergmann +Reviewed-by: Rajendra Nayak +Signed-off-by: Zhang Rui +--- + drivers/thermal/qcom/tsens.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -40,7 +40,7 @@ static int tsens_get_trend(void *data, l + return -ENOTSUPP; + } + +-static int tsens_suspend(struct device *dev) ++static int __maybe_unused tsens_suspend(struct device *dev) + { + struct tsens_device *tmdev = dev_get_drvdata(dev); + +@@ -50,7 +50,7 @@ static int tsens_suspend(struct device * + return 0; + } + +-static int tsens_resume(struct device *dev) ++static int __maybe_unused tsens_resume(struct device *dev) + { + struct tsens_device *tmdev = dev_get_drvdata(dev); + diff --git a/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch b/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch new file mode 100644 index 000000000..13415f51d --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch @@ -0,0 +1,42 @@ +From 313a72ff983cc2e00ac4dcb791d40ebf2f9d5718 Mon Sep 17 00:00:00 2001 +From: Srinivas Kandagatla +Date: Tue, 17 Nov 2015 09:12:41 +0000 +Subject: nvmem: core: return error for non word aligned access + +nvmem providers have restrictions on register strides, so return error +when users attempt to read/write buffers with sizes which are less +than word size. + +Without this patch the userspace would continue to try as it does not +get any error from the nvmem core, resulting in a hang or endless loop +in userspace. + +Reported-by: Ariel D'Alessandro +Signed-off-by: Srinivas Kandagatla +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struc + if (pos >= nvmem->size) + return 0; + ++ if (count < nvmem->word_size) ++ return -EINVAL; ++ + if (pos + count > nvmem->size) + count = nvmem->size - pos; + +@@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(stru + if (pos >= nvmem->size) + return 0; + ++ if (count < nvmem->word_size) ++ return -EINVAL; ++ + if (pos + count > nvmem->size) + count = nvmem->size - pos; + diff --git a/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch b/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch new file mode 100644 index 000000000..1f9473baf --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch @@ -0,0 +1,34 @@ +From dfdf141429f0895b63c882facc42c86f225033cb Mon Sep 17 00:00:00 2001 +From: Rasmus Villemoes +Date: Mon, 8 Feb 2016 22:04:29 +0100 +Subject: nvmem: core: fix error path in nvmem_add_cells() + +The current code fails to nvmem_cell_drop(cells[0]) - even worse, if +the loop above fails already at i==0, we'll enter an essentially +infinite loop doing nvmem_cell_drop on cells[-1], cells[-2], ... which +is unlikely to end well. + +Also, we're not freeing the temporary backing array cells on the error +path. + +Signed-off-by: Rasmus Villemoes +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -294,9 +294,11 @@ static int nvmem_add_cells(struct nvmem_ + + return 0; + err: +- while (--i) ++ while (i--) + nvmem_cell_drop(cells[i]); + ++ kfree(cells); ++ + return rval; + } + diff --git a/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch b/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch new file mode 100644 index 000000000..77136eab7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch @@ -0,0 +1,101 @@ +From 811b0d6538b9f26f3eb0f90fe4e6118f2480ec6f Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +Date: Fri, 26 Feb 2016 20:59:18 +0100 +Subject: nvmem: Add flag to export NVMEM to root only + +Legacy AT24, AT25 EEPROMs are exported in sys so that only root can +read the contents. The EEPROMs may contain sensitive information. Add +a flag so the provide can indicate that NVMEM should also restrict +access to root only. + +Signed-off-by: Andrew Lunn +Acked-by: Srinivas Kandagatla +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 57 ++++++++++++++++++++++++++++++++++++++++-- + include/linux/nvmem-provider.h | 1 + + 2 files changed, 56 insertions(+), 2 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -161,6 +161,53 @@ static const struct attribute_group *nvm + NULL, + }; + ++/* default read/write permissions, root only */ ++static struct bin_attribute bin_attr_rw_root_nvmem = { ++ .attr = { ++ .name = "nvmem", ++ .mode = S_IWUSR | S_IRUSR, ++ }, ++ .read = bin_attr_nvmem_read, ++ .write = bin_attr_nvmem_write, ++}; ++ ++static struct bin_attribute *nvmem_bin_rw_root_attributes[] = { ++ &bin_attr_rw_root_nvmem, ++ NULL, ++}; ++ ++static const struct attribute_group nvmem_bin_rw_root_group = { ++ .bin_attrs = nvmem_bin_rw_root_attributes, ++}; ++ ++static const struct attribute_group *nvmem_rw_root_dev_groups[] = { ++ &nvmem_bin_rw_root_group, ++ NULL, ++}; ++ ++/* read only permission, root only */ ++static struct bin_attribute bin_attr_ro_root_nvmem = { ++ .attr = { ++ .name = "nvmem", ++ .mode = S_IRUSR, ++ }, ++ .read = bin_attr_nvmem_read, ++}; ++ ++static struct bin_attribute *nvmem_bin_ro_root_attributes[] = { ++ &bin_attr_ro_root_nvmem, ++ NULL, ++}; ++ ++static const struct attribute_group nvmem_bin_ro_root_group = { ++ .bin_attrs = nvmem_bin_ro_root_attributes, ++}; ++ ++static const struct attribute_group *nvmem_ro_root_dev_groups[] = { ++ &nvmem_bin_ro_root_group, ++ NULL, ++}; ++ + static void nvmem_release(struct device *dev) + { + struct nvmem_device *nvmem = to_nvmem_device(dev); +@@ -355,8 +402,14 @@ struct nvmem_device *nvmem_register(cons + nvmem->read_only = of_property_read_bool(np, "read-only") | + config->read_only; + +- nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups : +- nvmem_rw_dev_groups; ++ if (config->root_only) ++ nvmem->dev.groups = nvmem->read_only ? ++ nvmem_ro_root_dev_groups : ++ nvmem_rw_root_dev_groups; ++ else ++ nvmem->dev.groups = nvmem->read_only ? ++ nvmem_ro_dev_groups : ++ nvmem_rw_dev_groups; + + device_initialize(&nvmem->dev); + +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -23,6 +23,7 @@ struct nvmem_config { + const struct nvmem_cell_info *cells; + int ncells; + bool read_only; ++ bool root_only; + }; + + #if IS_ENABLED(CONFIG_NVMEM) diff --git a/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch b/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch new file mode 100644 index 000000000..6344d0ed0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch @@ -0,0 +1,181 @@ +From b6c217ab9be6895384cf0b284ace84ad79e5c53b Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +Date: Fri, 26 Feb 2016 20:59:19 +0100 +Subject: nvmem: Add backwards compatibility support for older EEPROM drivers. + +Older drivers made an 'eeprom' file available in the /sys device +directory. Have the NVMEM core provide this to retain backwards +compatibility. + +Signed-off-by: Andrew Lunn +Acked-by: Srinivas Kandagatla +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 84 ++++++++++++++++++++++++++++++++++++++---- + include/linux/nvmem-provider.h | 4 +- + 2 files changed, 79 insertions(+), 9 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -38,8 +38,13 @@ struct nvmem_device { + int users; + size_t size; + bool read_only; ++ int flags; ++ struct bin_attribute eeprom; ++ struct device *base_dev; + }; + ++#define FLAG_COMPAT BIT(0) ++ + struct nvmem_cell { + const char *name; + int offset; +@@ -56,16 +61,26 @@ static DEFINE_IDA(nvmem_ida); + static LIST_HEAD(nvmem_cells); + static DEFINE_MUTEX(nvmem_cells_mutex); + ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++static struct lock_class_key eeprom_lock_key; ++#endif ++ + #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) + + static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) + { +- struct device *dev = container_of(kobj, struct device, kobj); +- struct nvmem_device *nvmem = to_nvmem_device(dev); ++ struct device *dev; ++ struct nvmem_device *nvmem; + int rc; + ++ if (attr->private) ++ dev = attr->private; ++ else ++ dev = container_of(kobj, struct device, kobj); ++ nvmem = to_nvmem_device(dev); ++ + /* Stop the user from reading */ + if (pos >= nvmem->size) + return 0; +@@ -90,10 +105,16 @@ static ssize_t bin_attr_nvmem_write(stru + struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) + { +- struct device *dev = container_of(kobj, struct device, kobj); +- struct nvmem_device *nvmem = to_nvmem_device(dev); ++ struct device *dev; ++ struct nvmem_device *nvmem; + int rc; + ++ if (attr->private) ++ dev = attr->private; ++ else ++ dev = container_of(kobj, struct device, kobj); ++ nvmem = to_nvmem_device(dev); ++ + /* Stop the user from writing */ + if (pos >= nvmem->size) + return 0; +@@ -349,6 +370,43 @@ err: + return rval; + } + ++/* ++ * nvmem_setup_compat() - Create an additional binary entry in ++ * drivers sys directory, to be backwards compatible with the older ++ * drivers/misc/eeprom drivers. ++ */ ++static int nvmem_setup_compat(struct nvmem_device *nvmem, ++ const struct nvmem_config *config) ++{ ++ int rval; ++ ++ if (!config->base_dev) ++ return -EINVAL; ++ ++ if (nvmem->read_only) ++ nvmem->eeprom = bin_attr_ro_root_nvmem; ++ else ++ nvmem->eeprom = bin_attr_rw_root_nvmem; ++ nvmem->eeprom.attr.name = "eeprom"; ++ nvmem->eeprom.size = nvmem->size; ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ nvmem->eeprom.attr.key = &eeprom_lock_key; ++#endif ++ nvmem->eeprom.private = &nvmem->dev; ++ nvmem->base_dev = config->base_dev; ++ ++ rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom); ++ if (rval) { ++ dev_err(&nvmem->dev, ++ "Failed to create eeprom binary file %d\n", rval); ++ return rval; ++ } ++ ++ nvmem->flags |= FLAG_COMPAT; ++ ++ return 0; ++} ++ + /** + * nvmem_register() - Register a nvmem device for given nvmem_config. + * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem +@@ -416,16 +474,23 @@ struct nvmem_device *nvmem_register(cons + dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + + rval = device_add(&nvmem->dev); +- if (rval) { +- ida_simple_remove(&nvmem_ida, nvmem->id); +- kfree(nvmem); +- return ERR_PTR(rval); ++ if (rval) ++ goto out; ++ ++ if (config->compat) { ++ rval = nvmem_setup_compat(nvmem, config); ++ if (rval) ++ goto out; + } + + if (config->cells) + nvmem_add_cells(nvmem, config); + + return nvmem; ++out: ++ ida_simple_remove(&nvmem_ida, nvmem->id); ++ kfree(nvmem); ++ return ERR_PTR(rval); + } + EXPORT_SYMBOL_GPL(nvmem_register); + +@@ -445,6 +510,9 @@ int nvmem_unregister(struct nvmem_device + } + mutex_unlock(&nvmem_mutex); + ++ if (nvmem->flags & FLAG_COMPAT) ++ device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); ++ + nvmem_device_remove_all_cells(nvmem); + device_del(&nvmem->dev); + +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -24,6 +24,9 @@ struct nvmem_config { + int ncells; + bool read_only; + bool root_only; ++ /* To be only used by old driver/misc/eeprom drivers */ ++ bool compat; ++ struct device *base_dev; + }; + + #if IS_ENABLED(CONFIG_NVMEM) +@@ -44,5 +47,4 @@ static inline int nvmem_unregister(struc + } + + #endif /* CONFIG_NVMEM */ +- + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch new file mode 100644 index 000000000..9c33bad5c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch @@ -0,0 +1,61 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -4,14 +4,6 @@ + model = "Qualcomm IPQ8064/AP148"; + compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; + +- aliases { +- serial0 = &gsbi4_serial; +- }; +- +- chosen { +- stdout-path = "serial0:115200n8"; +- }; +- + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; +@@ -22,6 +14,14 @@ + }; + }; + ++ alias { ++ serial0 = &uart4; ++ }; ++ ++ chosen { ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ + soc { + pinmux@800000 { + i2c4_pins: i2c4_pinmux { +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -159,7 +159,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@12490000 { ++ uart2: serial@12490000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12490000 0x1000>, + <0x12480000 0x1000>; +@@ -197,7 +197,7 @@ + + syscon-tcsr = <&tcsr>; + +- gsbi4_serial: serial@16340000 { ++ uart4: serial@16340000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16340000 0x1000>, + <0x16300000 0x1000>; +@@ -234,7 +234,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@1a240000 { ++ uart5: serial@1a240000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x1a240000 0x1000>, + <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch new file mode 100644 index 000000000..bfdb30fe1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch @@ -0,0 +1,19 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -77,15 +77,7 @@ + spi-max-frequency = <50000000>; + reg = <0>; + +- partition@0 { +- label = "rootfs"; +- reg = <0x0 0x1000000>; +- }; +- +- partition@1 { +- label = "scratch"; +- reg = <0x1000000 0x1000000>; +- }; ++ linux,part-probe = "qcom-smem"; + }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch new file mode 100644 index 000000000..0845d3f21 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch @@ -0,0 +1,160 @@ +From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 7 Apr 2015 19:58:58 -0700 +Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree + +Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, +SATA, USB2, USB3 and NOR flash. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++ + 2 files changed, 133 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -506,6 +506,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8084-ifc6540.dtb \ + qcom-apq8084-mtp.dtb \ + qcom-ipq8064-ap148.dtb \ ++ qcom-ipq8064-db149.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -0,0 +1,132 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++/ { ++ model = "Qualcomm IPQ8064/DB149"; ++ compatible = "qcom,ipq8064-db149", "qcom,ipq8064"; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ rsvd@41200000 { ++ reg = <0x41200000 0x300000>; ++ no-map; ++ }; ++ }; ++ ++ alias { ++ serial0 = &uart2; ++ }; ++ ++ chosen { ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ ++ soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ bias-disable; ++ }; ++ ++ spi_pins: spi_pins { ++ mux { ++ pins = "gpio18", "gpio19", "gpio21"; ++ function = "gsbi5"; ++ drive-strength = <10>; ++ bias-none; ++ }; ++ }; ++ }; ++ ++ gsbi2: gsbi@12480000 { ++ qcom,mode = ; ++ status = "ok"; ++ uart2: serial@12490000 { ++ status = "ok"; ++ }; ++ }; ++ ++ gsbi5: gsbi@1a200000 { ++ qcom,mode = ; ++ status = "ok"; ++ ++ spi4: spi@1a280000 { ++ status = "ok"; ++ spi-max-frequency = <50000000>; ++ ++ pinctrl-0 = <&spi_pins>; ++ pinctrl-names = "default"; ++ ++ cs-gpios = <&qcom_pinmux 20 0>; ++ ++ flash: m25p80@0 { ++ compatible = "s25fl256s1"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <50000000>; ++ reg = <0>; ++ m25p,fast-read; ++ ++ partition@0 { ++ label = "lowlevel_init"; ++ reg = <0x0 0x1b0000>; ++ }; ++ ++ partition@1 { ++ label = "u-boot"; ++ reg = <0x1b0000 0x80000>; ++ }; ++ ++ partition@2 { ++ label = "u-boot-env"; ++ reg = <0x230000 0x40000>; ++ }; ++ ++ partition@3 { ++ label = "caldata"; ++ reg = <0x270000 0x40000>; ++ }; ++ ++ partition@4 { ++ label = "firmware"; ++ reg = <0x2b0000 0x1d50000>; ++ }; ++ }; ++ }; ++ }; ++ ++ sata-phy@1b400000 { ++ status = "ok"; ++ }; ++ ++ sata@29000000 { ++ status = "ok"; ++ }; ++ ++ phy@100f8800 { /* USB3 port 1 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@100f8830 { /* USB3 port 1 SS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8800 { /* USB3 port 0 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8830 { /* USB3 port 0 SS phy */ ++ status = "ok"; ++ }; ++ ++ usb30@0 { ++ status = "ok"; ++ }; ++ ++ usb30@1 { ++ status = "ok"; ++ }; ++ }; ++}; diff --git a/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch new file mode 100644 index 000000000..705306d9f --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch @@ -0,0 +1,52 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -47,14 +47,12 @@ + status = "ok"; + }; + +- i2c4: i2c@16380000 { +- status = "ok"; +- +- clock-frequency = <200000>; +- +- pinctrl-0 = <&i2c4_pins>; +- pinctrl-names = "default"; +- }; ++ /* ++ * The i2c device on gsbi4 should not be enabled. ++ * On ipq806x designs gsbi4 i2c is meant for exclusive ++ * RPM usage. Turning this on in kernel manifests as ++ * i2c failure for the RPM. ++ */ + }; + + gsbi5: gsbi@1a200000 { +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, +- .flags = CLK_SET_PARENT_GATE, ++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk + }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -890,7 +890,7 @@ static struct clk_branch gsbi1_h_clk = { + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_h_clk", + .ops = &clk_branch_ops, +- .flags = CLK_IS_ROOT, ++ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch new file mode 100644 index 000000000..f026ed939 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch @@ -0,0 +1,14 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -4,6 +4,11 @@ + model = "Qualcomm IPQ8064/AP148"; + compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; + ++ memory@0 { ++ reg = <0x42000000 0x1e000000>; ++ device_type = "memory"; ++ }; ++ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; diff --git a/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch b/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch new file mode 100644 index 000000000..cb2d30397 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/030-ARM-qcom-select-ARM_CPU_SUSPEND-for-power-management.patch @@ -0,0 +1,30 @@ +From: Arnd Bergmann +Date: Tue, 24 Nov 2015 23:13:09 +0100 +Subject: [PATCH] ARM: qcom: select ARM_CPU_SUSPEND for power management + +The qcom spm driver uses cpu_resume_arm(), which is not included +in the kernel in all configurations: + +drivers/built-in.o: In function `qcom_cpu_spc': +:(.text+0xbc022): undefined reference to `cpu_suspend' +drivers/built-in.o: In function `qcom_cpuidle_init': +:(.init.text+0x610c): undefined reference to `cpu_resume_arm' + +This adds a 'select' Kconfig statement to ensure it's always +enabled. + +Signed-off-by: Arnd Bergmann +Reviewed-by: Stephen Boyd +Signed-off-by: Andy Gross +--- + +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -13,6 +13,7 @@ config QCOM_GSBI + config QCOM_PM + bool "Qualcomm Power Management" + depends on ARCH_QCOM && !ARM64 ++ select ARM_CPU_SUSPEND + select QCOM_SCM + help + QCOM Platform specific power driver to manage cores and L2 low power diff --git a/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch new file mode 100644 index 000000000..e1d317de4 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch @@ -0,0 +1,33 @@ +From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 13 Aug 2015 09:45:26 -0700 +Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts + +Add one new node to the ipq806x.dtsi file to declare & register the +hardware spinlock devices. This mechanism is required to be used by +other drivers such as SMEM. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -329,5 +329,16 @@ + #reset-cells = <1>; + }; + ++ sfpb_mutex_block: syscon@1200600 { ++ compatible = "syscon"; ++ reg = <0x01200600 0x100>; ++ }; ++ }; ++ ++ sfpb_mutex: sfpb-mutex { ++ compatible = "qcom,sfpb-mutex"; ++ syscon = <&sfpb_mutex_block 4 4>; ++ ++ #hwlock-cells = <1>; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch new file mode 100644 index 000000000..06b14052e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch @@ -0,0 +1,36 @@ +From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Fri, 14 Aug 2015 11:17:20 -0700 +Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts + +SMEM is used on IPQ806x to store various board related information such +as boot device and flash partition layout. We'll declare it as a device +so we can make use of it thanks to the new SMEM soc driver. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -55,7 +55,7 @@ + no-map; + }; + +- smem@41000000 { ++ smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; +@@ -341,4 +341,10 @@ + + #hwlock-cells = <1>; + }; ++ ++ smem { ++ compatible = "qcom,smem"; ++ memory-region = <&smem>; ++ hwlocks = <&sfpb_mutex 3>; ++ }; + }; diff --git a/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch new file mode 100644 index 000000000..7c0c80356 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch @@ -0,0 +1,275 @@ +From 61e8e1b1af77f24339da3f0822a76fa65ed635c6 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 13 Aug 2015 09:53:14 -0700 +Subject: [PATCH] mtd: add SMEM parser for QCOM platforms + +On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is +used to store partition layout. This new parser can now be used to read +SMEM and use it to register an MTD layout according to its content. + +Signed-off-by: Mathieu Olivari +Signed-off-by: Ram Chandra Jangir +--- + drivers/mtd/Kconfig | 7 ++ + drivers/mtd/Makefile | 1 + + drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 236 insertions(+) + create mode 100644 drivers/mtd/qcom_smem_part.c + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -190,6 +190,13 @@ config MTD_MYLOADER_PARTS + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. + ++config MTD_QCOM_SMEM_PARTS ++ tristate "QCOM SMEM partitioning support" ++ depends on QCOM_SMEM ++ help ++ This provides partitions parser for QCOM devices using SMEM ++ such as IPQ806x. ++ + comment "User Modules And Translation Layers" + + # +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o ++obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o +--- /dev/null ++++ b/drivers/mtd/qcom_smem_part.c +@@ -0,0 +1,228 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Processor/host identifier for the application processor */ ++#define SMEM_HOST_APPS 0 ++ ++/* SMEM items index */ ++#define SMEM_AARM_PARTITION_TABLE 9 ++#define SMEM_BOOT_FLASH_TYPE 421 ++#define SMEM_BOOT_FLASH_BLOCK_SIZE 424 ++ ++/* SMEM Flash types */ ++#define SMEM_FLASH_NAND 2 ++#define SMEM_FLASH_SPI 6 ++ ++#define SMEM_PART_NAME_SZ 16 ++#define SMEM_PARTS_MAX 32 ++ ++struct smem_partition { ++ char name[SMEM_PART_NAME_SZ]; ++ __le32 start; ++ __le32 size; ++ __le32 attr; ++}; ++ ++struct smem_partition_table { ++ u8 magic[8]; ++ __le32 version; ++ __le32 len; ++ struct smem_partition parts[SMEM_PARTS_MAX]; ++}; ++ ++/* SMEM Magic values in partition table */ ++static const u8 SMEM_PTABLE_MAGIC[] = { ++ 0xaa, 0x73, 0xee, 0x55, ++ 0xdb, 0xbd, 0x5e, 0xe3, ++}; ++ ++static int qcom_smem_get_flash_blksz(u64 **smem_blksz) ++{ ++ size_t size; ++ ++ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, ++ &size); ++ ++ if (IS_ERR(*smem_blksz)) { ++ pr_err("Unable to read flash blksz from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ if (size != sizeof(**smem_blksz)) { ++ pr_err("Invalid flash blksz size in SMEM\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_get_flash_type(u64 **smem_flash_type) ++{ ++ size_t size; ++ ++ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, ++ &size); ++ ++ if (IS_ERR(*smem_flash_type)) { ++ pr_err("Unable to read flash type from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ if (size != sizeof(**smem_flash_type)) { ++ pr_err("Invalid flash type size in SMEM\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) ++{ ++ size_t size; ++ ++ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, ++ &size); ++ ++ if (IS_ERR(*pparts)) { ++ pr_err("Unable to read partition table from SMEM\n"); ++ return -ENOENT; ++ } ++ ++ return 0; ++} ++ ++static int of_dev_node_match(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static bool is_spi_device(struct device_node *np) ++{ ++ struct device *dev; ++ ++ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); ++ if (!dev) ++ return false; ++ ++ put_device(dev); ++ return true; ++} ++ ++static int parse_qcom_smem_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct smem_partition_table *smem_parts; ++ u64 *smem_flash_type, *smem_blksz; ++ struct mtd_partition *mtd_parts; ++ struct device_node *of_node = data->of_node; ++ int i, ret; ++ ++ /* ++ * SMEM will only store the partition table of the boot device. ++ * If this is not the boot device, do not return any partition. ++ */ ++ ret = qcom_smem_get_flash_type(&smem_flash_type); ++ if (ret < 0) ++ return ret; ++ ++ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) ++ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) ++ return 0; ++ ++ /* ++ * Just for sanity purpose, make sure the block size in SMEM matches the ++ * block size of the MTD device ++ */ ++ ret = qcom_smem_get_flash_blksz(&smem_blksz); ++ if (ret < 0) ++ return ret; ++ ++ if (*smem_blksz != master->erasesize) { ++ pr_err("SMEM block size differs from MTD block size\n"); ++ return -EINVAL; ++ } ++ ++ /* Get partition pointer from SMEM */ ++ ret = qcom_smem_get_flash_partitions(&smem_parts); ++ if (ret < 0) ++ return ret; ++ ++ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, ++ sizeof(SMEM_PTABLE_MAGIC))) { ++ pr_err("SMEM partition magic invalid\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate and populate the mtd structures */ ++ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), ++ GFP_KERNEL); ++ if (!mtd_parts) ++ return -ENOMEM; ++ ++ for (i = 0; i < smem_parts->len; i++) { ++ struct smem_partition *s_part = &smem_parts->parts[i]; ++ struct mtd_partition *m_part = &mtd_parts[i]; ++ ++ m_part->name = s_part->name; ++ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); ++ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); ++ ++ /* ++ * The last SMEM partition may have its size marked as ++ * something like 0xffffffff, which means "until the end of the ++ * flash device". In this case, truncate it. ++ */ ++ if (m_part->offset + m_part->size > master->size) ++ m_part->size = master->size - m_part->offset; ++ } ++ ++ *pparts = mtd_parts; ++ ++ return smem_parts->len; ++} ++ ++static struct mtd_part_parser qcom_smem_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_qcom_smem_partitions, ++ .name = "qcom-smem", ++}; ++ ++static int __init qcom_smem_parser_init(void) ++{ ++ register_mtd_parser(&qcom_smem_parser); ++ return 0; ++} ++ ++static void __exit qcom_smem_parser_exit(void) ++{ ++ deregister_mtd_parser(&qcom_smem_parser); ++} ++ ++module_init(qcom_smem_parser_init); ++module_exit(qcom_smem_parser_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mathieu Olivari "); ++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); diff --git a/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch b/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch new file mode 100644 index 000000000..6ace50cbc --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-01-usb-dwc3-core-purge-dev_dbg-calls.patch @@ -0,0 +1,42 @@ +From 1407bf13e3bf5f1168484c3e68b6ef9d8cf2bc72 Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Mon, 16 Nov 2015 16:06:37 -0600 +Subject: usb: dwc3: core: purge dev_dbg() calls + +The last few dev_dbg() messages are converted to +tracepoints and we can finally ignore dev_dbg() +messages during debug sessions. + +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(stru + + for (n = 0; n < dwc->num_event_buffers; n++) { + evt = dwc->ev_buffs[n]; +- dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", ++ dwc3_trace(trace_dwc3_core, ++ "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, + evt->length); + +@@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *d + reg |= DWC3_GCTL_GBLHIBERNATIONEN; + break; + default: +- dev_dbg(dwc->dev, "No power optimization available\n"); ++ dwc3_trace(trace_dwc3_core, "No power optimization available\n"); + } + + /* check if current dwc3 is on simulation board */ + if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { +- dev_dbg(dwc->dev, "it is on FPGA board\n"); ++ dwc3_trace(trace_dwc3_core, ++ "running on FPGA platform\n"); + dwc->is_fpga = true; + } + diff --git a/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch b/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch new file mode 100644 index 000000000..de0e47fc1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-02-usb-dwc3-Update-maximum_speed-for-SuperSpeedPlus.patch @@ -0,0 +1,52 @@ +From 2c7f1bd9127a1a49ee25d9c2b2ce17b11c7fb05f Mon Sep 17 00:00:00 2001 +From: John Youn +Date: Fri, 5 Feb 2016 17:08:59 -0800 +Subject: usb: dwc3: Update maximum_speed for SuperSpeedPlus + +If the maximum_speed is not set, set it to a known value, either +SuperSpeed or SuperSpeedPlus based on the type of controller we are +using. If we are on DWC_usb31 controller, check the PHY interface to see +if it is capable of SuperSpeedPlus. + +Also this check is moved after dwc3_core_init() so that we can check +dwc->revision. + +Signed-off-by: John Youn +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -962,10 +962,6 @@ static int dwc3_probe(struct platform_de + fladj = pdata->fladj_value; + } + +- /* default to superspeed if no maximum_speed passed */ +- if (dwc->maximum_speed == USB_SPEED_UNKNOWN) +- dwc->maximum_speed = USB_SPEED_SUPER; +- + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + +@@ -1016,6 +1012,19 @@ static int dwc3_probe(struct platform_de + goto err1; + } + ++ /* default to superspeed if no maximum_speed passed */ ++ if (dwc->maximum_speed == USB_SPEED_UNKNOWN) { ++ dwc->maximum_speed = USB_SPEED_SUPER; ++ ++ /* ++ * default to superspeed plus if we are capable. ++ */ ++ if (dwc3_is_usb31(dwc) && ++ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == ++ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) ++ dwc->maximum_speed = USB_SPEED_SUPER_PLUS; ++ } ++ + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc, fladj); + diff --git a/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch b/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch new file mode 100644 index 000000000..784ba069e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-03-usb-dwc3-Validate-the-maximum_speed-parameter.patch @@ -0,0 +1,68 @@ +From 77966eb85e6d988a6daaf8ac14ac33026ceb3ab7 Mon Sep 17 00:00:00 2001 +From: John Youn +Date: Fri, 19 Feb 2016 17:31:01 -0800 +Subject: usb: dwc3: Validate the maximum_speed parameter + +Check that dwc->maximum_speed is set to a valid value. Also add an error +when we use it later if we encounter an invalid value. + +Signed-off-by: John Youn +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 18 ++++++++++++++++-- + drivers/usb/dwc3/gadget.c | 9 ++++++--- + 2 files changed, 22 insertions(+), 5 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1012,8 +1012,20 @@ static int dwc3_probe(struct platform_de + goto err1; + } + +- /* default to superspeed if no maximum_speed passed */ +- if (dwc->maximum_speed == USB_SPEED_UNKNOWN) { ++ /* Check the maximum_speed parameter */ ++ switch (dwc->maximum_speed) { ++ case USB_SPEED_LOW: ++ case USB_SPEED_FULL: ++ case USB_SPEED_HIGH: ++ case USB_SPEED_SUPER: ++ case USB_SPEED_SUPER_PLUS: ++ break; ++ default: ++ dev_err(dev, "invalid maximum_speed parameter %d\n", ++ dwc->maximum_speed); ++ /* fall through */ ++ case USB_SPEED_UNKNOWN: ++ /* default to superspeed */ + dwc->maximum_speed = USB_SPEED_SUPER; + + /* +@@ -1023,6 +1035,8 @@ static int dwc3_probe(struct platform_de + (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; ++ ++ break; + } + + /* Adjust Frame Length */ +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -1647,10 +1647,13 @@ static int dwc3_gadget_start(struct usb_ + case USB_SPEED_HIGH: + reg |= DWC3_DSTS_HIGHSPEED; + break; +- case USB_SPEED_SUPER: /* FALLTHROUGH */ +- case USB_SPEED_UNKNOWN: /* FALTHROUGH */ + default: +- reg |= DWC3_DSTS_SUPERSPEED; ++ dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n", ++ dwc->maximum_speed); ++ /* fall through */ ++ case USB_SPEED_SUPER: ++ reg |= DWC3_DCFG_SUPERSPEED; ++ break; + } + } + dwc3_writel(dwc->regs, DWC3_DCFG, reg); diff --git a/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch b/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch new file mode 100644 index 000000000..b6f583053 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-04-usb-dwc3-DWC_usb31-controller-check.patch @@ -0,0 +1,28 @@ +From c4137a9c841ec7fb300782d211f2d6907f4d6e20 Mon Sep 17 00:00:00 2001 +From: John Youn +Date: Fri, 5 Feb 2016 17:08:18 -0800 +Subject: usb: dwc3: DWC_usb31 controller check + +Add a convenience function to check if the controller is DWC_usb31. + +Signed-off-by: John Youn +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1019,6 +1019,12 @@ struct dwc3_gadget_ep_cmd_params { + void dwc3_set_mode(struct dwc3 *dwc, u32 mode); + int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); + ++/* check whether we are on the DWC_usb31 core */ ++static inline bool dwc3_is_usb31(struct dwc3 *dwc) ++{ ++ return !!(dwc->revision & DWC3_REVISION_IS_DWC31); ++} ++ + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) + int dwc3_host_init(struct dwc3 *dwc); + void dwc3_host_exit(struct dwc3 *dwc); diff --git a/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch b/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch new file mode 100644 index 000000000..264893d53 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-05-usb-dwc3-Update-register-fields-for-SuperSpeedPlus.patch @@ -0,0 +1,42 @@ +From 1f38f88a24c86d46cf47782ffabd5457f231f8ca Mon Sep 17 00:00:00 2001 +From: John Youn +Date: Fri, 5 Feb 2016 17:08:31 -0800 +Subject: usb: dwc3: Update register fields for SuperSpeedPlus + +Update various registers fields definitions for the DWC_usb31 controller +for SuperSpeedPlus support. + +Signed-off-by: John Youn +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -220,7 +220,8 @@ + /* Global HWPARAMS3 Register */ + #define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) + #define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +-#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 ++#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1 ++#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */ + #define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) + #define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 + #define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +@@ -246,6 +247,7 @@ + #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + + #define DWC3_DCFG_SPEED_MASK (7 << 0) ++#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ + #define DWC3_DCFG_SUPERSPEED (4 << 0) + #define DWC3_DCFG_HIGHSPEED (0 << 0) + #define DWC3_DCFG_FULLSPEED2 (1 << 0) +@@ -336,6 +338,7 @@ + + #define DWC3_DSTS_CONNECTSPD (7 << 0) + ++#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ + #define DWC3_DSTS_SUPERSPEED (4 << 0) + #define DWC3_DSTS_HIGHSPEED (0 << 0) + #define DWC3_DSTS_FULLSPEED2 (1 << 0) diff --git a/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch b/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch new file mode 100644 index 000000000..428a796e0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-06-usb-dwc3-core-improve-reset-sequence.patch @@ -0,0 +1,100 @@ +From f59dcab176293b646e1358144c93c58c3cda2813 Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Fri, 11 Mar 2016 10:51:52 +0200 +Subject: usb: dwc3: core: improve reset sequence + +According to Synopsys Databook, we shouldn't be +relying on GCTL.CORESOFTRESET bit as that's only for +debugging purposes. Instead, let's use DCTL.CSFTRST +if we're OTG or PERIPHERAL mode. + +Host side block will be reset by XHCI driver if +necessary. Note that this reduces amount of time +spent on dwc3_probe() by a long margin. + +We're still gonna wait for reset to finish for a +long time (default to 1ms max), but tests show that +the reset polling loop executed at most 19 times +(modprobe dwc3 && modprobe -r dwc3 executed 1000 +times in a row). + +Suggested-by: Mian Yousaf Kaukab +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 48 ++++++++++++++++++------------------------------ + 1 file changed, 18 insertions(+), 30 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -67,23 +67,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 + static int dwc3_core_soft_reset(struct dwc3 *dwc) + { + u32 reg; ++ int retries = 1000; + int ret; + +- /* Before Resetting PHY, put Core in Reset */ +- reg = dwc3_readl(dwc->regs, DWC3_GCTL); +- reg |= DWC3_GCTL_CORESOFTRESET; +- dwc3_writel(dwc->regs, DWC3_GCTL, reg); +- +- /* Assert USB3 PHY reset */ +- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); +- reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; +- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +- +- /* Assert USB2 PHY reset */ +- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); +- reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; +- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +- + usb_phy_init(dwc->usb2_phy); + usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); +@@ -95,26 +81,28 @@ static int dwc3_core_soft_reset(struct d + phy_exit(dwc->usb2_generic_phy); + return ret; + } +- mdelay(100); + +- /* Clear USB3 PHY reset */ +- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); +- reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; +- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +- +- /* Clear USB2 PHY reset */ +- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); +- reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; +- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +- +- mdelay(100); +- +- /* After PHYs are stable we can take Core out of reset state */ +- reg = dwc3_readl(dwc->regs, DWC3_GCTL); +- reg &= ~DWC3_GCTL_CORESOFTRESET; +- dwc3_writel(dwc->regs, DWC3_GCTL, reg); ++ /* ++ * We're resetting only the device side because, if we're in host mode, ++ * XHCI driver will reset the host block. If dwc3 was configured for ++ * host-only mode, then we can return early. ++ */ ++ if (dwc->dr_mode == USB_DR_MODE_HOST) ++ return 0; ++ ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ reg |= DWC3_DCTL_CSFTRST; ++ dwc3_writel(dwc->regs, DWC3_DCTL, reg); ++ ++ do { ++ reg = dwc3_readl(dwc->regs, DWC3_DCTL); ++ if (!(reg & DWC3_DCTL_CSFTRST)) ++ return 0; ++ ++ udelay(1); ++ } while (--retries); + +- return 0; ++ return -ETIMEDOUT; + } + + /** diff --git a/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch b/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch new file mode 100644 index 000000000..132d131df --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-07-usb-dwc3-drop-FIFO-resizing-logic.patch @@ -0,0 +1,242 @@ +From bc5081617faeb3b2f0c126dc37264b87af7da47f Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Thu, 4 Feb 2016 14:18:01 +0200 +Subject: usb: dwc3: drop FIFO resizing logic + +That FIFO resizing logic was added to support OMAP5 +ES1.0 which had a bogus default FIFO size. I can't +remember the exact size of default FIFO, but it was +less than one bulk superspeed packet (<1024) which +would prevent USB3 from ever working on OMAP5 ES1.0. + +However, OMAP5 ES1.0 support has been dropped by +commit aa2f4b16f830 ("ARM: OMAP5: id: Remove ES1.0 +support") which renders FIFO resizing unnecessary. + +Tested-by: Kishon Vijay Abraham I +Signed-off-by: Felipe Balbi +--- + Documentation/devicetree/bindings/usb/dwc3.txt | 4 +- + .../devicetree/bindings/usb/qcom,dwc3.txt | 1 - + drivers/usb/dwc3/core.c | 4 - + drivers/usb/dwc3/core.h | 5 -- + drivers/usb/dwc3/ep0.c | 9 --- + drivers/usb/dwc3/gadget.c | 86 ---------------------- + drivers/usb/dwc3/platform_data.h | 1 - + 7 files changed, 2 insertions(+), 108 deletions(-) + +--- a/Documentation/devicetree/bindings/usb/dwc3.txt ++++ b/Documentation/devicetree/bindings/usb/dwc3.txt +@@ -14,7 +14,6 @@ Optional properties: + the second element is expected to be a handle to the USB3/SS PHY + - phys: from the *Generic PHY* bindings + - phy-names: from the *Generic PHY* bindings +- - tx-fifo-resize: determines if the FIFO *has* to be reallocated. + - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable + - snps,disable_scramble_quirk: true when SW should disable data scrambling. + Only really useful for FPGA builds. +@@ -47,6 +46,8 @@ Optional properties: + register for post-silicon frame length adjustment when the + fladj_30mhz_sdbnd signal is invalid or incorrect. + ++ - tx-fifo-resize: determines if the FIFO *has* to be reallocated. ++ + This is usually a subnode to DWC3 glue to which it is connected. + + dwc3@4a030000 { +@@ -54,5 +55,4 @@ dwc3@4a030000 { + reg = <0x4a030000 0xcfff>; + interrupts = <0 92 4> + usb-phy = <&usb2_phy>, <&usb3,phy>; +- tx-fifo-resize; + }; +--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt ++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +@@ -59,7 +59,6 @@ Example device nodes: + interrupts = <0 205 0x4>; + phys = <&hs_phy>, <&ss_phy>; + phy-names = "usb2-phy", "usb3-phy"; +- tx-fifo-resize; + dr_mode = "host"; + }; + }; +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -882,9 +882,6 @@ static int dwc3_probe(struct platform_de + dwc->usb3_lpm_capable = device_property_read_bool(dev, + "snps,usb3_lpm_capable"); + +- dwc->needs_fifo_resize = device_property_read_bool(dev, +- "tx-fifo-resize"); +- + dwc->disable_scramble_quirk = device_property_read_bool(dev, + "snps,disable_scramble_quirk"); + dwc->u2exit_lfps_quirk = device_property_read_bool(dev, +@@ -926,7 +923,6 @@ static int dwc3_probe(struct platform_de + if (pdata->hird_threshold) + hird_threshold = pdata->hird_threshold; + +- dwc->needs_fifo_resize = pdata->tx_fifo_resize; + dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; + dwc->dr_mode = pdata->dr_mode; + +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -705,9 +705,7 @@ struct dwc3_scratchpad_array { + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n + * @is_fpga: true when we are using the FPGA board +- * @needs_fifo_resize: not all users might want fifo resizing, flag it + * @pullups_connected: true when Run/Stop bit is set +- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @start_config_issued: true when StartConfig command has been issued + * @three_stage_setup: set if we perform a three phase setup +@@ -850,9 +848,7 @@ struct dwc3 { + unsigned has_lpm_erratum:1; + unsigned is_utmi_l1_suspend:1; + unsigned is_fpga:1; +- unsigned needs_fifo_resize:1; + unsigned pullups_connected:1; +- unsigned resize_fifos:1; + unsigned setup_packet_pending:1; + unsigned three_stage_setup:1; + unsigned usb3_lpm_capable:1; +@@ -1020,7 +1016,6 @@ struct dwc3_gadget_ep_cmd_params { + + /* prototypes */ + void dwc3_set_mode(struct dwc3 *dwc, u32 mode); +-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); + + /* check whether we are on the DWC_usb31 core */ + static inline bool dwc3_is_usb31(struct dwc3 *dwc) +--- a/drivers/usb/dwc3/ep0.c ++++ b/drivers/usb/dwc3/ep0.c +@@ -587,9 +587,6 @@ static int dwc3_ep0_set_config(struct dw + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); +- +- dwc->resize_fifos = true; +- dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); + } + break; + +@@ -1028,12 +1025,6 @@ static int dwc3_ep0_start_control_status + + static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) + { +- if (dwc->resize_fifos) { +- dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); +- dwc3_gadget_resize_tx_fifos(dwc); +- dwc->resize_fifos = 0; +- } +- + WARN_ON(dwc3_ep0_start_control_status(dep)); + } + +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -145,92 +145,6 @@ int dwc3_gadget_set_link_state(struct dw + return -ETIMEDOUT; + } + +-/** +- * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case +- * @dwc: pointer to our context structure +- * +- * This function will a best effort FIFO allocation in order +- * to improve FIFO usage and throughput, while still allowing +- * us to enable as many endpoints as possible. +- * +- * Keep in mind that this operation will be highly dependent +- * on the configured size for RAM1 - which contains TxFifo -, +- * the amount of endpoints enabled on coreConsultant tool, and +- * the width of the Master Bus. +- * +- * In the ideal world, we would always be able to satisfy the +- * following equation: +- * +- * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \ +- * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes +- * +- * Unfortunately, due to many variables that's not always the case. +- */ +-int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) +-{ +- int last_fifo_depth = 0; +- int ram1_depth; +- int fifo_size; +- int mdwidth; +- int num; +- +- if (!dwc->needs_fifo_resize) +- return 0; +- +- ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); +- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); +- +- /* MDWIDTH is represented in bits, we need it in bytes */ +- mdwidth >>= 3; +- +- /* +- * FIXME For now we will only allocate 1 wMaxPacketSize space +- * for each enabled endpoint, later patches will come to +- * improve this algorithm so that we better use the internal +- * FIFO space +- */ +- for (num = 0; num < dwc->num_in_eps; num++) { +- /* bit0 indicates direction; 1 means IN ep */ +- struct dwc3_ep *dep = dwc->eps[(num << 1) | 1]; +- int mult = 1; +- int tmp; +- +- if (!(dep->flags & DWC3_EP_ENABLED)) +- continue; +- +- if (usb_endpoint_xfer_bulk(dep->endpoint.desc) +- || usb_endpoint_xfer_isoc(dep->endpoint.desc)) +- mult = 3; +- +- /* +- * REVISIT: the following assumes we will always have enough +- * space available on the FIFO RAM for all possible use cases. +- * Make sure that's true somehow and change FIFO allocation +- * accordingly. +- * +- * If we have Bulk or Isochronous endpoints, we want +- * them to be able to be very, very fast. So we're giving +- * those endpoints a fifo_size which is enough for 3 full +- * packets +- */ +- tmp = mult * (dep->endpoint.maxpacket + mdwidth); +- tmp += mdwidth; +- +- fifo_size = DIV_ROUND_UP(tmp, mdwidth); +- +- fifo_size |= (last_fifo_depth << 16); +- +- dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d", +- dep->name, last_fifo_depth, fifo_size & 0xffff); +- +- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size); +- +- last_fifo_depth += (fifo_size & 0xffff); +- } +- +- return 0; +-} +- + void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) + { +--- a/drivers/usb/dwc3/platform_data.h ++++ b/drivers/usb/dwc3/platform_data.h +@@ -23,7 +23,6 @@ + struct dwc3_platform_data { + enum usb_device_speed maximum_speed; + enum usb_dr_mode dr_mode; +- bool tx_fifo_resize; + bool usb3_lpm_capable; + + unsigned is_utmi_l1_suspend:1; diff --git a/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch b/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch new file mode 100644 index 000000000..4fb406442 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-08-usb-dwc3-remove-num_event_buffers.patch @@ -0,0 +1,265 @@ +From 660e9bde74d6915227d7ee3485b11e5f52637b26 Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Wed, 30 Mar 2016 09:26:24 +0300 +Subject: usb: dwc3: remove num_event_buffers + +We never, ever route any of the other event buffers +so we might as well drop support for them. + +Until someone has a real, proper benefit for +multiple event buffers, we will rely on a single +one. This also helps reduce memory footprint of +dwc3.ko which won't allocate memory for the extra +event buffers. + +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 81 +++++++++++++++++++---------------------------- + drivers/usb/dwc3/core.h | 2 -- + drivers/usb/dwc3/gadget.c | 38 +++++++--------------- + 3 files changed, 44 insertions(+), 77 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -203,13 +203,10 @@ static struct dwc3_event_buffer *dwc3_al + static void dwc3_free_event_buffers(struct dwc3 *dwc) + { + struct dwc3_event_buffer *evt; +- int i; + +- for (i = 0; i < dwc->num_event_buffers; i++) { +- evt = dwc->ev_buffs[i]; +- if (evt) +- dwc3_free_one_event_buffer(dwc, evt); +- } ++ evt = dwc->ev_buffs[0]; ++ if (evt) ++ dwc3_free_one_event_buffer(dwc, evt); + } + + /** +@@ -222,27 +219,19 @@ static void dwc3_free_event_buffers(stru + */ + static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) + { +- int num; +- int i; +- +- num = DWC3_NUM_INT(dwc->hwparams.hwparams1); +- dwc->num_event_buffers = num; ++ struct dwc3_event_buffer *evt; + +- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, ++ dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs), + GFP_KERNEL); + if (!dwc->ev_buffs) + return -ENOMEM; + +- for (i = 0; i < num; i++) { +- struct dwc3_event_buffer *evt; +- +- evt = dwc3_alloc_one_event_buffer(dwc, length); +- if (IS_ERR(evt)) { +- dev_err(dwc->dev, "can't allocate event buffer\n"); +- return PTR_ERR(evt); +- } +- dwc->ev_buffs[i] = evt; ++ evt = dwc3_alloc_one_event_buffer(dwc, length); ++ if (IS_ERR(evt)) { ++ dev_err(dwc->dev, "can't allocate event buffer\n"); ++ return PTR_ERR(evt); + } ++ dwc->ev_buffs[0] = evt; + + return 0; + } +@@ -256,25 +245,22 @@ static int dwc3_alloc_event_buffers(stru + static int dwc3_event_buffers_setup(struct dwc3 *dwc) + { + struct dwc3_event_buffer *evt; +- int n; + +- for (n = 0; n < dwc->num_event_buffers; n++) { +- evt = dwc->ev_buffs[n]; +- dwc3_trace(trace_dwc3_core, +- "Event buf %p dma %08llx length %d\n", +- evt->buf, (unsigned long long) evt->dma, +- evt->length); +- +- evt->lpos = 0; +- +- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), +- lower_32_bits(evt->dma)); +- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), +- upper_32_bits(evt->dma)); +- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), +- DWC3_GEVNTSIZ_SIZE(evt->length)); +- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); +- } ++ evt = dwc->ev_buffs[0]; ++ dwc3_trace(trace_dwc3_core, ++ "Event buf %p dma %08llx length %d\n", ++ evt->buf, (unsigned long long) evt->dma, ++ evt->length); ++ ++ evt->lpos = 0; ++ ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), ++ lower_32_bits(evt->dma)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), ++ upper_32_bits(evt->dma)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), ++ DWC3_GEVNTSIZ_SIZE(evt->length)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + + return 0; + } +@@ -282,19 +268,16 @@ static int dwc3_event_buffers_setup(stru + static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) + { + struct dwc3_event_buffer *evt; +- int n; + +- for (n = 0; n < dwc->num_event_buffers; n++) { +- evt = dwc->ev_buffs[n]; ++ evt = dwc->ev_buffs[0]; + +- evt->lpos = 0; ++ evt->lpos = 0; + +- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); +- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); +- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK +- | DWC3_GEVNTSIZ_SIZE(0)); +- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); +- } ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0); ++ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK ++ | DWC3_GEVNTSIZ_SIZE(0)); ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0); + } + + static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -663,7 +663,6 @@ struct dwc3_scratchpad_array { + * @regs: base address for our registers + * @regs_size: address space size + * @nr_scratch: number of scratch buffers +- * @num_event_buffers: calculated number of event buffers + * @u1u2: only used on revisions <1.83a for workaround + * @maximum_speed: maximum speed requested (mainly for testing purposes) + * @revision: revision register contents +@@ -773,7 +772,6 @@ struct dwc3 { + u32 gctl; + + u32 nr_scratch; +- u32 num_event_buffers; + u32 u1u2; + u32 maximum_speed; + +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2569,14 +2569,14 @@ static void dwc3_process_event_entry(str + } + } + +-static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) ++static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc) + { + struct dwc3_event_buffer *evt; + irqreturn_t ret = IRQ_NONE; + int left; + u32 reg; + +- evt = dwc->ev_buffs[buf]; ++ evt = dwc->ev_buffs[0]; + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) +@@ -2601,7 +2601,7 @@ static irqreturn_t dwc3_process_event_bu + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + +- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4); + } + + evt->count = 0; +@@ -2609,9 +2609,9 @@ static irqreturn_t dwc3_process_event_bu + ret = IRQ_HANDLED; + + /* Unmask interrupt */ +- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); ++ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0)); + reg &= ~DWC3_GEVNTSIZ_INTMASK; +- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg); + + return ret; + } +@@ -2621,27 +2621,23 @@ static irqreturn_t dwc3_thread_interrupt + struct dwc3 *dwc = _dwc; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; +- int i; + + spin_lock_irqsave(&dwc->lock, flags); +- +- for (i = 0; i < dwc->num_event_buffers; i++) +- ret |= dwc3_process_event_buf(dwc, i); +- ++ ret = dwc3_process_event_buf(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; + } + +-static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf) ++static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc) + { + struct dwc3_event_buffer *evt; + u32 count; + u32 reg; + +- evt = dwc->ev_buffs[buf]; ++ evt = dwc->ev_buffs[0]; + +- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); ++ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return IRQ_NONE; +@@ -2650,9 +2646,9 @@ static irqreturn_t dwc3_check_event_buf( + evt->flags |= DWC3_EVENT_PENDING; + + /* Mask interrupt */ +- reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); ++ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0)); + reg |= DWC3_GEVNTSIZ_INTMASK; +- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); ++ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg); + + return IRQ_WAKE_THREAD; + } +@@ -2660,18 +2656,8 @@ static irqreturn_t dwc3_check_event_buf( + static irqreturn_t dwc3_interrupt(int irq, void *_dwc) + { + struct dwc3 *dwc = _dwc; +- int i; +- irqreturn_t ret = IRQ_NONE; +- +- for (i = 0; i < dwc->num_event_buffers; i++) { +- irqreturn_t status; + +- status = dwc3_check_event_buf(dwc, i); +- if (status == IRQ_WAKE_THREAD) +- ret = status; +- } +- +- return ret; ++ return dwc3_check_event_buf(dwc); + } + + /** diff --git a/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch b/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch new file mode 100644 index 000000000..6592a4eec --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-09-usb-dwc3-drop-ev_buffs-array.patch @@ -0,0 +1,96 @@ +From 696c8b1282205caa5206264449f80ef756f14ef7 Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Wed, 30 Mar 2016 09:37:03 +0300 +Subject: usb: dwc3: drop ev_buffs array + +we will be using a single event buffer and that +renders ev_buffs array unnecessary. Let's remove it +in favor of a single pointer to a single event +buffer. + +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 13 ++++--------- + drivers/usb/dwc3/core.h | 2 +- + drivers/usb/dwc3/gadget.c | 4 ++-- + 3 files changed, 7 insertions(+), 12 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -204,7 +204,7 @@ static void dwc3_free_event_buffers(stru + { + struct dwc3_event_buffer *evt; + +- evt = dwc->ev_buffs[0]; ++ evt = dwc->ev_buf; + if (evt) + dwc3_free_one_event_buffer(dwc, evt); + } +@@ -221,17 +221,12 @@ static int dwc3_alloc_event_buffers(stru + { + struct dwc3_event_buffer *evt; + +- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs), +- GFP_KERNEL); +- if (!dwc->ev_buffs) +- return -ENOMEM; +- + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } +- dwc->ev_buffs[0] = evt; ++ dwc->ev_buf = evt; + + return 0; + } +@@ -246,7 +241,7 @@ static int dwc3_event_buffers_setup(stru + { + struct dwc3_event_buffer *evt; + +- evt = dwc->ev_buffs[0]; ++ evt = dwc->ev_buf; + dwc3_trace(trace_dwc3_core, + "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, +@@ -269,7 +264,7 @@ static void dwc3_event_buffers_cleanup(s + { + struct dwc3_event_buffer *evt; + +- evt = dwc->ev_buffs[0]; ++ evt = dwc->ev_buf; + + evt->lpos = 0; + +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -748,7 +748,7 @@ struct dwc3 { + struct platform_device *xhci; + struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; + +- struct dwc3_event_buffer **ev_buffs; ++ struct dwc3_event_buffer *ev_buf; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2576,7 +2576,7 @@ static irqreturn_t dwc3_process_event_bu + int left; + u32 reg; + +- evt = dwc->ev_buffs[0]; ++ evt = dwc->ev_buf; + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) +@@ -2635,7 +2635,7 @@ static irqreturn_t dwc3_check_event_buf( + u32 count; + u32 reg; + +- evt = dwc->ev_buffs[0]; ++ evt = dwc->ev_buf; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + count &= DWC3_GEVNTCOUNT_MASK; diff --git a/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch b/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch new file mode 100644 index 000000000..8bf09edfb --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/096-10-usb-dwc3-core-fix-PHY-handling-during-suspend.patch @@ -0,0 +1,67 @@ +From 5c4ad318de3b8e8680d654c82a254c4b65243739 Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Mon, 11 Apr 2016 17:12:34 +0300 +Subject: usb: dwc3: core: fix PHY handling during suspend + +we need to power off the PHY during suspend and +power it back on during resume. + +Signed-off-by: Felipe Balbi +[nsekhar@ti.com: fix call to usb_phy_set_suspend() in dwc3_suspend()] +Signed-off-by: Sekhar Nori +Signed-off-by: Roger Quadros +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/core.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1124,6 +1124,11 @@ static int dwc3_suspend(struct device *d + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + ++ usb_phy_set_suspend(dwc->usb2_phy, 1); ++ usb_phy_set_suspend(dwc->usb3_phy, 1); ++ WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0); ++ WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0); ++ + pinctrl_pm_select_sleep_state(dev); + + return 0; +@@ -1137,11 +1142,21 @@ static int dwc3_resume(struct device *de + + pinctrl_pm_select_default_state(dev); + ++ usb_phy_set_suspend(dwc->usb2_phy, 0); ++ usb_phy_set_suspend(dwc->usb3_phy, 0); ++ ret = phy_power_on(dwc->usb2_generic_phy); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_power_on(dwc->usb3_generic_phy); ++ if (ret < 0) ++ goto err_usb2phy_power; ++ + usb_phy_init(dwc->usb3_phy); + usb_phy_init(dwc->usb2_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) +- return ret; ++ goto err_usb3phy_power; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) +@@ -1174,6 +1189,12 @@ static int dwc3_resume(struct device *de + err_usb2phy_init: + phy_exit(dwc->usb2_generic_phy); + ++err_usb3phy_power: ++ phy_power_off(dwc->usb3_generic_phy); ++ ++err_usb2phy_power: ++ phy_power_off(dwc->usb2_generic_phy); ++ + return ret; + } + diff --git a/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch b/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch new file mode 100644 index 000000000..214bedc95 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/097-1-usb-dwc3-add-generic-OF-glue-layer.patch @@ -0,0 +1,234 @@ +From 41c2b5280cd2fa3e198c422cdf223ba6e48f857a Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Wed, 18 Nov 2015 13:15:20 -0600 +Subject: [PATCH] usb: dwc3: add generic OF glue layer + +For simple platforms which merely enable some clocks +and populate its children, we can use this generic +glue layer to avoid boilerplate code duplication. + +For now this supports Qcom and Xilinx, but if we +find a way to add generic handling of regulators and +optional PHYs, we can absorb exynos as well. + +Tested-by: Subbaraya Sundeep Bhatta +Signed-off-by: Felipe Balbi +(cherry picked from commit 16adc674d0d68a50dfc725574738d7ae11cf5d7e) + +Change-Id: I6fd260442997b198dc12ca726814b7a9518e6353 +Signed-off-by: Nitheesh Sekar +--- + drivers/usb/dwc3/Kconfig | 9 ++ + drivers/usb/dwc3/Makefile | 1 + + drivers/usb/dwc3/dwc3-of-simple.c | 178 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 188 insertions(+) + create mode 100644 drivers/usb/dwc3/dwc3-of-simple.c + +--- a/drivers/usb/dwc3/Kconfig ++++ b/drivers/usb/dwc3/Kconfig +@@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE + Support of USB2/3 functionality in TI Keystone2 platforms. + Say 'Y' or 'M' here if you have one such device + ++config USB_DWC3_OF_SIMPLE ++ tristate "Generic OF Simple Glue Layer" ++ depends on OF && COMMON_CLK ++ default USB_DWC3 ++ help ++ Support USB2/3 functionality in simple SoC integrations. ++ Currently supports Xilinx and Qualcomm DWC USB3 IP. ++ Say 'Y' or 'M' if you have one such device. ++ + config USB_DWC3_ST + tristate "STMicroelectronics Platforms" + depends on ARCH_STI && OF +--- a/drivers/usb/dwc3/Makefile ++++ b/drivers/usb/dwc3/Makefile +@@ -37,5 +37,6 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-oma + obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o + obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o + obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o ++obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o + obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o + obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o +--- /dev/null ++++ b/drivers/usb/dwc3/dwc3-of-simple.c +@@ -0,0 +1,178 @@ ++/** ++ * dwc3-of-simple.c - OF glue layer for simple integrations ++ * ++ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com ++ * ++ * Author: Felipe Balbi ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 of ++ * the License 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 is a combination of the old dwc3-qcom.c by Ivan T. Ivanov ++ * and the original patch adding support for Xilinx' SoC ++ * by Subbaraya Sundeep Bhatta ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct dwc3_of_simple { ++ struct device *dev; ++ struct clk **clks; ++ int num_clocks; ++}; ++ ++static int dwc3_of_simple_probe(struct platform_device *pdev) ++{ ++ struct dwc3_of_simple *simple; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ ++ int ret; ++ int i; ++ ++ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); ++ if (!simple) ++ return -ENOMEM; ++ ++ ret = of_clk_get_parent_count(np); ++ if (ret < 0) ++ return ret; ++ ++ simple->num_clocks = ret; ++ ++ simple->clks = devm_kcalloc(dev, simple->num_clocks, ++ sizeof(struct clk *), GFP_KERNEL); ++ if (!simple->clks) ++ return -ENOMEM; ++ ++ simple->dev = dev; ++ ++ for (i = 0; i < simple->num_clocks; i++) { ++ struct clk *clk; ++ ++ clk = of_clk_get(np, i); ++ if (IS_ERR(clk)) { ++ while (--i >= 0) ++ clk_put(simple->clks[i]); ++ return PTR_ERR(clk); ++ } ++ ++ ret = clk_prepare_enable(clk); ++ if (ret < 0) { ++ while (--i >= 0) { ++ clk_disable_unprepare(simple->clks[i]); ++ clk_put(simple->clks[i]); ++ } ++ clk_put(clk); ++ ++ return ret; ++ } ++ ++ simple->clks[i] = clk; ++ } ++ ++ ret = of_platform_populate(np, NULL, NULL, dev); ++ if (ret) { ++ for (i = 0; i < simple->num_clocks; i++) { ++ clk_disable_unprepare(simple->clks[i]); ++ clk_put(simple->clks[i]); ++ } ++ ++ return ret; ++ } ++ ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ ++ return 0; ++} ++ ++static int dwc3_of_simple_remove(struct platform_device *pdev) ++{ ++ struct dwc3_of_simple *simple = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ int i; ++ ++ for (i = 0; i < simple->num_clocks; i++) { ++ clk_unprepare(simple->clks[i]); ++ clk_put(simple->clks[i]); ++ } ++ ++ of_platform_depopulate(dev); ++ ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ ++ return 0; ++} ++ ++static int dwc3_of_simple_runtime_suspend(struct device *dev) ++{ ++ struct dwc3_of_simple *simple = dev_get_drvdata(dev); ++ int i; ++ ++ for (i = 0; i < simple->num_clocks; i++) ++ clk_disable(simple->clks[i]); ++ ++ return 0; ++} ++ ++static int dwc3_of_simple_runtime_resume(struct device *dev) ++{ ++ struct dwc3_of_simple *simple = dev_get_drvdata(dev); ++ int ret; ++ int i; ++ ++ for (i = 0; i < simple->num_clocks; i++) { ++ ret = clk_enable(simple->clks[i]); ++ if (ret < 0) { ++ while (--i >= 0) ++ clk_disable(simple->clks[i]); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { ++ SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend, ++ dwc3_of_simple_runtime_resume, NULL) ++}; ++ ++static const struct of_device_id of_dwc3_simple_match[] = { ++ { .compatible = "qcom,dwc3" }, ++ { .compatible = "xlnx,zynqmp-dwc3" }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); ++ ++static struct platform_driver dwc3_of_simple_driver = { ++ .probe = dwc3_of_simple_probe, ++ .remove = dwc3_of_simple_remove, ++ .driver = { ++ .name = "dwc3-of-simple", ++ .of_match_table = of_dwc3_simple_match, ++ }, ++}; ++ ++module_platform_driver(dwc3_of_simple_driver); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer"); ++MODULE_AUTHOR("Felipe Balbi "); diff --git a/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch b/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch new file mode 100644 index 000000000..b982c8236 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/097-2-usb-dwc3-of-simple-fix-build-warning-on-PM.patch @@ -0,0 +1,36 @@ +From 131386d63ca3177d471aa93808c69b85fdac520d Mon Sep 17 00:00:00 2001 +From: Felipe Balbi +Date: Tue, 22 Dec 2015 21:56:10 -0600 +Subject: [PATCH] usb: dwc3: of-simple: fix build warning on !PM + +if we have a !PM kernel build, our runtime +suspend/resume callbacks will be left defined but +unused. Add a ifdef CONFIG_PM guard. + +Signed-off-by: Felipe Balbi +(cherry picked from commit 5072cfc40a80cea3749fd3413b3896630d8c787e) + +Change-Id: I088186c33aa917ec8da2985372ceefc289b24242 +Signed-off-by: Nitheesh Sekar +--- + drivers/usb/dwc3/dwc3-of-simple.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/usb/dwc3/dwc3-of-simple.c ++++ b/drivers/usb/dwc3/dwc3-of-simple.c +@@ -122,6 +122,7 @@ static int dwc3_of_simple_remove(struct + return 0; + } + ++#ifdef CONFIG_PM + static int dwc3_of_simple_runtime_suspend(struct device *dev) + { + struct dwc3_of_simple *simple = dev_get_drvdata(dev); +@@ -150,6 +151,7 @@ static int dwc3_of_simple_runtime_resume + + return 0; + } ++#endif + + static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { + SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend, diff --git a/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch b/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch new file mode 100644 index 000000000..32f9e3417 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/097-3-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch @@ -0,0 +1,47 @@ +From 07c8b15688055d81ac8e1c8c964b9e4c302287f1 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 22 Feb 2016 11:12:47 -0800 +Subject: [PATCH] usb: dwc3: Remove impossible check for + of_clk_get_parent_count() < 0 + +The check for < 0 is impossible now that +of_clk_get_parent_count() returns an unsigned int. Simplify the +code and update the types. + +Acked-by: Felipe Balbi +Cc: +Signed-off-by: Stephen Boyd +(cherry picked from commit 3d755dcc20dd452b52532eca17da40ebbd12aee9) + +Change-Id: Iaa38e064d801fb36c855fea51c0443840368e0d3 +Signed-off-by: Nitheesh Sekar +--- + drivers/usb/dwc3/dwc3-of-simple.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/usb/dwc3/dwc3-of-simple.c ++++ b/drivers/usb/dwc3/dwc3-of-simple.c +@@ -42,6 +42,7 @@ static int dwc3_of_simple_probe(struct p + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + ++ unsigned int count; + int ret; + int i; + +@@ -49,11 +50,11 @@ static int dwc3_of_simple_probe(struct p + if (!simple) + return -ENOMEM; + +- ret = of_clk_get_parent_count(np); +- if (ret < 0) +- return ret; ++ count = of_clk_get_parent_count(np); ++ if (!count) ++ return -ENOENT; + +- simple->num_clocks = ret; ++ simple->num_clocks = count; + + simple->clks = devm_kcalloc(dev, simple->num_clocks, + sizeof(struct clk *), GFP_KERNEL); diff --git a/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch b/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch new file mode 100644 index 000000000..2d18e4151 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/097-4-usb-dwc3-fix-missing-platform_set_drvdata-in-dwc3_of_simple_probe.patch @@ -0,0 +1,27 @@ +From 4c4f106c032ff32b89c98a4d819e68e6e643c14e Mon Sep 17 00:00:00 2001 +From: Wei Yongjun +Date: Tue, 26 Jul 2016 14:47:00 +0000 +Subject: usb: dwc3: fix missing platform_set_drvdata() in + dwc3_of_simple_probe() + +Add missing platform_set_drvdata() in dwc3_of_simple_probe(), otherwise +calling platform_get_drvdata() in remove returns NULL. + +This is detected by Coccinelle semantic patch. + +Signed-off-by: Wei Yongjun +Signed-off-by: Felipe Balbi +--- + drivers/usb/dwc3/dwc3-of-simple.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/usb/dwc3/dwc3-of-simple.c ++++ b/drivers/usb/dwc3/dwc3-of-simple.c +@@ -61,6 +61,7 @@ static int dwc3_of_simple_probe(struct p + if (!simple->clks) + return -ENOMEM; + ++ platform_set_drvdata(pdev, simple); + simple->dev = dev; + + for (i = 0; i < simple->num_clocks; i++) { diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch new file mode 100644 index 000000000..7d1ab139e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch @@ -0,0 +1,512 @@ +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -391,4 +391,15 @@ config PHY_CYGNUS_PCIE + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + ++config PHY_QCOM_DWC3 ++ tristate "QCOM DWC3 USB PHY support" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ help ++ This option enables support for the Synopsis PHYs present inside the ++ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS ++ PHY controllers. ++ + endmenu +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1 + obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o ++obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o +--- /dev/null ++++ b/drivers/phy/phy-qcom-dwc3.c +@@ -0,0 +1,484 @@ ++/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++* This program is distributed in the hope that it will be useful, ++* but WITHOUT ANY WARRANTY; without even the implied warranty of ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++* GNU General Public License for more details. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define QSCRATCH_GENERAL_CFG (0x08) ++#define HSUSB_PHY_CTRL_REG (0x10) ++ ++/* PHY_CTRL_REG */ ++#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) ++#define HSUSB_CTRL_USB2_SUSPEND BIT(23) ++#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) ++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) ++#define HSUSB_CTRL_USE_CLKCORE BIT(18) ++#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) ++#define HSUSB_CTRL_COMMONONN BIT(11) ++#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) ++#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) ++#define HSUSB_CTRL_CLAMP_EN BIT(7) ++#define HSUSB_CTRL_RETENABLEN BIT(1) ++#define HSUSB_CTRL_POR BIT(0) ++ ++/* QSCRATCH_GENERAL_CFG */ ++#define HSUSB_GCFG_XHCI_REV BIT(2) ++ ++/** ++ * USB QSCRATCH Hardware registers ++ */ ++#define SSUSB_PHY_CTRL_REG (0x00) ++#define SSUSB_PHY_PARAM_CTRL_1 (0x04) ++#define SSUSB_PHY_PARAM_CTRL_2 (0x08) ++#define CR_PROTOCOL_DATA_IN_REG (0x0c) ++#define CR_PROTOCOL_DATA_OUT_REG (0x10) ++#define CR_PROTOCOL_CAP_ADDR_REG (0x14) ++#define CR_PROTOCOL_CAP_DATA_REG (0x18) ++#define CR_PROTOCOL_READ_REG (0x1c) ++#define CR_PROTOCOL_WRITE_REG (0x20) ++ ++/* PHY_CTRL_REG */ ++#define SSUSB_CTRL_REF_USE_PAD BIT(28) ++#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) ++#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) ++#define SSUSB_CTRL_SS_PHY_EN BIT(8) ++#define SSUSB_CTRL_SS_PHY_RESET BIT(7) ++ ++/* SSPHY control registers */ ++#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane) ++#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane) ++ ++/* RX OVRD IN HI bits */ ++#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) ++#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) ++#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) ++#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700 ++#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8 ++#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) ++#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) ++#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018 ++#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) ++#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003 ++ ++/* TX OVRD DRV LO register bits */ ++#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F ++#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80 ++#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7 ++#define TX_OVRD_DRV_LO_EN BIT(14) ++ ++/* SS CAP register bits */ ++#define SS_CR_CAP_ADDR_REG BIT(0) ++#define SS_CR_CAP_DATA_REG BIT(0) ++#define SS_CR_READ_REG BIT(0) ++#define SS_CR_WRITE_REG BIT(0) ++ ++struct qcom_dwc3_usb_phy { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *xo_clk; ++ struct clk *ref_clk; ++}; ++ ++struct qcom_dwc3_phy_drvdata { ++ struct phy_ops ops; ++ u32 clk_rate; ++}; ++ ++/** ++ * Write register and read back masked value to confirm it is written ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @offset - register offset. ++ * @mask - register bitmask specifying what should be updated ++ * @val - value to write. ++ */ ++static inline void qcom_dwc3_phy_write_readback( ++ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset, ++ const u32 mask, u32 val) ++{ ++ u32 write_val, tmp = readl(phy_dwc3->base + offset); ++ ++ tmp &= ~mask; /* retain other bits */ ++ write_val = tmp | val; ++ ++ writel(write_val, phy_dwc3->base + offset); ++ ++ /* Read back to see if val was written */ ++ tmp = readl(phy_dwc3->base + offset); ++ tmp &= mask; /* clear other bits */ ++ ++ if (tmp != val) ++ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", ++ val, offset); ++} ++ ++static int wait_for_latch(void __iomem *addr) ++{ ++ u32 retry = 10; ++ ++ while (true) { ++ if (!readl(addr)) ++ break; ++ ++ if (--retry == 0) ++ return -ETIMEDOUT; ++ ++ usleep_range(10, 20); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Write SSPHY register ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to write. ++ * @val - value to write. ++ */ ++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3, ++ u32 addr, u32 val) ++{ ++ int ret; ++ ++ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); ++ ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); ++ ++err_wait: ++ if (ret) ++ dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); ++ return ret; ++} ++ ++/** ++ * Read SSPHY register. ++ * ++ * @base - QCOM DWC3 PHY base virtual address. ++ * @addr - SSPHY address to read. ++ */ ++static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val) ++{ ++ int ret; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); ++ if (ret) ++ goto err_wait; ++ ++ /* ++ * Due to hardware bug, first read of SSPHY register might be ++ * incorrect. Hence as workaround, SW should perform SSPHY register ++ * read twice, but use only second read and ignore first read. ++ */ ++ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); ++ if (ret) ++ goto err_wait; ++ ++ /* throwaway read */ ++ readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ ++ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); ++ if (ret) ++ goto err_wait; ++ ++ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ ++err_wait: ++ return ret; ++} ++ ++static int qcom_dwc3_phy_power_on(struct phy *phy) ++{ ++ int ret; ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ ret = clk_prepare_enable(phy_dwc3->xo_clk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(phy_dwc3->ref_clk); ++ if (ret) ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ ++ return ret; ++} ++ ++static int qcom_dwc3_phy_power_off(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(phy_dwc3->ref_clk); ++ clk_disable_unprepare(phy_dwc3->xo_clk); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_hs_phy_init(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ u32 val; ++ ++ /* ++ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel ++ * enable clamping, and disable RETENTION (power-on default is ENABLED) ++ */ ++ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | ++ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | ++ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | ++ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | ++ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; ++ ++ /* use core clock if external reference is not present */ ++ if (!phy_dwc3->xo_clk) ++ val |= HSUSB_CTRL_USE_CLKCORE; ++ ++ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ ++ /* Disable (bypass) VBUS and ID filters */ ++ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_ss_phy_init(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ int ret; ++ u32 data = 0; ++ ++ /* reset phy */ ++ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ writel(data | SSUSB_CTRL_SS_PHY_RESET, ++ phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* clear REF_PAD if we don't have XO clk */ ++ if (!phy_dwc3->xo_clk) ++ data &= ~SSUSB_CTRL_REF_USE_PAD; ++ else ++ data |= SSUSB_CTRL_REF_USE_PAD; ++ ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* wait for ref clk to become stable, this can take up to 30ms */ ++ msleep(30); ++ ++ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* ++ * Fix RX Equalization setting as follows ++ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3 ++ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 ++ */ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, ++ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data &= ~RX_OVRD_IN_HI_RX_EQ_EN; ++ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; ++ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; ++ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT; ++ data |= RX_OVRD_IN_HI_RX_EQ_OVRD; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, ++ SSPHY_CTRL_RX_OVRD_IN_HI(0), data); ++ if (ret) ++ goto err_phy_trans; ++ ++ /* ++ * Set EQ and TX launch amplitudes as follows ++ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22 ++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 ++ * LANE0.TX_OVRD_DRV_LO.EN set to 1. ++ */ ++ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, ++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); ++ if (ret) ++ goto err_phy_trans; ++ ++ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; ++ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT; ++ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; ++ data |= 0x7f; ++ data |= TX_OVRD_DRV_LO_EN; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, ++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); ++ if (ret) ++ goto err_phy_trans; ++ ++ /* ++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows ++ * TX_FULL_SWING [26:20] amplitude to 127 ++ * TX_DEEMPH_3_5DB [13:8] to 22 ++ * LOS_BIAS [2:0] to 0x5 ++ */ ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ ++err_phy_trans: ++ return ret; ++} ++ ++static int qcom_dwc3_ss_phy_exit(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ /* Sequence to put SSPHY in low power state: ++ * 1. Clear REF_PHY_EN in PHY_CTRL_REG ++ * 2. Clear REF_USE_PAD in PHY_CTRL_REG ++ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention ++ */ ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ SSUSB_CTRL_SS_PHY_EN, 0x0); ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ SSUSB_CTRL_REF_USE_PAD, 0x0); ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, ++ 0x0, SSUSB_CTRL_TEST_POWERDOWN); ++ ++ return 0; ++} ++ ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = { ++ .ops = { ++ .init = qcom_dwc3_hs_phy_init, ++ .power_on = qcom_dwc3_phy_power_on, ++ .power_off = qcom_dwc3_phy_power_off, ++ .owner = THIS_MODULE, ++ }, ++ .clk_rate = 60000000, ++}; ++ ++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = { ++ .ops = { ++ .init = qcom_dwc3_ss_phy_init, ++ .exit = qcom_dwc3_ss_phy_exit, ++ .power_on = qcom_dwc3_phy_power_on, ++ .power_off = qcom_dwc3_phy_power_off, ++ .owner = THIS_MODULE, ++ }, ++ .clk_rate = 125000000, ++}; ++ ++static const struct of_device_id qcom_dwc3_phy_table[] = { ++ { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata }, ++ { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata }, ++ { /* Sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table); ++ ++static int qcom_dwc3_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ struct resource *res; ++ const struct of_device_id *match; ++ const struct qcom_dwc3_phy_drvdata *data; ++ ++ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); ++ if (!phy_dwc3) ++ return -ENOMEM; ++ ++ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node); ++ data = match->data; ++ ++ phy_dwc3->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res); ++ if (IS_ERR(phy_dwc3->base)) ++ return PTR_ERR(phy_dwc3->base); ++ ++ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); ++ if (IS_ERR(phy_dwc3->ref_clk)) { ++ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); ++ return PTR_ERR(phy_dwc3->ref_clk); ++ } ++ ++ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); ++ ++ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); ++ if (IS_ERR(phy_dwc3->xo_clk)) { ++ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); ++ phy_dwc3->xo_clk = NULL; ++ } ++ ++ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, ++ &data->ops); ++ ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); ++ ++ phy_set_drvdata(generic_phy, phy_dwc3); ++ platform_set_drvdata(pdev, phy_dwc3); ++ ++ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, ++ of_phy_simple_xlate); ++ ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_dwc3_phy_driver = { ++ .probe = qcom_dwc3_phy_probe, ++ .driver = { ++ .name = "qcom-dwc3-usb-phy", ++ .owner = THIS_MODULE, ++ .of_match_table = qcom_dwc3_phy_table, ++ }, ++}; ++ ++module_platform_driver(qcom_dwc3_phy_driver); ++ ++MODULE_ALIAS("platform:phy-qcom-dwc3"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Andy Gross "); ++MODULE_AUTHOR("Ivan T. Ivanov "); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch new file mode 100644 index 000000000..d1f19c367 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch @@ -0,0 +1,123 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -92,5 +92,29 @@ + sata@29000000 { + status = "ok"; + }; ++ ++ phy@100f8800 { /* USB3 port 1 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@100f8830 { /* USB3 port 1 SS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8800 { /* USB3 port 0 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8830 { /* USB3 port 0 SS phy */ ++ status = "ok"; ++ }; ++ ++ usb30@0 { ++ status = "ok"; ++ }; ++ ++ usb30@1 { ++ status = "ok"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -333,6 +333,88 @@ + compatible = "syscon"; + reg = <0x01200600 0x100>; + }; ++ ++ hs_phy_1: phy@100f8800 { ++ compatible = "qcom,dwc3-hs-usb-phy"; ++ reg = <0x100f8800 0x30>; ++ clocks = <&gcc USB30_1_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_1: phy@100f8830 { ++ compatible = "qcom,dwc3-ss-usb-phy"; ++ reg = <0x100f8830 0x30>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hs-usb-phy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ss-usb-phy"; ++ reg = <0x110f8830 0x30>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ usb3_0: usb30@0 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@11000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x11000000 0xcd00>; ++ interrupts = <0 110 0x4>; ++ phys = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ dr_mode = "host"; ++ }; ++ }; ++ ++ usb3_1: usb30@1 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@10000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x10000000 0xcd00>; ++ interrupts = <0 205 0x4>; ++ phys = <&hs_phy_1>, <&ss_phy_1>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ dr_mode = "host"; ++ }; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch new file mode 100644 index 000000000..41f91fae7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch @@ -0,0 +1,263 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings +From: Stanimir Varbanov +X-Patchwork-Id: 6326181 +Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring , Kumar Gala , + Mark Rutland , + Grant Likely , + Bjorn Helgaas , + Kishon Vijay Abraham I , + Russell King , Arnd Bergmann +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari , + Srinivas Kandagatla , + Stanimir Varbanov +Date: Mon, 4 May 2015 15:42:16 +0300 + +Document Qualcomm PCIe driver devicetree bindings. + +Signed-off-by: Stanimir Varbanov + +--- +.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ + 1 files changed, 231 insertions(+), 0 deletions(-) + create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt +@@ -0,0 +1,231 @@ ++* Qualcomm PCI express root complex ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: Value shall include ++ - "qcom,pcie-v0" for apq/ipq8064 ++ - "qcom,pcie-v1" for apq8084 ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: Register ranges as listed in the reg-names property ++ ++- reg-names: ++ Usage: required ++ Value type: ++ Definition: Must include the following entries ++ - "parf" Qualcomm specific registers ++ - "dbi" Designware PCIe registers ++ - "elbi" External local bus interface registers ++ - "config" PCIe configuration space ++ ++- device_type: ++ Usage: required ++ Value type: ++ Definition: Should be "pci". As specified in designware-pcie.txt ++ ++- #address-cells: ++ Usage: required ++ Value type: ++ Definition: Should be set to 3. As specified in designware-pcie.txt ++ ++- #size-cells: ++ Usage: required ++ Value type: ++ Definition: Should be set 2. As specified in designware-pcie.txt ++ ++- ranges: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- interrupts: ++ Usage: required ++ Value type: ++ Definition: MSI interrupt ++ ++- interrupt-names: ++ Usage: required ++ Value type: ++ Definition: Should contain "msi" ++ ++- #interrupt-cells: ++ Usage: required ++ Value type: ++ Definition: Should be 1. As specified in designware-pcie.txt ++ ++- interrupt-map-mask: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- interrupt-map: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- clocks: ++ Usage: required ++ Value type: ++ Definition: List of phandle and clock specifier pairs as listed ++ in clock-names property ++ ++- clock-names: ++ Usage: required ++ Value type: ++ Definition: Should contain the following entries ++ * should be populated for v0 and v1 ++ - "iface" Configuration AHB clock ++ ++ * should be populated for v0 ++ - "core" Clocks the pcie hw block ++ - "phy" Clocks the pcie PHY block ++ ++ * should be populated for v1 ++ - "aux" Auxiliary (AUX) clock ++ - "bus_master" Master AXI clock ++ - "bus_slave" Slave AXI clock ++ ++- resets: ++ Usage: required ++ Value type: ++ Definition: List of phandle and reset specifier pairs as listed ++ in reset-names property ++ ++- reset-names: ++ Usage: required ++ Value type: ++ Definition: Should contain the following entries ++ * should be populated for v0 ++ - "axi" AXI reset ++ - "ahb" AHB reset ++ - "por" POR reset ++ - "pci" PCI reset ++ - "phy" PHY reset ++ ++ * should be populated for v1 ++ - "core" Core reset ++ ++- power-domains: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: A phandle and power domain specifier pair to the ++ power domain which is responsible for collapsing ++ and restoring power to the peripheral ++ ++- -supply: ++ Usage: required ++ Value type: ++ Definition: List of phandles to the power supply regulator(s) ++ * should be populated for v0 and v1 ++ - "vdda" core analog power supply ++ ++ * should be populated for v0 ++ - "vdda_phy" analog power supply for PHY ++ - "vdda_refclk" analog power supply for IC which generate ++ reference clock ++ ++- phys: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: List of phandle(s) as listed in phy-names property ++ ++- phy-names: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: Should contain "pciephy" ++ ++- -gpio: ++ Usage: optional ++ Value type: ++ Definition: List of phandle and gpio specifier pairs. Should contain ++ - "perst" PCIe endpoint reset signal line ++ - "pewake" PCIe endpoint wake signal line ++ ++- pinctrl-0: ++ Usage: required ++ Value type: ++ Definition: List of phandles pointing at a pin(s) configuration ++ ++- pinctrl-names ++ Usage: required ++ Value type: ++ Definition: List of names of pinctrl-0 state ++ ++* Example for v0 ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ resets = <&gcc PCIE_ACLK_RESET>, ++ <&gcc PCIE_HCLK_RESET>, ++ <&gcc PCIE_POR_RESET>, ++ <&gcc PCIE_PCI_RESET>, ++ <&gcc PCIE_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ }; ++ ++* Example for v1 ++ pcie0@fc520000 { ++ compatible = "qcom,pcie-v1"; ++ reg = <0xfc520000 0x2000>, ++ <0xff000000 0x1000>, ++ <0xff001000 0x1000>, ++ <0xff002000 0x2000>; ++ reg-names = "parf", "dbi", "elbi", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, ++ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, ++ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, ++ <&gcc GCC_PCIE_0_AUX_CLK>; ++ clock-names = "iface", "master_bus", "slave_bus", "aux"; ++ resets = <&gcc GCC_PCIE_0_BCR>; ++ reset-names = "core"; ++ power-domains = <&gcc PCIE0_GDSC>; ++ vdda-supply = <&pma8084_l3>; ++ phys = <&pciephy0>; ++ phy-names = "pciephy"; ++ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&pcie0_pins_default>; ++ pinctrl-names = "default"; ++ }; diff --git a/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch new file mode 100644 index 000000000..b140787ac --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch @@ -0,0 +1,752 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver +From: Stanimir Varbanov +X-Patchwork-Id: 6326161 +Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring , Kumar Gala , + Mark Rutland , + Grant Likely , + Bjorn Helgaas , + Kishon Vijay Abraham I , + Russell King , Arnd Bergmann +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari , + Srinivas Kandagatla , + Stanimir Varbanov +Date: Mon, 4 May 2015 15:42:17 +0300 + +The PCIe driver reuse the Designware common code for host +and MSI initialization, and also program the Qualcomm +application specific registers. + +Signed-off-by: Stanimir Varbanov + +--- +MAINTAINERS | 7 + + drivers/pci/host/Kconfig | 9 + + drivers/pci/host/Makefile | 1 + + drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 694 insertions(+), 0 deletions(-) + create mode 100644 drivers/pci/host/pcie-qcom.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8248,6 +8248,13 @@ S: Maintained + F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt + F: drivers/pci/host/pcie-hisi.c + ++PCIE DRIVER FOR QUALCOMM MSM ++M: Stanimir Varbanov ++L: linux-pci@vger.kernel.org ++L: linux-arm-msm@vger.kernel.org ++S: Maintained ++F: drivers/pci/host/*qcom* ++ + PCMCIA SUBSYSTEM + P: Linux PCMCIA Team + L: linux-pcmcia@lists.infradead.org +--- a/drivers/pci/host/Kconfig ++++ b/drivers/pci/host/Kconfig +@@ -173,4 +173,13 @@ config PCI_HISI + help + Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC + ++config PCIE_QCOM ++ bool "Qualcomm PCIe controller" ++ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) ++ select PCIE_DW ++ select PCIEPORTBUS ++ help ++ Say Y here to enable PCIe controller support on Qualcomm SoCs. The ++ PCIe controller use Designware core plus Qualcomm specific hardware ++ wrappers. + endmenu +--- /dev/null ++++ b/drivers/pci/host/pcie-qcom.c +@@ -0,0 +1,676 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcie-designware.h" ++ ++#define PCIE20_PARF_PHY_CTRL 0x40 ++#define PCIE20_PARF_PHY_REFCLK 0x4C ++#define PCIE20_PARF_DBI_BASE_ADDR 0x168 ++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c ++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 ++ ++#define PCIE20_ELBI_SYS_CTRL 0x04 ++#define PCIE20_ELBI_SYS_STTS 0x08 ++#define XMLH_LINK_UP BIT(10) ++ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PERST_DELAY_MIN_US 1000 ++#define PERST_DELAY_MAX_US 1005 ++ ++#define LINKUP_DELAY_MIN_US 5000 ++#define LINKUP_DELAY_MAX_US 5100 ++#define LINKUP_RETRIES_COUNT 20 ++ ++#define PCIE_V0 0 /* apq8064 */ ++#define PCIE_V1 1 /* apq8084 */ ++ ++struct qcom_pcie_resources_v0 { ++ struct clk *iface_clk; ++ struct clk *core_clk; ++ struct clk *phy_clk; ++ struct reset_control *pci_reset; ++ struct reset_control *axi_reset; ++ struct reset_control *ahb_reset; ++ struct reset_control *por_reset; ++ struct reset_control *phy_reset; ++ struct regulator *vdda; ++ struct regulator *vdda_phy; ++ struct regulator *vdda_refclk; ++}; ++ ++struct qcom_pcie_resources_v1 { ++ struct clk *iface; ++ struct clk *aux; ++ struct clk *master_bus; ++ struct clk *slave_bus; ++ struct reset_control *core; ++ struct regulator *vdda; ++}; ++ ++union pcie_resources { ++ struct qcom_pcie_resources_v0 v0; ++ struct qcom_pcie_resources_v1 v1; ++}; ++ ++struct qcom_pcie { ++ struct pcie_port pp; ++ struct device *dev; ++ union pcie_resources res; ++ void __iomem *parf; ++ void __iomem *dbi; ++ void __iomem *elbi; ++ struct phy *phy; ++ struct gpio_desc *reset; ++ unsigned int version; ++}; ++ ++#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) ++ ++static inline void ++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) ++{ ++ u32 val = readl(addr); ++ ++ val &= ~clear_mask; ++ val |= set_mask; ++ writel(val, addr); ++} ++ ++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) ++{ ++ int val, active_low; ++ ++ if (IS_ERR_OR_NULL(pcie->reset)) ++ return; ++ ++ active_low = gpiod_is_active_low(pcie->reset); ++ ++ if (assert) ++ val = !!active_low; ++ else ++ val = !active_low; ++ ++ gpiod_set_value(pcie->reset, val); ++ ++ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); ++} ++ ++static void qcom_ep_reset_assert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 1); ++} ++ ++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 0); ++} ++ ++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) ++{ ++ struct pcie_port *pp = arg; ++ ++ return dw_handle_msi_irq(pp); ++} ++ ++static int qcom_pcie_link_up(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); ++ ++ return val & BIT(29) ? 1 : 0; ++} ++ ++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ ++ reset_control_assert(res->pci_reset); ++ reset_control_assert(res->axi_reset); ++ reset_control_assert(res->ahb_reset); ++ reset_control_assert(res->por_reset); ++ reset_control_assert(res->pci_reset); ++ clk_disable_unprepare(res->iface_clk); ++ clk_disable_unprepare(res->core_clk); ++ clk_disable_unprepare(res->phy_clk); ++ regulator_disable(res->vdda); ++ regulator_disable(res->vdda_phy); ++ regulator_disable(res->vdda_refclk); ++} ++ ++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ ++ reset_control_assert(res->core); ++ clk_disable_unprepare(res->slave_bus); ++ clk_disable_unprepare(res->master_bus); ++ clk_disable_unprepare(res->iface); ++ clk_disable_unprepare(res->aux); ++ regulator_disable(res->vdda); ++} ++ ++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(res->vdda_refclk); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_refclk regulator\n"); ++ goto err_refclk; ++ } ++ ++ ret = regulator_enable(res->vdda_phy); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_phy regulator\n"); ++ goto err_vdda_phy; ++ } ++ ++ ret = clk_prepare_enable(res->iface_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_iface; ++ } ++ ++ ret = clk_prepare_enable(res->core_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable core clock\n"); ++ goto err_clk_core; ++ } ++ ++ ret = clk_prepare_enable(res->phy_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable phy clock\n"); ++ goto err_clk_phy; ++ } ++ ++ ret = reset_control_deassert(res->ahb_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert ahb reset\n"); ++ goto err_reset_ahb; ++ } ++ ++ return 0; ++ ++err_reset_ahb: ++ clk_disable_unprepare(res->phy_clk); ++err_clk_phy: ++ clk_disable_unprepare(res->core_clk); ++err_clk_core: ++ clk_disable_unprepare(res->iface_clk); ++err_iface: ++ regulator_disable(res->vdda_phy); ++err_vdda_phy: ++ regulator_disable(res->vdda_refclk); ++err_refclk: ++ regulator_disable(res->vdda); ++ return ret; ++} ++ ++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = reset_control_deassert(res->core); ++ if (ret) { ++ dev_err(dev, "cannot deassert core reset\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(res->aux); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable aux clock\n"); ++ goto err_res; ++ } ++ ++ ret = clk_prepare_enable(res->iface); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_aux; ++ } ++ ++ ret = clk_prepare_enable(res->master_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable master_bus clock\n"); ++ goto err_iface; ++ } ++ ++ ret = clk_prepare_enable(res->slave_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); ++ goto err_master; ++ } ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ goto err_slave; ++ } ++ ++ return 0; ++ ++err_slave: ++ clk_disable_unprepare(res->slave_bus); ++err_master: ++ clk_disable_unprepare(res->master_bus); ++err_iface: ++ clk_disable_unprepare(res->iface); ++err_aux: ++ clk_disable_unprepare(res->aux); ++err_res: ++ reset_control_assert(res->core); ++ ++ return ret; ++} ++ ++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); ++ if (IS_ERR(res->vdda_phy)) ++ return PTR_ERR(res->vdda_phy); ++ ++ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); ++ if (IS_ERR(res->vdda_refclk)) ++ return PTR_ERR(res->vdda_refclk); ++ ++ res->iface_clk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface_clk)) ++ return PTR_ERR(res->iface_clk); ++ ++ res->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(res->core_clk)) ++ return PTR_ERR(res->core_clk); ++ ++ res->phy_clk = devm_clk_get(dev, "phy"); ++ if (IS_ERR(res->phy_clk)) ++ return PTR_ERR(res->phy_clk); ++ ++ res->pci_reset = devm_reset_control_get(dev, "pci"); ++ if (IS_ERR(res->pci_reset)) ++ return PTR_ERR(res->pci_reset); ++ ++ res->axi_reset = devm_reset_control_get(dev, "axi"); ++ if (IS_ERR(res->axi_reset)) ++ return PTR_ERR(res->axi_reset); ++ ++ res->ahb_reset = devm_reset_control_get(dev, "ahb"); ++ if (IS_ERR(res->ahb_reset)) ++ return PTR_ERR(res->ahb_reset); ++ ++ res->por_reset = devm_reset_control_get(dev, "por"); ++ if (IS_ERR(res->por_reset)) ++ return PTR_ERR(res->por_reset); ++ ++ res->phy_reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(res->phy_reset)) ++ return PTR_ERR(res->phy_reset); ++ ++ return 0; ++} ++ ++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->iface = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface)) ++ return PTR_ERR(res->iface); ++ ++ res->aux = devm_clk_get(dev, "aux"); ++ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ else if (IS_ERR(res->aux)) ++ res->aux = NULL; ++ ++ res->master_bus = devm_clk_get(dev, "master_bus"); ++ if (IS_ERR(res->master_bus)) ++ return PTR_ERR(res->master_bus); ++ ++ res->slave_bus = devm_clk_get(dev, "slave_bus"); ++ if (IS_ERR(res->slave_bus)) ++ return PTR_ERR(res->slave_bus); ++ ++ res->core = devm_reset_control_get(dev, "core"); ++ if (IS_ERR(res->core)) ++ return PTR_ERR(res->core); ++ ++ return 0; ++} ++ ++static int qcom_pcie_enable_link_training(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct device *dev = pp->dev; ++ int retries; ++ u32 val; ++ ++ /* enable link training */ ++ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); ++ ++ /* wait for up to 100ms for the link to come up */ ++ retries = LINKUP_RETRIES_COUNT; ++ do { ++ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); ++ if (val & XMLH_LINK_UP) ++ break; ++ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); ++ } while (retries--); ++ ++ if (retries < 0 || !dw_pcie_link_up(pp)) { ++ dev_err(dev, "link initialization failed\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void qcom_pcie_host_init_v1(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v1(pcie); ++ if (ret) ++ return; ++ ++ /* change DBI base address */ ++ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, ++ 0, BIT(31)); ++ ++ ret = phy_init(pcie->phy); ++ if (ret) ++ goto err_res; ++ ++ ret = phy_power_on(pcie->phy); ++ if (ret) ++ goto err_phy; ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++ ++err: ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++err_phy: ++ phy_exit(pcie->phy); ++err_res: ++ qcom_pcie_disable_resources_v1(pcie); ++} ++ ++static void qcom_pcie_host_init_v0(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v0(pcie); ++ if (ret) ++ return; ++ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* enable external reference clock */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); ++ ++ ret = reset_control_deassert(res->phy_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert phy reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->pci_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert pci reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->por_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert por reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->axi_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert axi reset\n"); ++ return; ++ } ++ ++ /* wait 150ms for clock acquisition */ ++ usleep_range(10000, 15000); ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++err: ++ qcom_ep_reset_assert(pcie); ++ qcom_pcie_disable_resources_v0(pcie); ++} ++ ++static void qcom_pcie_host_init(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ ++ if (pcie->version == PCIE_V0) ++ return qcom_pcie_host_init_v0(pp); ++ else ++ return qcom_pcie_host_init_v1(pp); ++} ++ ++static int ++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) ++{ ++ /* the device class is not reported correctly from the register */ ++ if (where == PCI_CLASS_REVISION && size == 4) { ++ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); ++ *val &= ~(0xffff << 16); ++ *val |= PCI_CLASS_BRIDGE_PCI << 16; ++ return PCIBIOS_SUCCESSFUL; ++ } ++ ++ return dw_pcie_cfg_read(pp->dbi_base + where, size, val); ++} ++ ++static struct pcie_host_ops qcom_pcie_ops = { ++ .link_up = qcom_pcie_link_up, ++ .host_init = qcom_pcie_host_init, ++ .rd_own_conf = qcom_pcie_rd_own_conf, ++}; ++ ++static const struct of_device_id qcom_pcie_match[] = { ++ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, ++ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, ++ { } ++}; ++ ++static int qcom_pcie_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ struct resource *res; ++ struct qcom_pcie *pcie; ++ struct pcie_port *pp; ++ int ret; ++ ++ match = of_match_node(qcom_pcie_match, dev->of_node); ++ if (!match) ++ return -ENXIO; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ pcie->version = (unsigned int)match->data; ++ ++ pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); ++ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) ++ return PTR_ERR(pcie->reset); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); ++ pcie->parf = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->parf)) ++ return PTR_ERR(pcie->parf); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); ++ pcie->dbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->dbi)) ++ return PTR_ERR(pcie->dbi); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); ++ pcie->elbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->elbi)) ++ return PTR_ERR(pcie->elbi); ++ ++ pcie->phy = devm_phy_optional_get(dev, "pciephy"); ++ if (IS_ERR(pcie->phy)) ++ return PTR_ERR(pcie->phy); ++ ++ pcie->dev = dev; ++ ++ if (pcie->version == PCIE_V0) ++ ret = qcom_pcie_get_resources_v0(pcie); ++ else ++ ret = qcom_pcie_get_resources_v1(pcie); ++ ++ if (ret) ++ return ret; ++ ++ pp = &pcie->pp; ++ pp->dev = dev; ++ pp->dbi_base = pcie->dbi; ++ pp->root_bus_nr = -1; ++ pp->ops = &qcom_pcie_ops; ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) { ++ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); ++ if (pp->msi_irq < 0) { ++ dev_err(dev, "cannot get msi irq\n"); ++ return pp->msi_irq; ++ } ++ ++ ret = devm_request_irq(dev, pp->msi_irq, ++ qcom_pcie_msi_irq_handler, ++ IRQF_SHARED, "qcom-pcie-msi", pp); ++ if (ret) { ++ dev_err(dev, "cannot request msi irq\n"); ++ return ret; ++ } ++ } ++ ++ ret = dw_pcie_host_init(pp); ++ if (ret) { ++ dev_err(dev, "cannot initialize host\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, pcie); ++ ++ return 0; ++} ++ ++static int qcom_pcie_remove(struct platform_device *pdev) ++{ ++ struct qcom_pcie *pcie = platform_get_drvdata(pdev); ++ ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++ phy_exit(pcie->phy); ++ if (pcie->version == PCIE_V0) ++ qcom_pcie_disable_resources_v0(pcie); ++ else ++ qcom_pcie_disable_resources_v1(pcie); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_pcie_driver = { ++ .probe = qcom_pcie_probe, ++ .remove = qcom_pcie_remove, ++ .driver = { ++ .name = "qcom-pcie", ++ .of_match_table = qcom_pcie_match, ++ }, ++}; ++ ++module_platform_driver(qcom_pcie_driver); ++ ++MODULE_AUTHOR("Stanimir Varbanov "); ++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-pcie"); +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -20,3 +20,4 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-ip + obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o + obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o + obj-$(CONFIG_PCI_HISI) += pcie-hisi.o ++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch new file mode 100644 index 000000000..90631c669 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,245 @@ +From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:01:42 -0700 +Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms + +qcom-pcie driver now supports version 0 of the controller. This change +adds the corresponding entries to the IPQ806x dtsi file and +corresponding platform (AP148). + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ + 2 files changed, 154 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -116,5 +116,15 @@ + usb30@1 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ phy-tx0-term-offset = <7>; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ phy-tx0-term-offset = <7>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -128,5 +128,17 @@ + usb30@1 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ status = "ok"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -4,6 +4,9 @@ + #include + #include + #include ++#include ++#include ++#include + + / { + model = "Qualcomm IPQ8064"; +@@ -99,6 +102,34 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 16 0x4>; ++ ++ pcie0_pins: pcie0_pinmux { ++ mux { ++ pins = "gpio3"; ++ function = "pcie1_rst"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio48"; ++ function = "pcie2_rst"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie2_pins: pcie2_pinmux { ++ mux { ++ pins = "gpio63"; ++ function = "pcie3_rst"; ++ drive-strength = <2>; ++ bias-disable; ++ output-low; ++ }; ++ }; + }; + + intc: interrupt-controller@2000000 { +@@ -415,6 +446,144 @@ + dr_mode = "host"; + }; + }; ++ ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_ACLK_RESET>, ++ <&gcc PCIE_HCLK_RESET>, ++ <&gcc PCIE_POR_RESET>, ++ <&gcc PCIE_PCI_RESET>, ++ <&gcc PCIE_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie0_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpios = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b700000 0x1000 ++ 0x1b702000 0x80 ++ 0x1b800000 0x100 ++ 0x31f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <1>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_1_A_CLK>, ++ <&gcc PCIE_1_H_CLK>, ++ <&gcc PCIE_1_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_1_ACLK_RESET>, ++ <&gcc PCIE_1_HCLK_RESET>, ++ <&gcc PCIE_1_POR_RESET>, ++ <&gcc PCIE_1_PCI_RESET>, ++ <&gcc PCIE_1_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpios = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b900000 0x1000 ++ 0x1b902000 0x80 ++ 0x1ba00000 0x100 ++ 0x35f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <2>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_2_A_CLK>, ++ <&gcc PCIE_2_H_CLK>, ++ <&gcc PCIE_2_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_2_ACLK_RESET>, ++ <&gcc PCIE_2_HCLK_RESET>, ++ <&gcc PCIE_2_POR_RESET>, ++ <&gcc PCIE_2_PCI_RESET>, ++ <&gcc PCIE_2_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpios = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 000000000..e2d31354e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -0,0 +1,29 @@ +From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:09:07 -0700 +Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is + enabled + +If multiple PCIe devices are present in the system, the kernel will +panic at boot time when trying to scan the PCI buses. This happens on +IPQ806x based platforms, which has 3 PCIe ports. + +Enabling this option allows the kernel to assign the pci-domains +according to the device-tree content. This allows multiple PCIe +controllers to coexist in the system. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -5,6 +5,7 @@ menuconfig ARCH_QCOM + select ARM_AMBA + select PINCTRL + select QCOM_SCM if SMP ++ select PCI_DOMAINS if PCI + help + Support for Qualcomm's devicetree based systems. + diff --git a/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch new file mode 100644 index 000000000..6328113c0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch @@ -0,0 +1,311 @@ +--- a/drivers/pci/host/pcie-qcom.c ++++ b/drivers/pci/host/pcie-qcom.c +@@ -29,8 +29,53 @@ + + #include "pcie-designware.h" + ++/* DBI registers */ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 ++#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c ++ ++#define PCIE20_PLR_IATU_VIEWPORT 0x900 ++#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31) ++#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0) ++ ++#define PCIE20_PLR_IATU_CTRL1 0x904 ++#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0) ++#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0) ++ ++#define PCIE20_PLR_IATU_CTRL2 0x908 ++#define PCIE20_PLR_IATU_ENABLE BIT(31) ++ ++#define PCIE20_PLR_IATU_LBAR 0x90C ++#define PCIE20_PLR_IATU_UBAR 0x910 ++#define PCIE20_PLR_IATU_LAR 0x914 ++#define PCIE20_PLR_IATU_LTAR 0x918 ++#define PCIE20_PLR_IATU_UTAR 0x91c ++ ++#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 ++ ++/* PARF registers */ ++#define PCIE20_PARF_PCS_DEEMPH 0x34 ++#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16) ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8) ++#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0) ++ ++#define PCIE20_PARF_PCS_SWING 0x38 ++#define PCS_SWING_TX_SWING_FULL(x) (x << 8) ++#define PCS_SWING_TX_SWING_LOW(x) (x << 0) ++ + #define PCIE20_PARF_PHY_CTRL 0x40 ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16) ++#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16) ++ + #define PCIE20_PARF_PHY_REFCLK 0x4C ++#define REF_SSP_EN BIT(16) ++#define REF_USE_PAD BIT(12) ++ ++#define PCIE20_PARF_CONFIG_BITS 0x50 ++#define PHY_RX0_EQ(x) (x << 24) ++ + #define PCIE20_PARF_DBI_BASE_ADDR 0x168 + #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c + #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +@@ -39,9 +84,6 @@ + #define PCIE20_ELBI_SYS_STTS 0x08 + #define XMLH_LINK_UP BIT(10) + +-#define PCIE20_CAP 0x70 +-#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) +- + #define PERST_DELAY_MIN_US 1000 + #define PERST_DELAY_MAX_US 1005 + +@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 { + struct clk *iface_clk; + struct clk *core_clk; + struct clk *phy_clk; ++ struct clk *aux_clk; ++ struct clk *ref_clk; + struct reset_control *pci_reset; + struct reset_control *axi_reset; + struct reset_control *ahb_reset; + struct reset_control *por_reset; + struct reset_control *phy_reset; ++ struct reset_control *ext_reset; + struct regulator *vdda; + struct regulator *vdda_phy; + struct regulator *vdda_refclk; ++ uint8_t phy_tx0_term_offset; + }; + + struct qcom_pcie_resources_v1 { +@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl + + static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) + { +- int val, active_low; +- + if (IS_ERR_OR_NULL(pcie->reset)) + return; + +- active_low = gpiod_is_active_low(pcie->reset); +- +- if (assert) +- val = !!active_low; +- else +- val = !active_low; +- +- gpiod_set_value(pcie->reset, val); +- ++ gpiod_set_value(pcie->reset, assert); + usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); + } + +@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_ + reset_control_assert(res->axi_reset); + reset_control_assert(res->ahb_reset); + reset_control_assert(res->por_reset); +- reset_control_assert(res->pci_reset); ++ reset_control_assert(res->phy_reset); ++ reset_control_assert(res->ext_reset); + clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->core_clk); + clk_disable_unprepare(res->phy_clk); ++ clk_disable_unprepare(res->aux_clk); ++ clk_disable_unprepare(res->ref_clk); + regulator_disable(res->vdda); + regulator_disable(res->vdda_phy); + regulator_disable(res->vdda_refclk); +@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0 + goto err_vdda_phy; + } + ++ ret = reset_control_deassert(res->ext_reset); ++ if (ret) { ++ dev_err(dev, "cannot assert ext reset\n"); ++ goto err_reset_ext; ++ } ++ + ret = clk_prepare_enable(res->iface_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable iface clock\n"); +@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0 + goto err_clk_phy; + } + ++ ret = clk_prepare_enable(res->aux_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable aux clock\n"); ++ goto err_clk_aux; ++ } ++ ++ ret = clk_prepare_enable(res->ref_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable ref clock\n"); ++ goto err_clk_ref; ++ } ++ + ret = reset_control_deassert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot deassert ahb reset\n"); + goto err_reset_ahb; + } ++ udelay(1); + + return 0; + + err_reset_ahb: ++ clk_disable_unprepare(res->ref_clk); ++err_clk_ref: ++ clk_disable_unprepare(res->aux_clk); ++err_clk_aux: + clk_disable_unprepare(res->phy_clk); + err_clk_phy: + clk_disable_unprepare(res->core_clk); + err_clk_core: + clk_disable_unprepare(res->iface_clk); + err_iface: ++ reset_control_assert(res->ext_reset); ++err_reset_ext: + regulator_disable(res->vdda_phy); + err_vdda_phy: + regulator_disable(res->vdda_refclk); +@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st + if (IS_ERR(res->phy_clk)) + return PTR_ERR(res->phy_clk); + ++ res->aux_clk = devm_clk_get(dev, "aux"); ++ if (IS_ERR(res->aux_clk)) ++ return PTR_ERR(res->aux_clk); ++ ++ res->ref_clk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(res->ref_clk)) ++ return PTR_ERR(res->ref_clk); ++ + res->pci_reset = devm_reset_control_get(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); +@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st + if (IS_ERR(res->phy_reset)) + return PTR_ERR(res->phy_reset); + ++ res->ext_reset = devm_reset_control_get(dev, "ext"); ++ if (IS_ERR(res->ext_reset)) ++ return PTR_ERR(res->ext_reset); ++ ++ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset", ++ &res->phy_tx0_term_offset)) ++ res->phy_tx0_term_offset = 0; ++ + return 0; + } + +@@ -461,6 +541,57 @@ err_res: + qcom_pcie_disable_resources_v1(pcie); + } + ++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) ++{ ++ struct pcie_port *pp = &pcie->pp; ++ ++ /* ++ * program and enable address translation region 0 (device config ++ * address space); region type config; ++ * axi config address range to device config address range ++ */ ++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | ++ PCIE20_PLR_IATU_REGION_INDEX(0), ++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->cfg0_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); ++ writel((pp->cfg0_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); ++ writel((pp->cfg0_base + pp->cfg0_size - 1), ++ pcie->dbi + PCIE20_PLR_IATU_LAR); ++ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR); ++ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR); ++} ++ ++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) ++{ ++ struct pcie_port *pp = &pcie->pp; ++ ++ /* ++ * program and enable address translation region 2 (device resource ++ * address space); region type memory; ++ * axi device bar address range to device bar address range ++ */ ++ writel(PCIE20_PLR_IATU_REGION_OUTBOUND | ++ PCIE20_PLR_IATU_REGION_INDEX(2), ++ pcie->dbi + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->mem_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); ++ writel((pp->mem_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); ++ writel(pp->mem_base + pp->mem_size - 1, ++ pcie->dbi + PCIE20_PLR_IATU_LAR); ++ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR); ++ writel(upper_32_bits(pp->mem_bus_addr), ++ pcie->dbi + PCIE20_PLR_IATU_UTAR); ++ ++ /* 256B PCIE buffer setting */ ++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); ++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); ++} ++ + static void qcom_pcie_host_init_v0(struct pcie_port *pp) + { + struct qcom_pcie *pcie = to_qcom_pcie(pp); +@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc + + qcom_ep_reset_assert(pcie); + ++ reset_control_assert(res->ahb_reset); ++ + ret = qcom_pcie_enable_resources_v0(pcie); + if (ret) + return; + + writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); + +- /* enable external reference clock */ +- writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); ++ /* Set Tx termination offset */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, ++ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK, ++ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset)); ++ ++ /* PARF programming */ ++ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | ++ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | ++ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), ++ pcie->parf + PCIE20_PARF_PCS_DEEMPH); ++ writel(PCS_SWING_TX_SWING_FULL(0x78) | ++ PCS_SWING_TX_SWING_LOW(0x78), ++ pcie->parf + PCIE20_PARF_PCS_SWING); ++ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS); ++ ++ /* Enable reference clock */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, ++ REF_USE_PAD, REF_SSP_EN); + ++ /* De-assert PHY, PCIe, POR and AXI resets */ + ret = reset_control_deassert(res->phy_reset); + if (ret) { + dev_err(dev, "cannot deassert phy reset\n"); +@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc + if (ret) + goto err; + ++ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); ++ qcom_pcie_prog_viewport_mem2_outbound(pcie); ++ + return; + err: + qcom_ep_reset_assert(pcie); diff --git a/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch new file mode 100644 index 000000000..636a6c76a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch @@ -0,0 +1,80 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -475,15 +475,21 @@ + + clocks = <&gcc PCIE_A_CLK>, + <&gcc PCIE_H_CLK>, +- <&gcc PCIE_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_PHY_CLK>, ++ <&gcc PCIE_AUX_CLK>, ++ <&gcc PCIE_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_ACLK_RESET>, + <&gcc PCIE_HCLK_RESET>, + <&gcc PCIE_POR_RESET>, + <&gcc PCIE_PCI_RESET>, +- <&gcc PCIE_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_PHY_RESET>, ++ <&gcc PCIE_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; +@@ -521,15 +527,21 @@ + + clocks = <&gcc PCIE_1_A_CLK>, + <&gcc PCIE_1_H_CLK>, +- <&gcc PCIE_1_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_1_PHY_CLK>, ++ <&gcc PCIE_1_AUX_CLK>, ++ <&gcc PCIE_1_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_1_ACLK_RESET>, + <&gcc PCIE_1_HCLK_RESET>, + <&gcc PCIE_1_POR_RESET>, + <&gcc PCIE_1_PCI_RESET>, +- <&gcc PCIE_1_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_1_PHY_RESET>, ++ <&gcc PCIE_1_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; +@@ -567,15 +579,21 @@ + + clocks = <&gcc PCIE_2_A_CLK>, + <&gcc PCIE_2_H_CLK>, +- <&gcc PCIE_2_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_2_PHY_CLK>, ++ <&gcc PCIE_2_AUX_CLK>, ++ <&gcc PCIE_2_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_2_ACLK_RESET>, + <&gcc PCIE_2_HCLK_RESET>, + <&gcc PCIE_2_POR_RESET>, + <&gcc PCIE_2_PCI_RESET>, +- <&gcc PCIE_2_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_2_PHY_RESET>, ++ <&gcc PCIE_2_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie2_pins>; + pinctrl-names = "default"; diff --git a/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch new file mode 100644 index 000000000..6e589d26a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch @@ -0,0 +1,87 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -2,6 +2,7 @@ + + #include "skeleton.dtsi" + #include ++#include + #include + #include + #include +@@ -93,6 +94,63 @@ + reg-names = "lpass-lpaif"; + }; + ++ rpm@108000 { ++ compatible = "qcom,rpm-ipq8064"; ++ reg = <0x108000 0x1000>; ++ qcom,ipc = <&l2cc 0x8 2>; ++ ++ interrupts = <0 19 0>, ++ <0 21 0>, ++ <0 22 0>; ++ interrupt-names = "ack", ++ "err", ++ "wakeup"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ smb208_s1a: smb208-s1a { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ ++ }; ++ ++ smb208_s1b: smb208-s1b { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2a: smb208-s2a { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2b: smb208-s2b { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ }; ++ + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; +@@ -165,6 +223,12 @@ + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; + }; + ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc", "syscon"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; ++ + saw0: regulator@2089000 { + compatible = "qcom,saw2"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 000000000..36a92c858 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,144 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions +From: Stephen Boyd +X-Patchwork-Id: 6063051 +Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , + Mark Rutland , Russell King , + Courtney Cavin +Date: Fri, 20 Mar 2015 23:45:20 -0700 + +Krait CPUs have a handful of L2 cache controller registers that +live behind a cp15 based indirection register. First you program +the indirection register (l2cpselr) to point the L2 'window' +register (l2cpdr) at what you want to read/write. Then you +read/write the 'window' register to do what you want. The +l2cpselr register is not banked per-cpu so we must lock around +accesses to it to prevent other CPUs from re-pointing l2cpdr +underneath us. + +Cc: Mark Rutland +Cc: Russell King +Cc: Courtney Cavin +Signed-off-by: Stephen Boyd + +--- +arch/arm/common/Kconfig | 3 ++ + arch/arm/common/Makefile | 1 + + arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++ + arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++ + 4 files changed, 82 insertions(+) + create mode 100644 arch/arm/common/krait-l2-accessors.c + create mode 100644 arch/arm/include/asm/krait-l2-accessors.h + +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -9,6 +9,9 @@ config DMABOUNCE + bool + select ZONE_DMA + ++config KRAIT_L2_ACCESSORS ++ bool ++ + config SHARP_LOCOMO + bool + +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -7,6 +7,7 @@ obj-y += firmware.o + obj-$(CONFIG_ICST) += icst.o + obj-$(CONFIG_SA1111) += sa1111.o + obj-$(CONFIG_DMABOUNCE) += dmabounce.o ++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o + obj-$(CONFIG_SHARP_LOCOMO) += locomo.o + obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o +--- /dev/null ++++ b/arch/arm/common/krait-l2-accessors.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++static DEFINE_RAW_SPINLOCK(krait_l2_lock); ++ ++void krait_set_l2_indirect_reg(u32 addr, u32 val) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then write to the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); ++ isb(); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++} ++EXPORT_SYMBOL(krait_set_l2_indirect_reg); ++ ++u32 krait_get_l2_indirect_reg(u32 addr) ++{ ++ u32 val; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then read from the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++ ++ return val; ++} ++EXPORT_SYMBOL(krait_get_l2_indirect_reg); +--- /dev/null ++++ b/arch/arm/include/asm/krait-l2-accessors.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (c) 2011-2013, 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 __ASMARM_KRAIT_L2_ACCESSORS_H ++#define __ASMARM_KRAIT_L2_ACCESSORS_H ++ ++extern void krait_set_l2_indirect_reg(u32 addr, u32 val); ++extern u32 krait_get_l2_indirect_reg(u32 addr); ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch new file mode 100644 index 000000000..6efa5d804 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch @@ -0,0 +1,177 @@ +From 4c28a15ea536281c8d619e5c6716ade914c79a6e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:21 -0700 +Subject: [PATCH 1/2] clk: mux: Split out register accessors for reuse + +We want to reuse the logic in clk-mux.c for other clock drivers +that don't use readl as register accessors. Fortunately, there +really isn't much to the mux code besides the table indirection +and quirk flags if you assume any bit shifting and masking has +been done already. Pull that logic out into reusable functions +that operate on an optional table and some flags so that other +drivers can use the same logic. + +Signed-off-by: Stephen Boyd +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk-mux.c | 74 +++++++++++++++++++++++++++----------------- + include/linux/clk-provider.h | 9 ++++-- + 2 files changed, 53 insertions(+), 30 deletions(-) + +--- a/drivers/clk/clk-mux.c ++++ b/drivers/clk/clk-mux.c +@@ -28,35 +28,24 @@ + + #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +-static u8 clk_mux_get_parent(struct clk_hw *hw) ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, ++ unsigned int *table, unsigned long flags) + { +- struct clk_mux *mux = to_clk_mux(hw); + int num_parents = clk_hw_get_num_parents(hw); +- u32 val; + +- /* +- * FIXME need a mux-specific flag to determine if val is bitwise or numeric +- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 +- * to 0x7 (index starts at one) +- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so +- * val = 0x4 really means "bit 2, index starts at bit 0" +- */ +- val = clk_readl(mux->reg) >> mux->shift; +- val &= mux->mask; +- +- if (mux->table) { ++ if (table) { + int i; + + for (i = 0; i < num_parents; i++) +- if (mux->table[i] == val) ++ if (table[i] == val) + return i; + return -EINVAL; + } + +- if (val && (mux->flags & CLK_MUX_INDEX_BIT)) ++ if (val && (flags & CLK_MUX_INDEX_BIT)) + val = ffs(val) - 1; + +- if (val && (mux->flags & CLK_MUX_INDEX_ONE)) ++ if (val && (flags & CLK_MUX_INDEX_ONE)) + val--; + + if (val >= num_parents) +@@ -64,24 +53,53 @@ static u8 clk_mux_get_parent(struct clk_ + + return val; + } ++EXPORT_SYMBOL_GPL(clk_mux_get_parent); + +-static int clk_mux_set_parent(struct clk_hw *hw, u8 index) ++static u8 _clk_mux_get_parent(struct clk_hw *hw) + { + struct clk_mux *mux = to_clk_mux(hw); + u32 val; +- unsigned long flags = 0; + +- if (mux->table) +- index = mux->table[index]; ++ /* ++ * FIXME need a mux-specific flag to determine if val is bitwise or numeric ++ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 ++ * to 0x7 (index starts at one) ++ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so ++ * val = 0x4 really means "bit 2, index starts at bit 0" ++ */ ++ val = clk_readl(mux->reg) >> mux->shift; ++ val &= mux->mask; + +- else { +- if (mux->flags & CLK_MUX_INDEX_BIT) +- index = 1 << index; ++ return clk_mux_get_parent(hw, val, mux->table, mux->flags); ++} ++ ++unsigned int clk_mux_reindex(u8 index, unsigned int *table, ++ unsigned long flags) ++{ ++ unsigned int val = index; + +- if (mux->flags & CLK_MUX_INDEX_ONE) +- index++; ++ if (table) { ++ val = table[val]; ++ } else { ++ if (flags & CLK_MUX_INDEX_BIT) ++ val = 1 << index; ++ ++ if (flags & CLK_MUX_INDEX_ONE) ++ val++; + } + ++ return val; ++} ++EXPORT_SYMBOL_GPL(clk_mux_reindex); ++ ++static int clk_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct clk_mux *mux = to_clk_mux(hw); ++ u32 val; ++ unsigned long flags = 0; ++ ++ index = clk_mux_reindex(index, mux->table, mux->flags); ++ + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + else +@@ -105,7 +123,7 @@ static int clk_mux_set_parent(struct clk + } + + const struct clk_ops clk_mux_ops = { +- .get_parent = clk_mux_get_parent, ++ .get_parent = _clk_mux_get_parent, + .set_parent = clk_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, + }; +@@ -120,7 +138,7 @@ struct clk *clk_register_mux_table(struc + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, +- u8 clk_mux_flags, u32 *table, spinlock_t *lock) ++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock) + { + struct clk_mux *mux; + struct clk *clk; +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -433,7 +433,7 @@ void clk_unregister_divider(struct clk * + struct clk_mux { + struct clk_hw hw; + void __iomem *reg; +- u32 *table; ++ unsigned int *table; + u32 mask; + u8 shift; + u8 flags; +@@ -449,6 +449,11 @@ struct clk_mux { + extern const struct clk_ops clk_mux_ops; + extern const struct clk_ops clk_mux_ro_ops; + ++unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, ++ unsigned int *table, unsigned long flags); ++unsigned int clk_mux_reindex(u8 index, unsigned int *table, ++ unsigned long flags); ++ + struct clk *clk_register_mux(struct device *dev, const char *name, + const char * const *parent_names, u8 num_parents, + unsigned long flags, +@@ -459,7 +464,7 @@ struct clk *clk_register_mux_table(struc + const char * const *parent_names, u8 num_parents, + unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, +- u8 clk_mux_flags, u32 *table, spinlock_t *lock); ++ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock); + + void clk_unregister_mux(struct clk *clk); + diff --git a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch new file mode 100644 index 000000000..5015d32d8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch @@ -0,0 +1,122 @@ +From 39d42ce5031d2a4f92fa203b87acfbab340b15a2 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:22 -0700 +Subject: [PATCH 2/2] clk: Avoid sending high rates to downstream clocks during + set_rate + +If a clock is on and we call clk_set_rate() on it we may get into +a situation where the clock temporarily increases in rate +dramatically while we walk the tree and call .set_rate() ops. For +example, consider a case where a PLL feeds into a divider. +Initially the divider is set to divide by 1 and the PLL is +running fairly slow (100MHz). The downstream consumer of the +divider output can only handle rates =< 400 MHz, but the divider +can only choose between divisors of 1 and 4. + + +-----+ +----------------+ + | PLL |-->| div 1 or div 4 |---> consumer device + +-----+ +----------------+ + +To achieve a rate of 400MHz on the output of the divider, we +would have to set the rate of the PLL to 1.6 GHz and then divide +it by 4. The current code would set the PLL to 1.6GHz first while +the divider is still set to 1, thus causing the downstream +consumer of the clock to receive a few clock cycles of 1.6GHz +clock (far beyond it's maximum acceptable rate). We should be +changing the divider first before increasing the PLL rate to +avoid this problem. + +Therefore, set the rate of any child clocks that are increasing +in rate from their current rate so that they can increase their +dividers if necessary. We assume that there isn't such a thing as +minimum rate requirements. + +Signed-off-by: Stephen Boyd +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ + 1 file changed, 22 insertions(+), 12 deletions(-) + +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1427,21 +1427,24 @@ static struct clk_core *clk_propagate_ra + * walk down a subtree and set the new rates notifying the rate + * change on the way + */ +-static void clk_change_rate(struct clk_core *core) ++static void ++clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) + { + struct clk_core *child; + struct hlist_node *tmp; + unsigned long old_rate; +- unsigned long best_parent_rate = 0; + bool skip_set_rate = false; + struct clk_core *old_parent; + +- old_rate = core->rate; ++ hlist_for_each_entry(child, &core->children, child_node) { ++ /* Skip children who will be reparented to another clock */ ++ if (child->new_parent && child->new_parent != core) ++ continue; ++ if (child->new_rate > child->rate) ++ clk_change_rate(child, core->new_rate); ++ } + +- if (core->new_parent) +- best_parent_rate = core->new_parent->rate; +- else if (core->parent) +- best_parent_rate = core->parent->rate; ++ old_rate = core->rate; + + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); +@@ -1467,7 +1470,7 @@ static void clk_change_rate(struct clk_c + + trace_clk_set_rate_complete(core, core->new_rate); + +- core->rate = clk_recalc(core, best_parent_rate); ++ core->rate = core->new_rate; + + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +@@ -1483,12 +1486,13 @@ static void clk_change_rate(struct clk_c + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != core) + continue; +- clk_change_rate(child); ++ if (child->new_rate != child->rate) ++ clk_change_rate(child, core->new_rate); + } + + /* handle the new child who might not be in core->children yet */ +- if (core->new_child) +- clk_change_rate(core->new_child); ++ if (core->new_child && core->new_child->new_rate != core->new_child->rate) ++ clk_change_rate(core->new_child, core->new_rate); + } + + static int clk_core_set_rate_nolock(struct clk_core *core, +@@ -1497,6 +1501,7 @@ static int clk_core_set_rate_nolock(stru + struct clk_core *top, *fail_clk; + unsigned long rate = req_rate; + int ret = 0; ++ unsigned long parent_rate; + + if (!core) + return 0; +@@ -1522,8 +1527,13 @@ static int clk_core_set_rate_nolock(stru + return -EBUSY; + } + ++ if (top->parent) ++ parent_rate = top->parent->rate; ++ else ++ parent_rate = 0; ++ + /* change the rates */ +- clk_change_rate(top); ++ clk_change_rate(top, parent_rate); + + core->req_rate = req_rate; + diff --git a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch new file mode 100644 index 000000000..de3ac7d14 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch @@ -0,0 +1,151 @@ +From f7a00ea959be31f9b742042294a359d508edce94 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:23 -0700 +Subject: [PATCH] clk: Add safe switch hook + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +Signed-off-by: Stephen Boyd +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ + include/linux/clk-provider.h | 1 + + 2 files changed, 54 insertions(+), 8 deletions(-) + +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -51,9 +51,12 @@ struct clk_core { + struct clk_core **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long req_rate; ++ unsigned long old_rate; + unsigned long new_rate; ++ struct clk_core *safe_parent; + struct clk_core *new_parent; + struct clk_core *new_child; + unsigned long flags; +@@ -1271,7 +1274,8 @@ out: + static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, + struct clk_core *new_parent, u8 p_index) + { +- struct clk_core *child; ++ struct clk_core *child, *parent; ++ struct clk_hw *parent_hw; + + core->new_rate = new_rate; + core->new_parent = new_parent; +@@ -1281,6 +1285,18 @@ static void clk_calc_subtree(struct clk_ + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; + ++ if (core->ops->get_safe_parent) { ++ parent_hw = core->ops->get_safe_parent(core->hw); ++ if (parent_hw) { ++ parent = parent_hw->core; ++ p_index = clk_fetch_parent_index(core, parent); ++ core->safe_parent_index = p_index; ++ core->safe_parent = parent; ++ } ++ } else { ++ core->safe_parent = NULL; ++ } ++ + hlist_for_each_entry(child, &core->children, child_node) { + child->new_rate = clk_recalc(child, new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); +@@ -1393,14 +1409,43 @@ static struct clk_core *clk_propagate_ra + unsigned long event) + { + struct clk_core *child, *tmp_clk, *fail_clk = NULL; ++ struct clk_core *old_parent; + int ret = NOTIFY_DONE; + +- if (core->rate == core->new_rate) ++ if (core->rate == core->new_rate && event != POST_RATE_CHANGE) + return NULL; + ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ if (core->safe_parent) ++ core->ops->set_parent(core->hw, core->safe_parent_index); ++ core->old_rate = core->rate; ++ break; ++ case POST_RATE_CHANGE: ++ if (core->safe_parent) { ++ old_parent = __clk_set_parent_before(core, ++ core->new_parent); ++ if (core->ops->set_rate_and_parent) { ++ core->ops->set_rate_and_parent(core->hw, ++ core->new_rate, ++ core->new_parent ? ++ core->new_parent->rate : 0, ++ core->new_parent_index); ++ } else if (core->ops->set_parent) { ++ core->ops->set_parent(core->hw, ++ core->new_parent_index); ++ } ++ __clk_set_parent_after(core, core->new_parent, ++ old_parent); ++ } ++ break; ++ } ++ + if (core->notifier_count) { +- ret = __clk_notify(core, event, core->rate, core->new_rate); +- if (ret & NOTIFY_STOP_MASK) ++ if (event != POST_RATE_CHANGE || core->old_rate != core->rate) ++ ret = __clk_notify(core, event, core->old_rate, ++ core->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = core; + } + +@@ -1446,7 +1491,8 @@ clk_change_rate(struct clk_core *core, u + + old_rate = core->rate; + +- if (core->new_parent && core->new_parent != core->parent) { ++ if (core->new_parent && core->new_parent != core->parent && ++ !core->safe_parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); + +@@ -1472,9 +1518,6 @@ clk_change_rate(struct clk_core *core, u + + core->rate = core->new_rate; + +- if (core->notifier_count && old_rate != core->rate) +- __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +- + if (core->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(core, core->new_rate); + +@@ -1537,6 +1580,8 @@ static int clk_core_set_rate_nolock(stru + + core->req_rate = req_rate; + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); ++ + return ret; + } + +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -202,6 +202,7 @@ struct clk_ops { + struct clk_rate_request *req); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); + int (*set_rate_and_parent)(struct clk_hw *hw, diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 000000000..7e964490e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,351 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) +From: Stephen Boyd +X-Patchwork-Id: 6063261 +Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:24 -0700 + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd + +--- +I'd really like to get rid of __clk_hfpll_init_once() if possible... + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ + 3 files changed, 308 insertions(+) + create mode 100644 drivers/clk/qcom/clk-hfpll.c + create mode 100644 drivers/clk/qcom/clk-hfpll.h + +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o + +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.c +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++#define PLL_OUTCTRL BIT(0) ++#define PLL_BYPASSNL BIT(1) ++#define PLL_RESET_N BIT(2) ++ ++/* Initialize a HFPLL at a given rate and enable it. */ ++static void __clk_hfpll_init_once(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ if (likely(h->init_done)) ++ return; ++ ++ /* Configure PLL parameters for integer mode. */ ++ if (hd->config_val) ++ regmap_write(regmap, hd->config_reg, hd->config_val); ++ regmap_write(regmap, hd->m_reg, 0); ++ regmap_write(regmap, hd->n_reg, 1); ++ ++ if (hd->user_reg) { ++ u32 regval = hd->user_val; ++ unsigned long rate; ++ ++ rate = clk_hw_get_rate(hw); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate) ++ regval |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, regval); ++ } ++ ++ if (hd->droop_reg) ++ regmap_write(regmap, hd->droop_reg, hd->droop_val); ++ ++ h->init_done = true; ++} ++ ++static void __clk_hfpll_enable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 val; ++ ++ __clk_hfpll_init_once(hw); ++ ++ /* Disable PLL bypass mode. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); ++ ++ /* ++ * H/W requires a 5us delay between disabling the bypass and ++ * de-asserting the reset. Delay 10us just to be safe. ++ */ ++ udelay(10); ++ ++ /* De-assert active-low PLL reset. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); ++ ++ /* Wait for PLL to lock. */ ++ if (hd->status_reg) { ++ do { ++ regmap_read(regmap, hd->status_reg, &val); ++ } while (!(val & BIT(hd->lock_bit))); ++ } else { ++ udelay(60); ++ } ++ ++ /* Enable PLL output. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); ++} ++ ++/* Enable an already-configured HFPLL. */ ++static int clk_hfpll_enable(struct clk_hw *hw) ++{ ++ unsigned long flags; ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) ++ __clk_hfpll_enable(hw); ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static void __clk_hfpll_disable(struct clk_hfpll *h) ++{ ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ /* ++ * Disable the PLL output, disable test mode, enable the bypass mode, ++ * and assert the reset. ++ */ ++ regmap_update_bits(regmap, hd->mode_reg, ++ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); ++} ++ ++static void clk_hfpll_disable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ __clk_hfpll_disable(h); ++ spin_unlock_irqrestore(&h->lock, flags); ++} ++ ++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ unsigned long rrate; ++ ++ rate = clamp(rate, hd->min_rate, hd->max_rate); ++ ++ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; ++ if (rrate > hd->max_rate) ++ rrate -= *parent_rate; ++ ++ return rrate; ++} ++ ++/* ++ * For optimization reasons, assumes no downstream clocks are actively using ++ * it. ++ */ ++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ unsigned long flags; ++ u32 l_val, val; ++ bool enabled; ++ ++ l_val = rate / parent_rate; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ ++ enabled = __clk_is_enabled(hw->clk); ++ if (enabled) ++ __clk_hfpll_disable(h); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_reg && hd->user_vco_mask) { ++ regmap_read(regmap, hd->user_reg, &val); ++ if (rate <= hd->low_vco_max_rate) ++ val &= ~hd->user_vco_mask; ++ else ++ val |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, val); ++ } ++ ++ regmap_write(regmap, hd->l_reg, l_val); ++ ++ if (enabled) ++ __clk_hfpll_enable(hw); ++ ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 l_val; ++ ++ regmap_read(regmap, hd->l_reg, &l_val); ++ ++ return l_val * parent_rate; ++} ++ ++static void clk_hfpll_init(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode, status; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { ++ __clk_hfpll_init_once(hw); ++ return; ++ } ++ ++ if (hd->status_reg) { ++ regmap_read(regmap, hd->status_reg, &status); ++ if (!(status & BIT(hd->lock_bit))) { ++ WARN(1, "HFPLL %s is ON, but not locked!\n", ++ __clk_get_name(hw->clk)); ++ clk_hfpll_disable(hw); ++ __clk_hfpll_init_once(hw); ++ } ++ } ++} ++ ++static int hfpll_is_enabled(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ mode &= 0x7; ++ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); ++} ++ ++const struct clk_ops clk_ops_hfpll = { ++ .enable = clk_hfpll_enable, ++ .disable = clk_hfpll_disable, ++ .is_enabled = hfpll_is_enabled, ++ .round_rate = clk_hfpll_round_rate, ++ .set_rate = clk_hfpll_set_rate, ++ .recalc_rate = clk_hfpll_recalc_rate, ++ .init = clk_hfpll_init, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_hfpll); +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __QCOM_CLK_HFPLL_H__ ++#define __QCOM_CLK_HFPLL_H__ ++ ++#include ++#include ++#include "clk-regmap.h" ++ ++struct hfpll_data { ++ u32 mode_reg; ++ u32 l_reg; ++ u32 m_reg; ++ u32 n_reg; ++ u32 user_reg; ++ u32 droop_reg; ++ u32 config_reg; ++ u32 status_reg; ++ u8 lock_bit; ++ ++ u32 droop_val; ++ u32 config_val; ++ u32 user_val; ++ u32 user_vco_mask; ++ unsigned long low_vco_max_rate; ++ ++ unsigned long min_rate; ++ unsigned long max_rate; ++}; ++ ++struct clk_hfpll { ++ struct hfpll_data const *d; ++ int init_done; ++ ++ struct clk_regmap clkr; ++ spinlock_t lock; ++}; ++ ++#define to_clk_hfpll(_hw) \ ++ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) ++ ++extern const struct clk_ops clk_ops_hfpll; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 000000000..83623206c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,206 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,06/13] clk: qcom: Add HFPLL driver +From: Stephen Boyd +X-Patchwork-Id: 6063231 +Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:25 -0700 + +On some devices (MSM8974 for example), the HFPLLs are +instantiated within the Krait processor subsystem as separate +register regions. Add a driver for these PLLs so that we can +provide HFPLL clocks for use by the system. + +Cc: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ + 4 files changed, 158 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt + create mode 100644 drivers/clk/qcom/hfpll.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt +@@ -0,0 +1,40 @@ ++High-Frequency PLL (HFPLL) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be "qcom,hfpll" ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: address and size of HPLL registers. An optional second ++ element specifies the address and size of the alias ++ register region. ++ ++- clock-output-names: ++ Usage: required ++ Value type: ++ Definition: Name of the PLL. Typically hfpllX where X is a CPU number ++ starting at 0. Otherwise hfpll_Y where Y is more specific ++ such as "l2". ++ ++Example: ++ ++1) An HFPLL for the L2 cache. ++ ++ clock-controller@f9016000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf9016000 0x30>; ++ clock-output-names = "hfpll_l2"; ++ }; ++ ++2) An HFPLL for CPU0. This HFPLL has the alias register region. ++ ++ clock-controller@f908a000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf908a000 0x30>, <0xf900a000 0x30>; ++ clock-output-names = "hfpll0"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -135,3 +135,11 @@ config MSM_MMCC_8974 + Support for the multimedia clock controller on msm8974 devices. + Say Y if you want to support multimedia devices such as display, + graphics, video encode/decode, camera, etc. ++ ++config QCOM_HFPLL ++ tristate "High-Frequency PLL (HFPLL) Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the high-frequency PLLs present on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling on devices ++ such as MSM8974, APQ8084, etc. +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o ++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++static const struct hfpll_data hdata = { ++ .mode_reg = 0x00, ++ .l_reg = 0x04, ++ .m_reg = 0x08, ++ .n_reg = 0x0c, ++ .user_reg = 0x10, ++ .config_reg = 0x14, ++ .config_val = 0x430405d, ++ .status_reg = 0x1c, ++ .lock_bit = 16, ++ ++ .user_val = 0x8, ++ .user_vco_mask = 0x100000, ++ .low_vco_max_rate = 1248000000, ++ .min_rate = 537600000UL, ++ .max_rate = 2900000000UL, ++}; ++ ++static const struct of_device_id qcom_hfpll_match_table[] = { ++ { .compatible = "qcom,hfpll" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); ++ ++static const struct regmap_config hfpll_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x30, ++ .fast_io = true, ++}; ++ ++static int qcom_hfpll_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ void __iomem *base; ++ struct regmap *regmap; ++ struct clk_hfpll *h; ++ struct clk_init_data init = { ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_ops_hfpll, ++ }; ++ ++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); ++ if (!h) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_string_index(dev->of_node, "clock-output-names", ++ 0, &init.name)) ++ return -ENODEV; ++ ++ h->d = &hdata; ++ h->clkr.hw.init = &init; ++ spin_lock_init(&h->lock); ++ ++ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct platform_driver qcom_hfpll_driver = { ++ .probe = qcom_hfpll_probe, ++ .driver = { ++ .name = "qcom-hfpll", ++ .of_match_table = qcom_hfpll_match_table, ++ }, ++}; ++module_platform_driver(qcom_hfpll_driver); ++ ++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-hfpll"); diff --git a/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch new file mode 100644 index 000000000..a01decfd8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch @@ -0,0 +1,127 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs +From: Stephen Boyd +X-Patchwork-Id: 6063241 +Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:27 -0700 + +Describe the HFPLLs present on IPQ806X devices. + +Signed-off-by: Stephen Boyd + +--- +drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -30,6 +30,7 @@ + #include "clk-pll.h" + #include "clk-rcg.h" + #include "clk-branch.h" ++#include "clk-hfpll.h" + #include "reset.h" + + static struct clk_pll pll0 = { +@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 0x3240, ++ .l_reg = 0x3248, ++ .m_reg = 0x324c, ++ .n_reg = 0x3250, ++ .config_reg = 0x3244, ++ .status_reg = 0x325c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll1 = { ++ .d = &hfpll1_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll1", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), ++}; ++ ++static struct hfpll_data hfpll_l2_data = { ++ .mode_reg = 0x3300, ++ .l_reg = 0x3308, ++ .m_reg = 0x330c, ++ .n_reg = 0x3310, ++ .config_reg = 0x3304, ++ .status_reg = 0x331c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll_l2 = { ++ .d = &hfpll_l2_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll_l2", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), ++}; ++ ++ + static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, +@@ -2837,6 +2917,9 @@ static struct clk_regmap *gcc_ipq806x_cl + [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, + [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, + [NSSTCM_CLK] = &nss_tcm_clk.clkr, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 000000000..bbfcab9cc --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,272 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,09/13] clk: qcom: Add support for Krait clocks +From: Stephen Boyd +X-Patchwork-Id: 6063251 +Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:28 -0700 + +The Krait clocks are made up of a series of muxes and a divider +that choose between a fixed rate clock and dedicated HFPLLs for +each CPU. Instead of using mmio accesses to remux parents, the +Krait implementation exposes the remux control via cp15 +registers. Support these clocks. + +Signed-off-by: Stephen Boyd + +--- +drivers/clk/qcom/Kconfig | 4 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ + 4 files changed, 220 insertions(+) + create mode 100644 drivers/clk/qcom/clk-krait.c + create mode 100644 drivers/clk/qcom/clk-krait.h + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -143,3 +143,7 @@ config QCOM_HFPLL + Support for the high-frequency PLLs present on Qualcomm devices. + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. ++ ++config KRAIT_CLOCKS ++ bool ++ select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.c +@@ -0,0 +1,167 @@ ++/* ++ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "clk-krait.h" ++ ++/* Secondary and primary muxes share the same cp15 register */ ++static DEFINE_SPINLOCK(krait_clock_reg_lock); ++ ++#define LPL_SHIFT 8 ++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) ++{ ++ unsigned long flags; ++ u32 regval; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(mux->offset); ++ regval &= ~(mux->mask << mux->shift); ++ regval |= (sel & mux->mask) << mux->shift; ++ if (mux->lpl) { ++ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); ++ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); ++ } ++ krait_set_l2_indirect_reg(mux->offset, regval); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ /* Wait for switch to complete. */ ++ mb(); ++ udelay(1); ++} ++ ++static int krait_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = clk_mux_reindex(index, mux->parent_map, 0); ++ mux->en_mask = sel; ++ /* Don't touch mux if CPU is off as it won't work */ ++ if (__clk_is_enabled(hw->clk)) ++ __krait_mux_set_sel(mux, sel); ++ return 0; ++} ++ ++static u8 krait_mux_get_parent(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = krait_get_l2_indirect_reg(mux->offset); ++ sel >>= mux->shift; ++ sel &= mux->mask; ++ mux->en_mask = sel; ++ ++ return clk_mux_get_parent(hw, sel, mux->parent_map, 0); ++} ++ ++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw, ++ unsigned long *safe_freq) ++{ ++ int i; ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ int num_parents = clk_hw_get_num_parents(hw); ++ ++ i = mux->safe_sel; ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return clk_hw_get_parent_by_index(hw, i); ++} ++ ++static int krait_mux_enable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->en_mask); ++ ++ return 0; ++} ++ ++static void krait_mux_disable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->safe_sel); ++} ++ ++const struct clk_ops krait_mux_clk_ops = { ++ .enable = krait_mux_enable, ++ .disable = krait_mux_disable, ++ .set_parent = krait_mux_set_parent, ++ .get_parent = krait_mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate_closest, ++ .get_safe_parent = krait_mux_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(krait_mux_clk_ops); ++ ++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ ++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); ++ return DIV_ROUND_UP(*parent_rate, 2); ++} ++ ++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ unsigned long flags; ++ u32 val; ++ u32 mask = BIT(d->width) - 1; ++ ++ if (d->lpl) ++ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ val = krait_get_l2_indirect_reg(d->offset); ++ val &= ~mask; ++ krait_set_l2_indirect_reg(d->offset, val); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long ++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ u32 mask = BIT(d->width) - 1; ++ u32 div; ++ ++ div = krait_get_l2_indirect_reg(d->offset); ++ div >>= d->shift; ++ div &= mask; ++ div = (div + 1) * 2; ++ ++ return DIV_ROUND_UP(parent_rate, div); ++} ++ ++const struct clk_ops krait_div2_clk_ops = { ++ .round_rate = krait_div2_round_rate, ++ .set_rate = krait_div2_set_rate, ++ .recalc_rate = krait_div2_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(krait_div2_clk_ops); +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.h +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2013, 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 __QCOM_CLK_KRAIT_H ++#define __QCOM_CLK_KRAIT_H ++ ++#include ++ ++struct krait_mux_clk { ++ unsigned int *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) ++ ++extern const struct clk_ops krait_mux_clk_ops; ++ ++struct krait_div2_clk { ++ u32 offset; ++ u8 width; ++ u32 shift; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) ++ ++extern const struct clk_ops krait_div2_clk_ops; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 000000000..56374c5d0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,207 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver +From: Stephen Boyd +X-Patchwork-Id: 6063201 +Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:29 -0700 + +The ACC and GCC regions present in KPSSv1 contain registers to +control clocks and power to each Krait CPU and L2. For CPUfreq +purposes probe these devices and expose a mux clock that chooses +between PXO and PLL8. + +Cc: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++ + .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++ + 5 files changed, 139 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt + create mode 100644 drivers/clk/qcom/kpss-xcc.c + +--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +@@ -21,10 +21,17 @@ PROPERTIES + the register region. An optional second element specifies + the base address and size of the alias register region. + ++- clock-output-names: ++ Usage: optional ++ Value type: ++ Definition: Name of the output clock. Typically acpuX_aux where X is a ++ CPU number starting at 0. ++ + Example: + + clock-controller@2088000 { + compatible = "qcom,kpss-acc-v2"; + reg = <0x02088000 0x1000>, + <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt +@@ -0,0 +1,28 @@ ++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: should be one of: ++ "qcom,kpss-gcc" ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: base address and size of the register region ++ ++- clock-output-names: ++ Usage: required ++ Value type: ++ Definition: Name of the output clock. Typically acpu_l2_aux indicating ++ an L2 cache auxiliary clock. ++ ++Example: ++ ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -144,6 +144,14 @@ config QCOM_HFPLL + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. + ++config KPSS_XCC ++ tristate "KPSS Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the Krait ACC and GCC clock controllers. Say Y ++ if you want to support CPU frequency scaling on devices such ++ as MSM8960, APQ8064, etc. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -9,6 +9,7 @@ clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o + clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o ++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +--- /dev/null ++++ b/drivers/clk/qcom/kpss-xcc.c +@@ -0,0 +1,95 @@ ++/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static const char *aux_parents[] = { ++ "pll8_vote", ++ "pxo", ++}; ++ ++static unsigned int aux_parent_map[] = { ++ 3, ++ 0, ++}; ++ ++static const struct of_device_id kpss_xcc_match_table[] = { ++ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, ++ { .compatible = "qcom,kpss-gcc" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); ++ ++static int kpss_xcc_driver_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct clk *clk; ++ struct resource *res; ++ void __iomem *base; ++ const char *name; ++ ++ id = of_match_device(kpss_xcc_match_table, &pdev->dev); ++ if (!id) ++ return -ENODEV; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (id->data) { ++ if (of_property_read_string_index(pdev->dev.of_node, ++ "clock-output-names", 0, &name)) ++ return -ENODEV; ++ base += 0x14; ++ } else { ++ name = "acpu_l2_aux"; ++ base += 0x28; ++ } ++ ++ clk = clk_register_mux_table(&pdev->dev, name, aux_parents, ++ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, ++ 0, aux_parent_map, NULL); ++ ++ platform_set_drvdata(pdev, clk); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int kpss_xcc_driver_remove(struct platform_device *pdev) ++{ ++ clk_unregister_mux(platform_get_drvdata(pdev)); ++ return 0; ++} ++ ++static struct platform_driver kpss_xcc_driver = { ++ .probe = kpss_xcc_driver_probe, ++ .remove = kpss_xcc_driver_remove, ++ .driver = { ++ .name = "kpss-xcc", ++ .of_match_table = kpss_xcc_match_table, ++ }, ++}; ++module_platform_driver(kpss_xcc_driver); ++ ++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:kpss-xcc"); diff --git a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 000000000..bd4e7bca3 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,435 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver +From: Stephen Boyd +X-Patchwork-Id: 6063121 +Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:30 -0700 + +The Krait CPU clocks are made up of a primary mux and secondary +mux for each CPU and the L2, controlled via cp15 accessors. For +Kraits within KPSSv1 each secondary mux accepts a different aux +source, but on KPSSv2 each secondary mux accepts the same aux +source. + +Cc: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ + 4 files changed, 383 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt + create mode 100644 drivers/clk/qcom/krait-cc.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt +@@ -0,0 +1,22 @@ ++Krait Clock Controller ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be one of: ++ "qcom,krait-cc-v1" ++ "qcom,krait-cc-v2" ++ ++- #clock-cells: ++ Usage: required ++ Value type: ++ Definition: must be 1 ++ ++Example: ++ ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -152,6 +152,14 @@ config KPSS_XCC + if you want to support CPU frequency scaling on devices such + as MSM8960, APQ8064, etc. + ++config KRAITCC ++ tristate "Krait Clock Controller" ++ depends on COMMON_CLK_QCOM && ARM ++ select KRAIT_CLOCKS ++ help ++ Support for the Krait CPU clocks on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -28,3 +28,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ++obj-$(CONFIG_KRAITCC) += krait-cc.o +--- /dev/null ++++ b/drivers/clk/qcom/krait-cc.c +@@ -0,0 +1,352 @@ ++/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-krait.h" ++ ++static unsigned int sec_mux_map[] = { ++ 2, ++ 0, ++}; ++ ++static unsigned int pri_mux_map[] = { ++ 1, ++ 2, ++ 0, ++}; ++ ++static int ++krait_add_div(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct krait_div2_clk *div; ++ struct clk_init_data init = { ++ .num_parents = 1, ++ .ops = &krait_div2_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ const char *p_names[1]; ++ struct clk *clk; ++ ++ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); ++ if (!div) ++ return -ENOMEM; ++ ++ div->width = 2; ++ div->shift = 6; ++ div->lpl = id >= 0; ++ div->offset = offset; ++ div->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ init.parent_names = p_names; ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ kfree(init.name); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_register(dev, &div->hw); ++ kfree(p_names[0]); ++ kfree(init.name); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int ++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset, ++ bool unique_aux) ++{ ++ struct krait_mux_clk *mux; ++ static const char *sec_mux_list[] = { ++ "acpu_aux", ++ "qsb", ++ }; ++ struct clk_init_data init = { ++ .parent_names = sec_mux_list, ++ .num_parents = ARRAY_SIZE(sec_mux_list), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return -ENOMEM; ++ ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->has_safe_parent = true; ++ mux->safe_sel = 2; ++ mux->mask = 0x3; ++ mux->shift = 2; ++ mux->parent_map = sec_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ if (unique_aux) { ++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); ++ if (!sec_mux_list[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_aux; ++ } ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ if (unique_aux) ++ kfree(sec_mux_list[0]); ++err_aux: ++ kfree(init.name); ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct clk * ++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct krait_mux_clk *mux; ++ const char *p_names[3]; ++ struct clk_init_data init = { ++ .parent_names = p_names, ++ .num_parents = ARRAY_SIZE(p_names), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return ERR_PTR(-ENOMEM); ++ ++ mux->has_safe_parent = true; ++ mux->safe_sel = 0; ++ mux->mask = 0x3; ++ mux->shift = 0; ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->parent_map = pri_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); ++ if (!init.name) ++ return ERR_PTR(-ENOMEM); ++ ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p0; ++ } ++ ++ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!p_names[1]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p1; ++ } ++ ++ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!p_names[2]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p2; ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ kfree(p_names[2]); ++err_p2: ++ kfree(p_names[1]); ++err_p1: ++ kfree(p_names[0]); ++err_p0: ++ kfree(init.name); ++ return clk; ++} ++ ++/* id < 0 for L2, otherwise id == physical CPU number */ ++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) ++{ ++ int ret; ++ unsigned offset; ++ void *p = NULL; ++ const char *s; ++ struct clk *clk; ++ ++ if (id >= 0) { ++ offset = 0x4501 + (0x1000 * id); ++ s = p = kasprintf(GFP_KERNEL, "%d", id); ++ if (!s) ++ return ERR_PTR(-ENOMEM); ++ } else { ++ offset = 0x500; ++ s = "_l2"; ++ } ++ ++ ret = krait_add_div(dev, id, s, offset); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ clk = krait_add_pri_mux(dev, id, s, offset); ++err: ++ kfree(p); ++ return clk; ++} ++ ++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) ++{ ++ unsigned int idx = clkspec->args[0]; ++ struct clk **clks = data; ++ ++ if (idx >= 5) { ++ pr_err("%s: invalid clock index %d\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return clks[idx] ? : ERR_PTR(-ENODEV); ++} ++ ++static const struct of_device_id krait_cc_match_table[] = { ++ { .compatible = "qcom,krait-cc-v1", (void *)1UL }, ++ { .compatible = "qcom,krait-cc-v2" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, krait_cc_match_table); ++ ++static int krait_cc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *id; ++ unsigned long cur_rate, aux_rate; ++ int cpu; ++ struct clk *clk; ++ struct clk **clks; ++ struct clk *l2_pri_mux_clk; ++ ++ id = of_match_device(krait_cc_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ ++ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ ++ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ if (!id->data) { ++ clk = clk_register_fixed_factor(dev, "acpu_aux", ++ "gpll0_vote", 0, 1, 2); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ ++ /* Krait configurations have at most 4 CPUs and one L2 */ ++ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); ++ if (!clks) ++ return -ENOMEM; ++ ++ for_each_possible_cpu(cpu) { ++ clk = krait_add_clks(dev, cpu, id->data); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[cpu] = clk; ++ } ++ ++ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); ++ if (IS_ERR(l2_pri_mux_clk)) ++ return PTR_ERR(l2_pri_mux_clk); ++ clks[4] = l2_pri_mux_clk; ++ ++ /* ++ * We don't want the CPU or L2 clocks to be turned off at late init ++ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the ++ * refcount of these clocks. Any cpufreq/hotplug manager can assume ++ * that the clocks have already been prepared and enabled by the time ++ * they take over. ++ */ ++ for_each_online_cpu(cpu) { ++ clk_prepare_enable(l2_pri_mux_clk); ++ WARN(clk_prepare_enable(clks[cpu]), ++ "Unable to turn on CPU%d clock", cpu); ++ } ++ ++ /* ++ * Force reinit of HFPLLs and muxes to overwrite any potential ++ * incorrect configuration of HFPLLs and muxes by the bootloader. ++ * While at it, also make sure the cores are running at known rates ++ * and print the current rate. ++ * ++ * The clocks are set to aux clock rate first to make sure the ++ * secondary mux is not sourcing off of QSB. The rate is then set to ++ * two different rates to force a HFPLL reinit under all ++ * circumstances. ++ */ ++ cur_rate = clk_get_rate(l2_pri_mux_clk); ++ aux_rate = 384000000; ++ if (cur_rate == 1) { ++ pr_info("L2 @ QSB rate. Forcing new rate.\n"); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(l2_pri_mux_clk, aux_rate); ++ clk_set_rate(l2_pri_mux_clk, 2); ++ clk_set_rate(l2_pri_mux_clk, cur_rate); ++ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); ++ for_each_possible_cpu(cpu) { ++ clk = clks[cpu]; ++ cur_rate = clk_get_rate(clk); ++ if (cur_rate == 1) { ++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(clk, aux_rate); ++ clk_set_rate(clk, 2); ++ clk_set_rate(clk, cur_rate); ++ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); ++ } ++ ++ of_clk_add_provider(dev->of_node, krait_of_get, clks); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cc_driver = { ++ .probe = krait_cc_probe, ++ .driver = { ++ .name = "krait-cc", ++ .of_match_table = krait_cc_match_table, ++ }, ++}; ++module_platform_driver(krait_cc_driver); ++ ++MODULE_DESCRIPTION("Krait CPU Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:krait-cc"); diff --git a/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch new file mode 100644 index 000000000..e8d8ddf67 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch @@ -0,0 +1,304 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs +From: Stephen Boyd +X-Patchwork-Id: 6063191 +Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:31 -0700 + +Register a cpufreq-generic device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Cc: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++ + drivers/cpufreq/Kconfig.arm | 9 + + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++ + 4 files changed, 252 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt + create mode 100644 drivers/cpufreq/qcom-cpufreq.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt +@@ -0,0 +1,38 @@ ++Qualcomm Process Voltage Scaling Tables ++ ++The node name is required to be "qcom,pvs". There shall only be one ++such node present in the root of the tree. ++ ++PROPERTIES ++ ++- qcom,pvs-format-a or qcom,pvs-format-b: ++ Usage: required ++ Value type: ++ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties. ++ If qcom,pvs-format-a is used the table is two columns ++ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage, ++ and current in that order). ++ ++- qcom,speedX-pvsY-bin-vZ: ++ Usage: required ++ Value type: ++ Definition: The PVS table corresponding to the speed bin X, pvs bin Y, ++ and version Z. ++Example: ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 384000000 950000 >, ++ < 486000000 975000 >, ++ < 594000000 1000000 >, ++ < 702000000 1025000 >, ++ < 810000000 1075000 >, ++ < 918000000 1100000 >, ++ < 1026000000 1125000 >, ++ < 1134000000 1175000 >, ++ < 1242000000 1200000 >, ++ < 1350000000 1225000 >, ++ < 1458000000 1237500 >, ++ < 1512000000 1250000 >; ++ }; +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -95,6 +95,15 @@ config ARM_OMAP2PLUS_CPUFREQ + depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS + ++config ARM_QCOM_CPUFREQ ++ tristate "Qualcomm based" ++ depends on ARCH_QCOM ++ select PM_OPP ++ help ++ This adds the CPUFreq driver for Qualcomm SoC based boards. ++ ++ If in doubt, say N. ++ + config ARM_S3C_CPUFREQ + bool + help +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -61,6 +61,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt81 + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o + obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o + obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -0,0 +1,204 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver) ++{ ++ void __iomem *base; ++ u32 pte_efuse; ++ ++ *speed = *pvs = *pvs_ver = 0; ++ ++ base = ioremap(0x007000c0, 4); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ iounmap(base); ++ ++ *speed = pte_efuse & 0xf; ++ if (*speed == 0xf) ++ *speed = (pte_efuse >> 4) & 0xf; ++ ++ if (*speed == 0xf) { ++ *speed = 0; ++ pr_warn("Speed bin: Defaulting to %d\n", *speed); ++ } else { ++ pr_info("Speed bin: %d\n", *speed); ++ } ++ ++ *pvs = (pte_efuse >> 10) & 0x7; ++ if (*pvs == 0x7) ++ *pvs = (pte_efuse >> 13) & 0x7; ++ ++ if (*pvs == 0x7) { ++ *pvs = 0; ++ pr_warn("PVS bin: Defaulting to %d\n", *pvs); ++ } else { ++ pr_info("PVS bin: %d\n", *pvs); ++ } ++} ++ ++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver) ++{ ++ u32 pte_efuse, redundant_sel; ++ void __iomem *base; ++ ++ *speed = 0; ++ *pvs = 0; ++ *pvs_ver = 0; ++ ++ base = ioremap(0xfc4b80b0, 8); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ redundant_sel = (pte_efuse >> 24) & 0x7; ++ *speed = pte_efuse & 0x7; ++ /* 4 bits of PVS are in efuse register bits 31, 8-6. */ ++ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); ++ *pvs_ver = (pte_efuse >> 4) & 0x3; ++ ++ switch (redundant_sel) { ++ case 1: ++ *speed = (pte_efuse >> 27) & 0xf; ++ break; ++ case 2: ++ *pvs = (pte_efuse >> 27) & 0xf; ++ break; ++ } ++ ++ /* Check SPEED_BIN_BLOW_STATUS */ ++ if (pte_efuse & BIT(3)) { ++ pr_info("Speed bin: %d\n", *speed); ++ } else { ++ pr_warn("Speed bin not set. Defaulting to 0!\n"); ++ *speed = 0; ++ } ++ ++ /* Check PVS_BLOW_STATUS */ ++ pte_efuse = readl_relaxed(base + 0x4) & BIT(21); ++ if (pte_efuse) { ++ pr_info("PVS bin: %d\n", *pvs); ++ } else { ++ pr_warn("PVS bin not set. Defaulting to 0!\n"); ++ *pvs = 0; ++ } ++ ++ pr_info("PVS version: %d\n", *pvs_ver); ++ iounmap(base); ++} ++ ++static int __init qcom_cpufreq_populate_opps(void) ++{ ++ int len, rows, cols, i, k, speed, pvs, pvs_ver; ++ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX"; ++ struct device_node *np; ++ struct device *dev; ++ int cpu = 0; ++ ++ np = of_find_node_by_name(NULL, "qcom,pvs"); ++ if (!np) ++ return -ENODEV; ++ ++ if (of_property_read_bool(np, "qcom,pvs-format-a")) { ++ get_krait_bin_format_a(&speed, &pvs, &pvs_ver); ++ cols = 2; ++ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) { ++ get_krait_bin_format_b(&speed, &pvs, &pvs_ver); ++ cols = 3; ++ } else { ++ return -ENODEV; ++ } ++ ++ snprintf(table_name, sizeof(table_name), ++ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver); ++ ++ if (!of_find_property(np, table_name, &len)) ++ return -EINVAL; ++ ++ len /= sizeof(u32); ++ if (len % cols || len == 0) ++ return -EINVAL; ++ ++ rows = len / cols; ++ ++ for (i = 0, k = 0; i < rows; i++) { ++ u32 freq, volt; ++ ++ of_property_read_u32_index(np, table_name, k++, &freq); ++ of_property_read_u32_index(np, table_name, k++, &volt); ++ while (k % cols) ++ k++; /* Skip uA entries if present */ ++ for (cpu = 0; cpu < num_possible_cpus(); cpu++) { ++ dev = get_cpu_device(cpu); ++ if (!dev) ++ return -ENODEV; ++ if (dev_pm_opp_add(dev, freq, volt)) ++ pr_warn("failed to add OPP %u\n", freq); ++ } ++ } ++ ++ return 0; ++} ++ ++static int __init qcom_cpufreq_driver_init(void) ++{ ++ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; ++ struct platform_device_info devinfo = { ++ .name = "cpufreq-dt", ++ .data = &pdata, ++ .size_data = sizeof(pdata), ++ }; ++ struct device *cpu_dev; ++ struct device_node *np; ++ int ret; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) ++ return -ENODEV; ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) ++ return -ENOENT; ++ ++ if (!of_device_is_compatible(np, "qcom,krait")) { ++ of_node_put(np); ++ return -ENODEV; ++ } ++ of_node_put(np); ++ ++ ret = qcom_cpufreq_populate_opps(); ++ if (ret) ++ return ret; ++ ++ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo)); ++} ++module_init(qcom_cpufreq_driver_init); ++ ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch new file mode 100644 index 000000000..45870c436 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch @@ -0,0 +1,96 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -26,6 +26,11 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2a>; ++ voltage-tolerance = <5>; + }; + + cpu@1 { +@@ -36,12 +41,20 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2b>; + }; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; + }; ++ ++ qcom,l2 { ++ qcom,l2-rates = <384000000 1000000000 1200000000>; ++ }; + }; + + cpu-pmu { +@@ -73,6 +86,46 @@ + }; + }; + ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 1400000000 1250000 >, ++ < 1200000000 1200000 >, ++ < 1000000000 1150000 >, ++ < 800000000 1100000 >, ++ < 600000000 1050000 >, ++ < 384000000 1000000 >; ++ ++ qcom,speed0-pvs1-bin-v0 = ++ < 1400000000 1175000 >, ++ < 1200000000 1125000 >, ++ < 1000000000 1075000 >, ++ < 800000000 1025000 >, ++ < 600000000 975000 >, ++ < 384000000 925000 >; ++ ++ qcom,speed0-pvs2-bin-v0 = ++ < 1400000000 1125000 >, ++ < 1200000000 1075000 >, ++ < 1000000000 1025000 >, ++ < 800000000 995000 >, ++ < 600000000 925000 >, ++ < 384000000 875000 >; ++ ++ qcom,speed0-pvs3-bin-v0 = ++ < 1400000000 1050000 >, ++ < 1200000000 1000000 >, ++ < 1000000000 950000 >, ++ < 800000000 900000 >, ++ < 600000000 850000 >, ++ < 384000000 800000 >; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -216,11 +269,13 @@ + acc0: clock-controller@2088000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; + + acc1: clock-controller@2098000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu1_aux"; + }; + + l2cc: clock-controller@2011000 { diff --git a/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch new file mode 100644 index 000000000..4f5c0efb7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch @@ -0,0 +1,76 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers +From: Andy Gross +X-Patchwork-Id: 6027361 +Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org> +To: Vinod Koul +Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, Kumar Gala , + Bjorn Andersson , + Andy Gross +Date: Tue, 17 Mar 2015 00:46:11 -0500 + +This patch removes the crci information from the dma channel property. At least +one client device requires using more than one CRCI value for a channel. This +does not match the current binding and the crci information needs to be removed. + +Instead, the client device will provide this information via other means. + +Signed-off-by: Andy Gross + +--- +Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt ++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt +@@ -4,8 +4,7 @@ Required properties: + - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 + - reg: Address range for DMA registers + - interrupts: Should contain one interrupt shared by all channels +-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell +- denotes CRCI (client rate control interface) flow control assignment. ++- #dma-cells: must be <1>. First cell denotes the channel number. + - clocks: Should contain the core clock and interface clock. + - clock-names: Must contain "core" for the core clock and "iface" for the + interface clock. +@@ -22,7 +21,7 @@ Example: + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; +- #dma-cells = <2>; ++ #dma-cells = <1>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; + clock-names = "core", "iface"; +@@ -35,15 +34,12 @@ Example: + qcom,ee = <0>; + }; + +-DMA clients must use the format descripted in the dma.txt file, using a three ++DMA clients must use the format descripted in the dma.txt file, using a two + cell specifier for each channel. + +-Each dmas request consists of 3 cells: ++Each dmas request consists of two cells: + 1. phandle pointing to the DMA controller + 2. channel number +- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. +- The CRCI is used for flow control. It identifies the peripheral device that +- is the source/destination for the transferred data. + + Example: + +@@ -56,7 +52,7 @@ Example: + + cs-gpios = <&qcom_pinmux 20 0>; + +- dmas = <&adm_dma 6 9>, +- <&adm_dma 5 10>; ++ dmas = <&adm_dma 6>, ++ <&adm_dma 5>; + dma-names = "rx", "tx"; + }; diff --git a/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch new file mode 100644 index 000000000..02038aa94 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch @@ -0,0 +1,964 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v6,2/2] dmaengine: Add ADM driver +From: Andy Gross +X-Patchwork-Id: 6027351 +Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org> +To: Vinod Koul +Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, Kumar Gala , + Bjorn Andersson , + Andy Gross +Date: Tue, 17 Mar 2015 00:46:12 -0500 + +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA +controller found in the MSM8x60 and IPQ/APQ8064 platforms. + +The ADM supports both memory to memory transactions and memory +to/from peripheral device transactions. The controller also provides flow +control capabilities for transactions to/from peripheral devices. + +The initial release of this driver supports slave transfers to/from peripherals +and also incorporates CRCI (client rate control interface) flow control. + +Signed-off-by: Andy Gross +Reviewed-by: sricharan + +--- +drivers/dma/Kconfig | 10 + + drivers/dma/Makefile | 1 + + drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 911 insertions(+) + create mode 100644 drivers/dma/qcom_adm.c + +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -558,4 +558,14 @@ config DMATEST + config DMA_ENGINE_RAID + bool + ++config QCOM_ADM ++ tristate "Qualcomm ADM support" ++ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ ---help--- ++ Enable support for the Qualcomm ADM DMA controller. This controller ++ provides DMA capabilities for both general purpose and on-chip ++ peripheral devices. ++ + endif +--- /dev/null ++++ b/drivers/dma/qcom_adm.c +@@ -0,0 +1,900 @@ ++/* ++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmaengine.h" ++#include "virt-dma.h" ++ ++/* ADM registers - calculated from channel number and security domain */ ++#define ADM_CHAN_MULTI 0x4 ++#define ADM_CI_MULTI 0x4 ++#define ADM_CRCI_MULTI 0x4 ++#define ADM_EE_MULTI 0x800 ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) ++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee) ++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) ++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) ++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) ++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) ++#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI) ++#define ADM_GP_CTL 0x3d8 ++#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \ ++ ADM_EE_OFFS(ee)) ++ ++/* channel status */ ++#define ADM_CH_STATUS_VALID BIT(1) ++ ++/* channel result */ ++#define ADM_CH_RSLT_VALID BIT(31) ++#define ADM_CH_RSLT_ERR BIT(3) ++#define ADM_CH_RSLT_FLUSH BIT(2) ++#define ADM_CH_RSLT_TPD BIT(1) ++ ++/* channel conf */ ++#define ADM_CH_CONF_SHADOW_EN BIT(12) ++#define ADM_CH_CONF_MPU_DISABLE BIT(11) ++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9) ++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) ++#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11)) ++ ++/* channel result conf */ ++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) ++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) ++ ++/* CRCI CTL */ ++#define ADM_CRCI_CTL_MUX_SEL BIT(18) ++#define ADM_CRCI_CTL_RST BIT(17) ++ ++/* CI configuration */ ++#define ADM_CI_RANGE_END(x) (x << 24) ++#define ADM_CI_RANGE_START(x) (x << 16) ++#define ADM_CI_BURST_4_WORDS BIT(2) ++#define ADM_CI_BURST_8_WORDS BIT(3) ++ ++/* GP CTL */ ++#define ADM_GP_CTL_LP_EN BIT(12) ++#define ADM_GP_CTL_LP_CNT(x) (x << 8) ++ ++/* Command pointer list entry */ ++#define ADM_CPLE_LP BIT(31) ++#define ADM_CPLE_CMD_PTR_LIST BIT(29) ++ ++/* Command list entry */ ++#define ADM_CMD_LC BIT(31) ++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++ ++#define ADM_CMD_TYPE_SINGLE 0x0 ++#define ADM_CMD_TYPE_BOX 0x3 ++ ++#define ADM_CRCI_MUX_SEL BIT(4) ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K-1) ++#define ADM_MAX_ROWS (SZ_64K-1) ++#define ADM_MAX_CHANNELS 16 ++ ++struct adm_desc_hw_box { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 row_len; ++ u32 num_rows; ++ u32 row_offset; ++}; ++ ++struct adm_desc_hw_single { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 len; ++}; ++ ++struct adm_async_desc { ++ struct virt_dma_desc vd; ++ struct adm_device *adev; ++ ++ size_t length; ++ enum dma_transfer_direction dir; ++ dma_addr_t dma_addr; ++ size_t dma_len; ++ ++ void *cpl; ++ dma_addr_t cp_addr; ++ u32 crci; ++ u32 mux; ++ u32 blk_size; ++}; ++ ++struct adm_chan { ++ struct virt_dma_chan vc; ++ struct adm_device *adev; ++ ++ /* parsed from DT */ ++ u32 id; /* channel id */ ++ ++ struct adm_async_desc *curr_txd; ++ struct dma_slave_config slave; ++ struct list_head node; ++ ++ int error; ++ int initialized; ++}; ++ ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct adm_chan, vc.chan); ++} ++ ++struct adm_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct adm_chan *channels; ++ ++ u32 ee; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ ++ struct reset_control *clk_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ int irq; ++}; ++ ++/** ++ * adm_free_chan - Frees dma resources associated with the specific channel ++ * ++ * Free all allocated descriptors associated with this channel ++ * ++ */ ++static void adm_free_chan(struct dma_chan *chan) ++{ ++ /* free all queued descriptors */ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++} ++ ++/** ++ * adm_get_blksize - Get block size from burst value ++ * ++ */ ++static int adm_get_blksize(unsigned int burst) ++{ ++ int ret; ++ ++ switch (burst) { ++ case 16: ++ case 32: ++ case 64: ++ case 128: ++ ret = ffs(burst>>4) - 1; ++ break; ++ case 192: ++ ret = 4; ++ break; ++ case 256: ++ ret = 5; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @crci: CRCI value ++ * @burst: Burst size of transaction ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_fc_descriptors(struct adm_chan *achan, ++ void *desc, struct scatterlist *sg, u32 crci, u32 burst, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_box *box_desc = NULL; ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 rows, row_offset, crci_cmd; ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ crci_cmd = ADM_CMD_SRC_CRCI(crci); ++ row_offset = burst; ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ crci_cmd = ADM_CMD_DST_CRCI(crci); ++ row_offset = burst << 16; ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ while (remainder >= burst) { ++ box_desc = desc; ++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; ++ box_desc->row_offset = row_offset; ++ box_desc->src_addr = *src; ++ box_desc->dst_addr = *dst; ++ ++ rows = remainder / burst; ++ rows = min_t(u32, rows, ADM_MAX_ROWS); ++ box_desc->num_rows = rows << 16 | rows; ++ box_desc->row_len = burst << 16 | burst; ++ ++ *incr_addr += burst * rows; ++ remainder -= burst * rows; ++ desc += sizeof(*box_desc); ++ } ++ ++ /* if leftover bytes, do one single descriptor */ ++ if (remainder) { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; ++ single_desc->len = remainder; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ desc += sizeof(*single_desc); ++ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ } else { ++ if (box_desc && sg_is_last(sg)) ++ box_desc->cmd |= ADM_CMD_LC; ++ } ++ ++ return desc; ++} ++ ++/** ++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, ++ void *desc, struct scatterlist *sg, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ do { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ single_desc->len = (remainder > ADM_MAX_XFER) ? ++ ADM_MAX_XFER : remainder; ++ ++ remainder -= single_desc->len; ++ *incr_addr += single_desc->len; ++ desc += sizeof(*single_desc); ++ } while (remainder); ++ ++ /* set last command if this is the end of the whole transaction */ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ ++ return desc; ++} ++ ++/** ++ * adm_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, unsigned int sg_len, ++ enum dma_transfer_direction direction, unsigned long flags, ++ void *context) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct scatterlist *sg; ++ u32 i, burst; ++ u32 single_count = 0, box_count = 0, crci = 0; ++ void *desc; ++ u32 *cple; ++ int blk_size = 0; ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(adev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* ++ * get burst value from slave configuration ++ */ ++ burst = (direction == DMA_MEM_TO_DEV) ? ++ achan->slave.dst_maxburst : ++ achan->slave.src_maxburst; ++ ++ /* if using flow control, validate burst and crci values */ ++ if (achan->slave.device_fc) { ++ ++ blk_size = adm_get_blksize(burst); ++ if (blk_size < 0) { ++ dev_err(adev->dev, "invalid burst value: %d\n", ++ burst); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ crci = achan->slave.slave_id & 0xf; ++ if (!crci || achan->slave.slave_id > 0x1f) { ++ dev_err(adev->dev, "invalid crci value\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ /* iterate through sgs and compute allocation size of structures */ ++ for_each_sg(sgl, sg, sg_len, i) { ++ if (achan->slave.device_fc) { ++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, ++ ADM_MAX_ROWS); ++ if (sg_dma_len(sg) % burst) ++ single_count++; ++ } else { ++ single_count += DIV_ROUND_UP(sg_dma_len(sg), ++ ADM_MAX_XFER); ++ } ++ } ++ ++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); ++ if (!async_desc) ++ return ERR_PTR(-ENOMEM); ++ ++ if (crci) ++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? ++ ADM_CRCI_CTL_MUX_SEL : 0; ++ async_desc->crci = crci; ++ async_desc->blk_size = blk_size; ++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + ++ box_count * sizeof(struct adm_desc_hw_box) + ++ sizeof(*cple) + 2 * ADM_DESC_ALIGN; ++ ++ async_desc->cpl = dma_alloc_writecombine(adev->dev, async_desc->dma_len, ++ &async_desc->dma_addr, GFP_NOWAIT); ++ ++ if (!async_desc->cpl) { ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ async_desc->adev = adev; ++ ++ /* both command list entry and descriptors must be 8 byte aligned */ ++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); ++ ++ /* init cmd list */ ++ *cple = ADM_CPLE_LP; ++ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ async_desc->length += sg_dma_len(sg); ++ ++ if (achan->slave.device_fc) ++ desc = adm_process_fc_descriptors(achan, desc, sg, crci, ++ burst, direction); ++ else ++ desc = adm_process_non_fc_descriptors(achan, desc, sg, ++ direction); ++ } ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++} ++ ++/** ++ * adm_terminate_all - terminate all transactions on a channel ++ * @achan: adm dma channel ++ * ++ * Dequeues and frees all transactions, aborts current transaction ++ * No callbacks are done ++ * ++ */ ++static int adm_terminate_all(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ vchan_get_all_descriptors(&achan->vc, &head); ++ ++ /* send flush command to terminate current transaction */ ++ writel_relaxed(0x0, ++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ vchan_dma_desc_free_list(&achan->vc, &head); ++ ++ return 0; ++} ++ ++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flag; ++ ++ spin_lock_irqsave(&achan->vc.lock, flag); ++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); ++ spin_unlock_irqrestore(&achan->vc.lock, flag); ++ ++ return 0; ++} ++ ++/** ++ * adm_start_dma - start next transaction ++ * @achan - ADM dma channel ++ */ ++static void adm_start_dma(struct adm_chan *achan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ ++ lockdep_assert_held(&achan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ /* write next command list out to the CMD FIFO */ ++ async_desc = container_of(vd, struct adm_async_desc, vd); ++ achan->curr_txd = async_desc; ++ ++ /* reset channel error */ ++ achan->error = 0; ++ ++ if (!achan->initialized) { ++ /* enable interrupts */ ++ writel(ADM_CH_CONF_SHADOW_EN | ++ ADM_CH_CONF_PERM_MPU_CONF | ++ ADM_CH_CONF_MPU_DISABLE | ++ ADM_CH_CONF_SEC_DOMAIN(adev->ee), ++ adev->regs + ADM_CH_CONF(achan->id)); ++ ++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ achan->initialized = 1; ++ } ++ ++ /* set the crci block size if this transaction requires CRCI */ ++ if (async_desc->crci) { ++ writel(async_desc->mux | async_desc->blk_size, ++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); ++ } ++ ++ /* make sure IRQ enable doesn't get reordered */ ++ wmb(); ++ ++ /* write next command list out to the CMD FIFO */ ++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, ++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); ++} ++ ++/** ++ * adm_dma_irq - irq handler for ADM controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t adm_dma_irq(int irq, void *data) ++{ ++ struct adm_device *adev = data; ++ u32 srcs, i; ++ struct adm_async_desc *async_desc; ++ unsigned long flags; ++ ++ srcs = readl_relaxed(adev->regs + ++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ struct adm_chan *achan = &adev->channels[i]; ++ u32 status, result; ++ ++ if (srcs & BIT(i)) { ++ status = readl_relaxed(adev->regs + ++ ADM_CH_STATUS_SD(i, adev->ee)); ++ ++ /* if no result present, skip */ ++ if (!(status & ADM_CH_STATUS_VALID)) ++ continue; ++ ++ result = readl_relaxed(adev->regs + ++ ADM_CH_RSLT(i, adev->ee)); ++ ++ /* no valid results, skip */ ++ if (!(result & ADM_CH_RSLT_VALID)) ++ continue; ++ ++ /* flag error if transaction was flushed or failed */ ++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) ++ achan->error = 1; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ async_desc = achan->curr_txd; ++ ++ achan->curr_txd = NULL; ++ ++ if (async_desc) { ++ vchan_cookie_complete(&async_desc->vd); ++ ++ /* kick off next DMA */ ++ adm_start_dma(achan); ++ } ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * adm_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ size_t residue = 0; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE || !txstate) ++ return ret; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ vd = vchan_find_desc(&achan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct adm_async_desc, vd)->length; ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ /* ++ * residue is either the full length if it is in the issued list, or 0 ++ * if it is in progress. We have no reliable way of determining ++ * anything inbetween ++ */ ++ dma_set_residue(txstate, residue); ++ ++ if (achan->error) ++ return DMA_ERROR; ++ ++ return ret; ++} ++ ++/** ++ * adm_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Issues all pending transactions and starts DMA ++ */ ++static void adm_issue_pending(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) ++ adm_start_dma(achan); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++} ++ ++/** ++ * adm_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void adm_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct adm_async_desc *async_desc = container_of(vd, ++ struct adm_async_desc, vd); ++ ++ dma_free_writecombine(async_desc->adev->dev, async_desc->dma_len, ++ async_desc->cpl, async_desc->dma_addr); ++ kfree(async_desc); ++} ++ ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, ++ u32 index) ++{ ++ achan->id = index; ++ achan->adev = adev; ++ ++ vchan_init(&achan->vc, &adev->common); ++ achan->vc.desc_free = adm_dma_free_desc; ++} ++ ++static int adm_dma_probe(struct platform_device *pdev) ++{ ++ struct adm_device *adev; ++ struct resource *iores; ++ int ret; ++ u32 i; ++ ++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); ++ if (!adev) ++ return -ENOMEM; ++ ++ adev->dev = &pdev->dev; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ adev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ if (IS_ERR(adev->regs)) ++ return PTR_ERR(adev->regs); ++ ++ adev->irq = platform_get_irq(pdev, 0); ++ if (adev->irq < 0) ++ return adev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); ++ if (ret) { ++ dev_err(adev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ adev->core_clk = devm_clk_get(adev->dev, "core"); ++ if (IS_ERR(adev->core_clk)) ++ return PTR_ERR(adev->core_clk); ++ ++ ret = clk_prepare_enable(adev->core_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable core clock\n"); ++ return ret; ++ } ++ ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) { ++ ret = PTR_ERR(adev->iface_clk); ++ goto err_disable_core_clk; ++ } ++ ++ ret = clk_prepare_enable(adev->iface_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); ++ goto err_disable_core_clk; ++ } ++ ++ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ ret = PTR_ERR(adev->clk_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ ret = PTR_ERR(adev->c0_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ ret = PTR_ERR(adev->c1_reset); ++ goto err_disable_clks; ++ } ++ ++ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ ret = PTR_ERR(adev->c2_reset); ++ goto err_disable_clks; ++ } ++ ++ reset_control_assert(adev->clk_reset); ++ reset_control_assert(adev->c0_reset); ++ reset_control_assert(adev->c1_reset); ++ reset_control_assert(adev->c2_reset); ++ ++ reset_control_deassert(adev->clk_reset); ++ reset_control_deassert(adev->c0_reset); ++ reset_control_deassert(adev->c1_reset); ++ reset_control_deassert(adev->c2_reset); ++ ++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, ++ sizeof(*adev->channels), GFP_KERNEL); ++ ++ if (!adev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clks; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&adev->common.channels); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) ++ adm_channel_init(adev, &adev->channels[i], i); ++ ++ /* reset CRCIs */ ++ for (i = 0; i < 16; i++) ++ writel(ADM_CRCI_CTL_RST, adev->regs + ++ ADM_CRCI_CTL(i, adev->ee)); ++ ++ /* configure client interfaces */ ++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); ++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); ++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); ++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), ++ adev->regs + ADM_GP_CTL); ++ ++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, ++ 0, "adm_dma", adev); ++ if (ret) ++ goto err_disable_clks; ++ ++ platform_set_drvdata(pdev, adev); ++ ++ adev->common.dev = adev->dev; ++ adev->common.dev->dma_parms = &adev->dma_parms; ++ ++ /* set capabilities */ ++ dma_cap_zero(adev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); ++ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; ++ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.device_free_chan_resources = adm_free_chan; ++ adev->common.device_prep_slave_sg = adm_prep_slave_sg; ++ adev->common.device_issue_pending = adm_issue_pending; ++ adev->common.device_tx_status = adm_tx_status; ++ adev->common.device_terminate_all = adm_terminate_all; ++ adev->common.device_config = adm_slave_config; ++ ++ ret = dma_async_device_register(&adev->common); ++ if (ret) { ++ dev_err(adev->dev, "failed to register dma async device\n"); ++ goto err_disable_clks; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, ++ of_dma_xlate_by_chan_id, ++ &adev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&adev->common); ++err_disable_clks: ++ clk_disable_unprepare(adev->iface_clk); ++err_disable_core_clk: ++ clk_disable_unprepare(adev->core_clk); ++ ++ return ret; ++} ++ ++static int adm_dma_remove(struct platform_device *pdev) ++{ ++ struct adm_device *adev = platform_get_drvdata(pdev); ++ struct adm_chan *achan; ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&adev->common); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ achan = &adev->channels[i]; ++ ++ /* mask IRQs for this channel/EE pair */ ++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ adm_terminate_all(&adev->channels[i].vc.chan); ++ } ++ ++ devm_free_irq(adev->dev, adev->irq, adev); ++ ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++ ++static struct platform_driver adm_dma_driver = { ++ .probe = adm_dma_probe, ++ .remove = adm_dma_remove, ++ .driver = { ++ .name = "adm-dma-engine", ++ .of_match_table = adm_of_match, ++ }, ++}; ++ ++module_platform_driver(adm_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross "); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -65,5 +65,6 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma- + obj-$(CONFIG_TI_EDMA) += edma.o + obj-$(CONFIG_XGENE_DMA) += xgene-dma.o + obj-$(CONFIG_ZX_DMA) += zx296702_dma.o ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o + + obj-y += xilinx/ diff --git a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch new file mode 100644 index 000000000..5383041ff --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch @@ -0,0 +1,42 @@ +From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Wed, 20 May 2015 15:41:07 +0530 +Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node + +This patch adds support for the ADM DMA on the IPQ8064 SOC + +Signed-off-by: Andy Gross +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++ + 2 files changed, 25 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -721,6 +721,26 @@ + + status = "disabled"; + }; ++ ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ #dma-cells = <1>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core", "iface"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_PBUS_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ reset-names = "clk", "pbus", "c0", "c1", "c2"; ++ qcom,ee = <0>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch new file mode 100644 index 000000000..154649dca --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch @@ -0,0 +1,83 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3, + 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw + mode +From: Archit Taneja +X-Patchwork-Id: 6927081 +Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja +Date: Mon, 3 Aug 2015 10:38:14 +0530 + +Some controllers can access the factory bad block marker from OOB only +when they read it in raw mode. When ECC is enabled, these controllers +discard reading/writing bad block markers, preventing access to them +altogether. + +The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks. +This results in the nand driver's ecc->read_oob() op to be called, which +works with ECC enabled. + +Create a new BBT option flag that tells nand_bbt to force the mode to +MTD_OPS_RAW. This would result in the correct op being called for the +underlying nand controller driver. + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +drivers/mtd/nand/nand_base.c | 6 +++++- + drivers/mtd/nand/nand_bbt.c | 6 +++++- + include/linux/mtd/bbm.h | 7 +++++++ + 3 files changed, 17 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -394,7 +394,11 @@ static int nand_default_block_markbad(st + } else { + ops.len = ops.ooblen = 1; + } +- ops.mode = MTD_OPS_PLACE_OOB; ++ ++ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW)) ++ ops.mode = MTD_OPS_RAW; ++ else ++ ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) +--- a/drivers/mtd/nand/nand_bbt.c ++++ b/drivers/mtd/nand/nand_bbt.c +@@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; +- ops.mode = MTD_OPS_PLACE_OOB; ++ ++ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW)) ++ ops.mode = MTD_OPS_RAW; ++ else ++ ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* +--- a/include/linux/mtd/bbm.h ++++ b/include/linux/mtd/bbm.h +@@ -116,6 +116,12 @@ struct nand_bbt_descr { + #define NAND_BBT_NO_OOB_BBM 0x00080000 + + /* ++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To ++ * be used by controllers which can access BBM only when ECC is disabled, i.e, ++ * when in RAW access mode ++ */ ++#define NAND_BBT_ACCESS_BBM_RAW 0x00100000 ++/* + * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr + * was allocated dynamicaly and must be freed in nand_release(). Has no meaning + * in nand_chip.bbt_options. diff --git a/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch new file mode 100644 index 000000000..84ff6e74e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch @@ -0,0 +1,2024 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver +From: Archit Taneja +X-Patchwork-Id: 6927101 +Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja +Date: Mon, 3 Aug 2015 10:38:15 +0530 + +The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx, +MDM9x15 series. + +It exists as a sub block inside the IPs EBI2 (External Bus Interface 2) +and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a +broader interface for external slow peripheral devices such as LCD and +NAND/NOR flash memory or SRAM like interfaces. + +We add support for the NAND controller found within EBI2. For the SoCs +of our interest, we only use the NAND controller within EBI2. Therefore, +it's safe for us to assume that the NAND controller is a standalone block +within the SoC. + +The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND +flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and +16 bit correction/step) and RS ECC(4 bit correction/step) that covers main +and spare data. The controller contains an internal 512 byte page buffer +to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA +for register read/write and data transfers. The controller performs page +reads and writes at a codeword/step level of 512 bytes. It can support up +to 2 external chips of different configurations. + +The driver prepares register read and write configuration descriptors for +each codeword, followed by data descriptors to read or write data from the +controller's internal buffer. It uses a single ADM DMA channel that we get +via dmaengine API. The controller requires 2 ADM CRCIs for command and +data flow control. These are passed via DT. + +The ecc layout used by the controller is syndrome like, but we can't use +the standard syndrome ecc ops because of several reasons. First, the amount +of data bytes covered by ecc isn't same in each step. Second, writing to +free oob space requires us writing to the entire step in which the oob +lies. This forces us to create our own ecc ops. + +One more difference is how the controller accesses the bad block marker. +The controller ignores reading the marker when ECC is enabled. ECC needs +to be explicity disabled to read or write to the bad block marker. For +this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to +read the factory provided bad block markers. + +v3: +- Refactor dma functions for maximum reuse +- Use dma_slave_confing on stack +- optimize and clean upempty_page_fixup using memchr_inv +- ensure portability with dma register reads using le32_* funcs +- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves +- fix handling of return values of dmaengine funcs +- constify wherever possible +- Remove dependency on ADM DMA in Kconfig +- Misc fixes and clean ups + +v2: +- Use new BBT flag that allows us to read BBM in raw mode +- reduce memcpy-s in the driver +- some refactor and clean ups because of above changes + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1921 insertions(+) + create mode 100644 drivers/mtd/nand/qcom_nandc.c + +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -546,4 +546,11 @@ config MTD_NAND_HISI504 + help + Enables support for NAND controller on Hisilicon SoC Hip04. + ++config MTD_NAND_QCOM ++ tristate "Support for NAND on QCOM SoCs" ++ depends on ARCH_QCOM ++ help ++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND ++ controller. This controller is found on IPQ806x SoC. ++ + endif # MTD_NAND +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nandc.c +@@ -0,0 +1,1918 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* NANDc reg offsets */ ++#define NAND_FLASH_CMD 0x00 ++#define NAND_ADDR0 0x04 ++#define NAND_ADDR1 0x08 ++#define NAND_FLASH_CHIP_SELECT 0x0c ++#define NAND_EXEC_CMD 0x10 ++#define NAND_FLASH_STATUS 0x14 ++#define NAND_BUFFER_STATUS 0x18 ++#define NAND_DEV0_CFG0 0x20 ++#define NAND_DEV0_CFG1 0x24 ++#define NAND_DEV0_ECC_CFG 0x28 ++#define NAND_DEV1_ECC_CFG 0x2c ++#define NAND_DEV1_CFG0 0x30 ++#define NAND_DEV1_CFG1 0x34 ++#define NAND_READ_ID 0x40 ++#define NAND_READ_STATUS 0x44 ++#define NAND_DEV_CMD0 0xa0 ++#define NAND_DEV_CMD1 0xa4 ++#define NAND_DEV_CMD2 0xa8 ++#define NAND_DEV_CMD_VLD 0xac ++#define SFLASHC_BURST_CFG 0xe0 ++#define NAND_ERASED_CW_DETECT_CFG 0xe8 ++#define NAND_ERASED_CW_DETECT_STATUS 0xec ++#define NAND_EBI2_ECC_BUF_CFG 0xf0 ++#define FLASH_BUF_ACC 0x100 ++ ++#define NAND_CTRL 0xf00 ++#define NAND_VERSION 0xf08 ++#define NAND_READ_LOCATION_0 0xf20 ++#define NAND_READ_LOCATION_1 0xf24 ++ ++/* dummy register offsets, used by write_reg_dma */ ++#define NAND_DEV_CMD1_RESTORE 0xdead ++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef ++ ++/* NAND_FLASH_CMD bits */ ++#define PAGE_ACC BIT(4) ++#define LAST_PAGE BIT(5) ++ ++/* NAND_FLASH_CHIP_SELECT bits */ ++#define NAND_DEV_SEL 0 ++#define DM_EN BIT(2) ++ ++/* NAND_FLASH_STATUS bits */ ++#define FS_OP_ERR BIT(4) ++#define FS_READY_BSY_N BIT(5) ++#define FS_MPU_ERR BIT(8) ++#define FS_DEVICE_STS_ERR BIT(16) ++#define FS_DEVICE_WP BIT(23) ++ ++/* NAND_BUFFER_STATUS bits */ ++#define BS_UNCORRECTABLE_BIT BIT(8) ++#define BS_CORRECTABLE_ERR_MSK 0x1f ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DISABLE_STATUS_AFTER_WRITE 4 ++#define CW_PER_PAGE 6 ++#define UD_SIZE_BYTES 9 ++#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define SPARE_SIZE_BYTES 23 ++#define NUM_ADDR_CYCLES 27 ++#define STATUS_BFR_READ 30 ++#define SET_RD_MODE_AFTER_STATUS 31 ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DEV0_CFG1_ECC_DISABLE 0 ++#define WIDE_FLASH 1 ++#define NAND_RECOVERY_CYCLES 2 ++#define CS_ACTIVE_BSY 5 ++#define BAD_BLOCK_BYTE_NUM 6 ++#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define WR_RD_BSY_GAP 17 ++#define ENABLE_BCH_ECC 27 ++ ++/* NAND_DEV0_ECC_CFG bits */ ++#define ECC_CFG_ECC_DISABLE 0 ++#define ECC_SW_RESET 1 ++#define ECC_MODE 4 ++#define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_NUM_DATA_BYTES 16 ++#define ECC_FORCE_CLK_OPEN 30 ++ ++/* NAND_DEV_CMD1 bits */ ++#define READ_ADDR 0 ++ ++/* NAND_DEV_CMD_VLD bits */ ++#define READ_START_VLD 0 ++ ++/* NAND_EBI2_ECC_BUF_CFG bits */ ++#define NUM_STEPS 0 ++ ++/* NAND_ERASED_CW_DETECT_CFG bits */ ++#define ERASED_CW_ECC_MASK 1 ++#define AUTO_DETECT_RES 0 ++#define MASK_ECC (1 << ERASED_CW_ECC_MASK) ++#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) ++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) ++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) ++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) ++ ++/* NAND_ERASED_CW_DETECT_STATUS bits */ ++#define PAGE_ALL_ERASED BIT(7) ++#define CODEWORD_ALL_ERASED BIT(6) ++#define PAGE_ERASED BIT(5) ++#define CODEWORD_ERASED BIT(4) ++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) ++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) ++ ++/* Version Mask */ ++#define NAND_VERSION_MAJOR_MASK 0xf0000000 ++#define NAND_VERSION_MAJOR_SHIFT 28 ++#define NAND_VERSION_MINOR_MASK 0x0fff0000 ++#define NAND_VERSION_MINOR_SHIFT 16 ++ ++/* NAND OP_CMDs */ ++#define PAGE_READ 0x2 ++#define PAGE_READ_WITH_ECC 0x3 ++#define PAGE_READ_WITH_ECC_SPARE 0x4 ++#define PROGRAM_PAGE 0x6 ++#define PAGE_PROGRAM_WITH_ECC 0x7 ++#define PROGRAM_PAGE_SPARE 0x9 ++#define BLOCK_ERASE 0xa ++#define FETCH_ID 0xb ++#define RESET_DEVICE 0xd ++ ++/* ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks. ++ * the driver calls the chunks 'step' or 'codeword' interchangeably ++ */ ++#define NANDC_STEP_SIZE 512 ++ ++/* ++ * the largest page size we support is 8K, this will have 16 steps/codewords ++ * of 512 bytes each ++ */ ++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) ++ ++/* we read at most 3 registers per codeword scan */ ++#define MAX_REG_RD (3 * MAX_NUM_STEPS) ++ ++/* ECC modes */ ++#define ECC_NONE BIT(0) ++#define ECC_RS_4BIT BIT(1) ++#define ECC_BCH_4BIT BIT(2) ++#define ECC_BCH_8BIT BIT(3) ++ ++struct desc_info { ++ struct list_head list; ++ ++ enum dma_transfer_direction dir; ++ struct scatterlist sgl; ++ struct dma_async_tx_descriptor *dma_desc; ++}; ++ ++/* ++ * holds the current register values that we want to write. acts as a contiguous ++ * chunk of memory which we use to write the controller registers through DMA. ++ */ ++struct nandc_regs { ++ u32 cmd; ++ u32 addr0; ++ u32 addr1; ++ u32 chip_sel; ++ u32 exec; ++ ++ u32 cfg0; ++ u32 cfg1; ++ u32 ecc_bch_cfg; ++ ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ ++ u32 cmd1; ++ u32 vld; ++ ++ u32 orig_cmd1; ++ u32 orig_vld; ++ ++ u32 ecc_buf_cfg; ++}; ++ ++/* ++ * @cmd_crci: ADM DMA CRCI for command flow control ++ * @data_crci: ADM DMA CRCI for data flow control ++ * @list: DMA descriptor list (list of desc_infos) ++ * @dma_done: completion param to denote end of last ++ * descriptor in the list ++ * @data_buffer: our local DMA buffer for page read/writes, ++ * used when we can't use the buffer provided ++ * by upper layers directly ++ * @buf_size/count/start: markers for chip->read_buf/write_buf functions ++ * @reg_read_buf: buffer for reading register data via DMA ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for ++ * ecc/non-ecc mode for the current nand flash ++ * device ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes ++ * @ecc_strength: 4 bit or 8 bit ecc, received via DT ++ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT ++ * @ecc_modes: supported ECC modes by the current controller, ++ * initialized via DT match data ++ * @cw_size: the number of bytes in a single step/codeword ++ * of a page, consisting of all data, ecc, spare ++ * and reserved bytes ++ * @cw_data: the number of bytes within a codeword protected ++ * by ECC ++ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used ++ * @status: value to be returned if NAND_CMD_STATUS command ++ * is executed ++ */ ++struct qcom_nandc_data { ++ struct platform_device *pdev; ++ struct device *dev; ++ ++ void __iomem *base; ++ struct resource *res; ++ ++ struct clk *core_clk; ++ struct clk *aon_clk; ++ ++ /* DMA stuff */ ++ struct dma_chan *chan; ++ struct dma_slave_config slave_conf; ++ unsigned int cmd_crci; ++ unsigned int data_crci; ++ struct list_head list; ++ struct completion dma_done; ++ ++ /* MTD stuff */ ++ struct nand_chip chip; ++ struct mtd_info mtd; ++ ++ /* local data buffer and markers */ ++ u8 *data_buffer; ++ int buf_size; ++ int buf_count; ++ int buf_start; ++ ++ /* local buffer to read back registers */ ++ u32 *reg_read_buf; ++ int reg_read_pos; ++ ++ /* required configs */ ++ u32 cfg0, cfg1; ++ u32 cfg0_raw, cfg1_raw; ++ u32 ecc_buf_cfg; ++ u32 ecc_bch_cfg; ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ u32 sflashc_burst_cfg; ++ u32 cmd1, vld; ++ ++ /* register state */ ++ struct nandc_regs *regs; ++ ++ /* things we get from DT */ ++ int ecc_strength; ++ int bus_width; ++ ++ u32 ecc_modes; ++ ++ /* misc params */ ++ int cw_size; ++ int cw_data; ++ bool use_ecc; ++ bool bch_enabled; ++ u8 status; ++ int last_command; ++}; ++ ++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset) ++{ ++ return ioread32(this->base + offset); ++} ++ ++static inline void nandc_write(struct qcom_nandc_data *this, int offset, ++ u32 val) ++{ ++ iowrite32(val, this->base + offset); ++} ++ ++/* helper to configure address register values */ ++static void set_address(struct qcom_nandc_data *this, u16 column, int page) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nandc_regs *regs = this->regs; ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ ++ regs->addr0 = page << 16 | column; ++ regs->addr1 = page >> 16 & 0xff; ++} ++ ++/* ++ * update_rw_regs: set up read/write register values, these will be ++ * written to the NAND controller registers via DMA ++ * ++ * @num_cw: number of steps for the read/write operation ++ * @read: read or write operation ++ */ ++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ if (read) { ++ if (this->use_ecc) ++ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; ++ else ++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; ++ } else { ++ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; ++ } ++ ++ if (this->use_ecc) { ++ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ regs->cfg1 = this->cfg1; ++ regs->ecc_bch_cfg = this->ecc_bch_cfg; ++ } else { ++ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ regs->cfg1 = this->cfg1_raw; ++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ } ++ ++ regs->ecc_buf_cfg = this->ecc_buf_cfg; ++ regs->clrflashstatus = this->clrflashstatus; ++ regs->clrreadstatus = this->clrreadstatus; ++ regs->exec = 1; ++} ++ ++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off, ++ const void *vaddr, int size, bool flow_control) ++{ ++ struct desc_info *desc; ++ struct dma_async_tx_descriptor *dma_desc; ++ struct scatterlist *sgl; ++ struct dma_slave_config slave_conf; ++ int r; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ list_add_tail(&desc->list, &this->list); ++ ++ sgl = &desc->sgl; ++ ++ sg_init_one(sgl, vaddr, size); ++ ++ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; ++ ++ r = dma_map_sg(this->dev, sgl, 1, desc->dir); ++ if (r == 0) { ++ r = -ENOMEM; ++ goto err; ++ } ++ ++ memset(&slave_conf, 0x00, sizeof(slave_conf)); ++ ++ slave_conf.device_fc = flow_control; ++ if (read) { ++ slave_conf.src_maxburst = 16; ++ slave_conf.src_addr = this->res->start + reg_off; ++ slave_conf.slave_id = this->data_crci; ++ } else { ++ slave_conf.dst_maxburst = 16; ++ slave_conf.dst_addr = this->res->start + reg_off; ++ slave_conf.slave_id = this->cmd_crci; ++ } ++ ++ r = dmaengine_slave_config(this->chan, &slave_conf); ++ if (r) { ++ dev_err(this->dev, "failed to configure dma channel\n"); ++ goto err; ++ } ++ ++ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0); ++ if (!dma_desc) { ++ dev_err(this->dev, "failed to prepare desc\n"); ++ r = -EINVAL; ++ goto err; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ return 0; ++err: ++ kfree(desc); ++ ++ return r; ++} ++ ++/* ++ * read_reg_dma: prepares a descriptor to read a given number of ++ * contiguous registers to the reg_read_buf pointer ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to read ++ */ ++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) ++{ ++ bool flow_control = false; ++ void *vaddr; ++ int size; ++ ++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) ++ flow_control = true; ++ ++ size = num_regs * sizeof(u32); ++ vaddr = this->reg_read_buf + this->reg_read_pos; ++ this->reg_read_pos += num_regs; ++ ++ return prep_dma_desc(this, true, first, vaddr, size, flow_control); ++} ++ ++/* ++ * write_reg_dma: prepares a descriptor to write a given number of ++ * contiguous registers ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to write ++ */ ++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) ++{ ++ bool flow_control = false; ++ struct nandc_regs *regs = this->regs; ++ void *vaddr; ++ int size; ++ ++ switch (first) { ++ case NAND_FLASH_CMD: ++ vaddr = ®s->cmd; ++ flow_control = true; ++ break; ++ case NAND_EXEC_CMD: ++ vaddr = ®s->exec; ++ break; ++ case NAND_FLASH_STATUS: ++ vaddr = ®s->clrflashstatus; ++ break; ++ case NAND_DEV0_CFG0: ++ vaddr = ®s->cfg0; ++ break; ++ case NAND_READ_STATUS: ++ vaddr = ®s->clrreadstatus; ++ break; ++ case NAND_DEV_CMD1: ++ vaddr = ®s->cmd1; ++ break; ++ case NAND_DEV_CMD1_RESTORE: ++ first = NAND_DEV_CMD1; ++ vaddr = ®s->orig_cmd1; ++ break; ++ case NAND_DEV_CMD_VLD: ++ vaddr = ®s->vld; ++ break; ++ case NAND_DEV_CMD_VLD_RESTORE: ++ first = NAND_DEV_CMD_VLD; ++ vaddr = ®s->orig_vld; ++ break; ++ case NAND_EBI2_ECC_BUF_CFG: ++ vaddr = ®s->ecc_buf_cfg; ++ break; ++ default: ++ dev_err(this->dev, "invalid starting register\n"); ++ return -EINVAL; ++ } ++ ++ size = num_regs * sizeof(u32); ++ ++ return prep_dma_desc(this, false, first, vaddr, size, flow_control); ++} ++ ++/* ++ * read_data_dma: prepares a DMA descriptor to transfer data from the ++ * controller's internal buffer to the buffer 'vaddr' ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ */ ++static int read_data_dma(struct qcom_nandc_data *this, int reg_off, ++ const u8 *vaddr, int size) ++{ ++ return prep_dma_desc(this, true, reg_off, vaddr, size, false); ++} ++ ++/* ++ * write_data_dma: prepares a DMA descriptor to transfer data from ++ * 'vaddr' to the controller's internal buffer ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to read from ++ * @size: DMA transaction size in bytes ++ */ ++static int write_data_dma(struct qcom_nandc_data *this, int reg_off, ++ const u8 *vaddr, int size) ++{ ++ return prep_dma_desc(this, false, reg_off, vaddr, size, false); ++} ++ ++/* ++ * helper to prepare dma descriptors to configure registers needed for reading a ++ * codeword/step in a page ++ */ ++static void config_cw_read(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 3); ++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); ++ ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 2); ++ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1); ++} ++ ++/* ++ * helpers to prepare dma descriptors used to configure registers needed for ++ * writing a codeword/step in a page ++ */ ++static void config_cw_write_pre(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 3); ++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); ++} ++ ++static void config_cw_write_post(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ write_reg_dma(this, NAND_FLASH_STATUS, 1); ++ write_reg_dma(this, NAND_READ_STATUS, 1); ++} ++ ++/* ++ * the following functions are used within chip->cmdfunc() to perform different ++ * NAND_CMD_* commands ++ */ ++ ++/* sets up descriptors for NAND_CMD_PARAM */ ++static int nandc_param(struct qcom_nandc_data *this) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ /* ++ * NAND_CMD_PARAM is called before we know much about the FLASH chip ++ * in use. we configure the controller to perform a raw read of 512 ++ * bytes to read onfi params ++ */ ++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; ++ regs->addr0 = 0; ++ regs->addr1 = 0; ++ regs->cfg0 = 0 << CW_PER_PAGE ++ | 512 << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES; ++ ++ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | 0 << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE; ++ ++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ ++ /* configure CMD1 and VLD for ONFI param probing */ ++ regs->vld = (this->vld & ~(1 << READ_START_VLD)) ++ | 0 << READ_START_VLD; ++ ++ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR)) ++ | NAND_CMD_PARAM << READ_ADDR; ++ ++ regs->exec = 1; ++ ++ regs->orig_cmd1 = this->cmd1; ++ regs->orig_vld = this->vld; ++ ++ write_reg_dma(this, NAND_DEV_CMD_VLD, 1); ++ write_reg_dma(this, NAND_DEV_CMD1, 1); ++ ++ this->buf_count = 512; ++ memset(this->data_buffer, 0xff, this->buf_count); ++ ++ config_cw_read(this); ++ ++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count); ++ ++ /* restore CMD1 and VLD regs */ ++ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1); ++ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_ERASE1 */ ++static int erase_block(struct qcom_nandc_data *this, int page_addr) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE; ++ regs->addr0 = page_addr; ++ regs->addr1 = 0; ++ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE); ++ regs->cfg1 = this->cfg1_raw; ++ regs->exec = 1; ++ regs->clrflashstatus = this->clrflashstatus; ++ regs->clrreadstatus = this->clrreadstatus; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 2); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ write_reg_dma(this, NAND_FLASH_STATUS, 1); ++ write_reg_dma(this, NAND_READ_STATUS, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_READID */ ++static int read_id(struct qcom_nandc_data *this, int column) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ if (column == -1) ++ return 0; ++ ++ regs->cmd = FETCH_ID; ++ regs->addr0 = column; ++ regs->addr1 = 0; ++ regs->chip_sel = DM_EN; ++ regs->exec = 1; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 4); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_READ_ID, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_RESET */ ++static int reset(struct qcom_nandc_data *this) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ regs->cmd = RESET_DEVICE; ++ regs->exec = 1; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 1); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ return 0; ++} ++ ++/* helpers to submit/free our list of dma descriptors */ ++static void dma_callback(void *param) ++{ ++ struct qcom_nandc_data *this = param; ++ struct completion *c = &this->dma_done; ++ ++ complete(c); ++} ++ ++static int submit_descs(struct qcom_nandc_data *this) ++{ ++ struct completion *c = &this->dma_done; ++ struct desc_info *desc; ++ int r; ++ ++ init_completion(c); ++ ++ list_for_each_entry(desc, &this->list, list) { ++ /* ++ * we add a callback to the last descriptor in our list to ++ * notify completion of command ++ */ ++ if (list_is_last(&desc->list, &this->list)) { ++ desc->dma_desc->callback = dma_callback; ++ desc->dma_desc->callback_param = this; ++ } ++ ++ dmaengine_submit(desc->dma_desc); ++ } ++ ++ dma_async_issue_pending(this->chan); ++ ++ r = wait_for_completion_timeout(c, msecs_to_jiffies(500)); ++ if (!r) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++static void free_descs(struct qcom_nandc_data *this) ++{ ++ struct desc_info *desc, *n; ++ ++ list_for_each_entry_safe(desc, n, &this->list, list) { ++ list_del(&desc->list); ++ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir); ++ kfree(desc); ++ } ++} ++ ++/* reset the register read buffer for next NAND operation */ ++static void clear_read_regs(struct qcom_nandc_data *this) ++{ ++ this->reg_read_pos = 0; ++ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf)); ++} ++ ++static void pre_command(struct qcom_nandc_data *this, int command) ++{ ++ this->buf_count = 0; ++ this->buf_start = 0; ++ this->use_ecc = false; ++ this->last_command = command; ++ ++ clear_read_regs(this); ++} ++ ++/* ++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our ++ * privately maintained status byte, this status byte can be read after ++ * NAND_CMD_STATUS is called ++ */ ++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int num_cw; ++ int i; ++ ++ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; ++ ++ for (i = 0; i < num_cw; i++) { ++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]); ++ ++ if (flash_status & FS_MPU_ERR) ++ this->status &= ~NAND_STATUS_WP; ++ ++ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && ++ (flash_status & FS_DEVICE_STS_ERR))) ++ this->status |= NAND_STATUS_FAIL; ++ } ++} ++ ++static void post_command(struct qcom_nandc_data *this, int command) ++{ ++ switch (command) { ++ case NAND_CMD_READID: ++ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count); ++ break; ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ parse_erase_write_errors(this, command); ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * Implements chip->cmdfunc. It's only used for a limited set of commands. ++ * The rest of the commands wouldn't be called by upper layers. For example, ++ * NAND_CMD_READOOB would never be called because we have our own versions ++ * of read_oob ops for nand_ecc_ctrl. ++ */ ++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ struct qcom_nandc_data *this = chip->priv; ++ bool wait = false; ++ int r = 0; ++ ++ pre_command(this, command); ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ r = reset(this); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READID: ++ this->buf_count = 4; ++ r = read_id(this, column); ++ wait = true; ++ break; ++ ++ case NAND_CMD_PARAM: ++ r = nandc_param(this); ++ wait = true; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ r = erase_block(this, page_addr); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READ0: ++ /* we read the entire page for now */ ++ WARN_ON(column != 0); ++ ++ this->use_ecc = true; ++ set_address(this, 0, page_addr); ++ update_rw_regs(this, ecc->steps, true); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ WARN_ON(column != 0); ++ set_address(this, 0, page_addr); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_NONE: ++ default: ++ break; ++ } ++ ++ if (r) { ++ dev_err(this->dev, "failure executing command %d\n", ++ command); ++ free_descs(this); ++ return; ++ } ++ ++ if (wait) { ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, ++ "failure submitting descs for command %d\n", ++ command); ++ } ++ ++ free_descs(this); ++ ++ post_command(this, command); ++} ++ ++/* ++ * when using RS ECC, the NAND controller flags an error when reading an ++ * erased page. however, there are special characters at certain offsets when ++ * we read the erased page. we check here if the page is really empty. if so, ++ * we replace the magic characters with 0xffs ++ */ ++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = ecc->steps; ++ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS]; ++ int i, j; ++ ++ /* if BCH is enabled, HW will take care of detecting erased pages */ ++ if (this->bch_enabled || !this->use_ecc) ++ return false; ++ ++ for (i = 0; i < cwperpage; i++) { ++ u8 *empty1, *empty2; ++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]); ++ ++ /* ++ * an erased page flags an error in NAND_FLASH_STATUS, check if ++ * the page is erased by looking for 0x54s at offsets 3 and 175 ++ * from the beginning of each codeword ++ */ ++ if (!(flash_status & FS_OP_ERR)) ++ break; ++ ++ empty1 = &data_buf[3 + i * this->cw_data]; ++ empty2 = &data_buf[175 + i * this->cw_data]; ++ ++ /* ++ * if the error wasn't because of an erased page, bail out and ++ * and let someone else do the error checking ++ */ ++ if ((*empty1 == 0x54 && *empty2 == 0xff) || ++ (*empty1 == 0xff && *empty2 == 0x54)) { ++ orig1[i] = *empty1; ++ orig2[i] = *empty2; ++ ++ *empty1 = 0xff; ++ *empty2 = 0xff; ++ } else { ++ break; ++ } ++ } ++ ++ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize)) ++ goto not_empty; ++ ++ /* ++ * tell the caller that the page was empty and is fixed up, so that ++ * parse_read_errors() doesn't think it's an error ++ */ ++ return true; ++ ++not_empty: ++ /* restore original values if not empty*/ ++ for (j = 0; j < i; j++) { ++ data_buf[3 + j * this->cw_data] = orig1[j]; ++ data_buf[175 + j * this->cw_data] = orig2[j]; ++ } ++ ++ return false; ++} ++ ++struct read_stats { ++ __le32 flash; ++ __le32 buffer; ++ __le32 erased_cw; ++}; ++ ++/* ++ * reads back status registers set by the controller to notify page read ++ * errors. this is equivalent to what 'ecc->correct()' would do. ++ */ ++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = ecc->steps; ++ unsigned int max_bitflips = 0; ++ int i; ++ ++ for (i = 0; i < cwperpage; i++) { ++ int stat; ++ struct read_stats *buf; ++ ++ buf = (struct read_stats *) (this->reg_read_buf + 3 * i); ++ ++ buf->flash = le32_to_cpu(buf->flash); ++ buf->buffer = le32_to_cpu(buf->buffer); ++ buf->erased_cw = le32_to_cpu(buf->erased_cw); ++ ++ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) { ++ ++ /* ignore erased codeword errors */ ++ if (this->bch_enabled) { ++ if ((buf->erased_cw & ERASED_CW) == ERASED_CW) ++ continue; ++ } else if (erased_page) { ++ continue; ++ } ++ ++ if (buf->buffer & BS_UNCORRECTABLE_BIT) { ++ mtd->ecc_stats.failed++; ++ continue; ++ } ++ } ++ ++ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK; ++ mtd->ecc_stats.corrected += stat; ++ ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ ++ return max_bitflips; ++} ++ ++/* ++ * helper to perform the actual page read operation, used by ecc->read_page() ++ * and ecc->read_oob() ++ */ ++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf, ++ u8 *oob_buf) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int i, r; ++ ++ /* queue cmd descs for each codeword */ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ } else { ++ data_size = this->cw_data; ++ oob_size = ecc->bytes; ++ } ++ ++ config_cw_read(this); ++ ++ if (data_buf) ++ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); ++ ++ if (oob_buf) ++ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf, ++ oob_size); ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failure to read page/oob\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* ++ * a helper that copies the last step/codeword of a page (containing free oob) ++ * into our local buffer ++ */ ++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int size; ++ int r; ++ ++ clear_read_regs(this); ++ ++ size = use_ecc ? this->cw_data : this->cw_size; ++ ++ /* prepare a clean read buffer */ ++ memset(this->data_buffer, 0xff, size); ++ ++ this->use_ecc = use_ecc; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, true); ++ ++ config_cw_read(this); ++ ++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size); ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failed to copy last codeword\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* implements ecc->read_page() */ ++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ u8 *data_buf, *oob_buf = NULL; ++ bool erased_page; ++ int r; ++ ++ data_buf = buf; ++ oob_buf = oob_required ? chip->oob_poi : NULL; ++ ++ r = read_page_low(this, data_buf, oob_buf); ++ if (r) { ++ dev_err(this->dev, "failure to read page\n"); ++ return r; ++ } ++ ++ erased_page = empty_page_fixup(this, data_buf); ++ ++ return parse_read_errors(this, erased_page); ++} ++ ++/* implements ecc->read_oob() */ ++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int r; ++ ++ clear_read_regs(this); ++ ++ this->use_ecc = true; ++ set_address(this, 0, page); ++ update_rw_regs(this, ecc->steps, true); ++ ++ r = read_page_low(this, NULL, chip->oob_poi); ++ if (r) ++ dev_err(this->dev, "failure to read oob\n"); ++ ++ return r; ++} ++ ++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */ ++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int start, length; ++ int r; ++ ++ /* ++ * configure registers for a raw page read, the address is set to the ++ * beginning of the last codeword, we don't care about reading ecc ++ * portion of oob, just the free stuff ++ */ ++ r = copy_last_cw(this, false, page); ++ if (r) ++ return r; ++ ++ /* ++ * reading raw oob has 2 parts, first the bad block byte, then the ++ * actual free oob region. perform a memcpy in two steps ++ */ ++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); ++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; ++ ++ memcpy(oob, this->data_buffer + start, length); ++ ++ oob += length; ++ ++ start = this->cw_data - (ecc->steps << 2) + 1; ++ length = ecc->steps << 2; ++ ++ memcpy(oob, this->data_buffer + start, length); ++ ++ return 0; ++} ++ ++/* implements ecc->write_page() */ ++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ u8 *data_buf, *oob_buf; ++ int i, r = 0; ++ ++ clear_read_regs(this); ++ ++ data_buf = (u8 *) buf; ++ oob_buf = chip->oob_poi; ++ ++ this->use_ecc = true; ++ update_rw_regs(this, ecc->steps, false); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ } else { ++ data_size = this->cw_data; ++ oob_size = ecc->bytes; ++ } ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); ++ ++ /* ++ * we don't really need to write anything to oob for the ++ * first n - 1 codewords since these oob regions just ++ * contain ecc that's written by the controller itself ++ */ ++ if (i == (ecc->steps - 1)) ++ write_data_dma(this, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size); ++ config_cw_write_post(this); ++ ++ data_buf += data_size; ++ oob_buf += oob_size; ++ } ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failure to write page\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* ++ * implements ecc->write_oob() ++ * ++ * the NAND controller cannot write only data or only oob within a codeword, ++ * since ecc is calculated for the combined codeword. we first copy the ++ * entire contents for the last codeword(data + oob), replace the old oob ++ * with the new one in chip->oob_poi, and then write the entire codeword. ++ * this read-copy-write operation results in a slight perormance loss. ++ */ ++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int free_boff; ++ int data_size, oob_size; ++ int r, status = 0; ++ ++ r = copy_last_cw(this, true, page); ++ if (r) ++ return r; ++ ++ clear_read_regs(this); ++ ++ /* calculate the data and oob size for the last codeword/step */ ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ ++ /* ++ * the location of spare data in the oob buffer, we could also use ++ * ecc->layout.oobfree here ++ */ ++ free_boff = ecc->bytes * (ecc->steps - 1); ++ ++ /* override new oob content to last codeword */ ++ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size); ++ ++ this->use_ecc = true; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, false); ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, ++ data_size + oob_size); ++ config_cw_write_post(this); ++ ++ r = submit_descs(this); ++ ++ free_descs(this); ++ ++ if (r) { ++ dev_err(this->dev, "failure to write oob\n"); ++ return -EIO; ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/* implements ecc->write_oob_raw(), used to write bad block marker flag */ ++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int start, length; ++ int r, status = 0; ++ ++ r = copy_last_cw(this, false, page); ++ if (r) ++ return r; ++ ++ clear_read_regs(this); ++ ++ /* ++ * writing raw oob has 2 parts, first the bad block region, then the ++ * actual free region ++ */ ++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); ++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; ++ ++ memcpy(this->data_buffer + start, oob, length); ++ ++ oob += length; ++ ++ start = this->cw_data - (ecc->steps << 2) + 1; ++ length = ecc->steps << 2; ++ ++ memcpy(this->data_buffer + start, oob, length); ++ ++ /* prepare write */ ++ this->use_ecc = false; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, false); ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size); ++ config_cw_write_post(this); ++ ++ r = submit_descs(this); ++ ++ free_descs(this); ++ ++ if (r) { ++ dev_err(this->dev, "failure to write updated oob\n"); ++ return -EIO; ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/* ++ * the three functions below implement chip->read_byte(), chip->read_buf() ++ * and chip->write_buf() respectively. these aren't used for ++ * reading/writing page data, they are used for smaller data like reading ++ * id, status etc ++ */ ++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ uint8_t *buf = this->data_buffer; ++ uint8_t ret = 0x0; ++ ++ if (this->last_command == NAND_CMD_STATUS) { ++ ret = this->status; ++ ++ this->status = NAND_STATUS_READY | NAND_STATUS_WP; ++ ++ return ret; ++ } ++ ++ if (this->buf_start < this->buf_count) ++ ret = buf[this->buf_start++]; ++ ++ return ret; ++} ++ ++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); ++ ++ memcpy(buf, this->data_buffer + this->buf_start, real_len); ++ this->buf_start += real_len; ++} ++ ++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); ++ ++ memcpy(this->data_buffer + this->buf_start, buf, real_len); ++ ++ this->buf_start += real_len; ++} ++ ++/* we support only one external chip for now */ ++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ ++ if (chipnr <= 0) ++ return; ++ ++ dev_warn(this->dev, "invalid chip select\n"); ++} ++ ++/* ++ * NAND controller page layout info ++ * ++ * |-----------------------| |---------------------------------| ++ * | xx.......xx| | *********xx.......xx| ++ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx| ++ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx| ++ * | xx.......xx| | *********xx.......xx| ++ * |-----------------------| |---------------------------------| ++ * codeword 1,2..n-1 codeword n ++ * <---(528/532 Bytes)----> <-------(528/532 Bytes)----------> ++ * ++ * n = number of codewords in the page ++ * . = ECC bytes ++ * * = spare bytes ++ * x = unused/reserved bytes ++ * ++ * 2K page: n = 4, spare = 16 bytes ++ * 4K page: n = 8, spare = 32 bytes ++ * 8K page: n = 16, spare = 64 bytes ++ * ++ * the qcom nand controller operates at a sub page/codeword level. each ++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. ++ * the number of ECC bytes vary based on the ECC strength and the bus width. ++ * ++ * the first n - 1 codewords contains 516 bytes of user data, the remaining ++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains ++ * both user data and spare(oobavail) bytes that sum up to 516 bytes. ++ * ++ * the layout described above is used by the controller when the ECC block is ++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are ++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout ++ * layouts defined below doesn't consider the positions occupied by the reserved ++ * bytes ++ * ++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width) ++ * in the last codeword is the position of bad block marker. the bad block ++ * marker cannot be accessed when ECC is enabled. ++ * ++ */ ++ ++/* ++ * Layouts for different page sizes and ecc modes. We skip the eccpos field ++ * since it isn't needed for this driver ++ */ ++ ++/* 2K page, 4 bit ECC */ ++static struct nand_ecclayout layout_oob_64 = { ++ .eccbytes = 40, ++ .oobfree = { ++ { 30, 16 }, ++ }, ++}; ++ ++/* 4K page, 4 bit ECC, 8/16 bit bus width */ ++static struct nand_ecclayout layout_oob_128 = { ++ .eccbytes = 80, ++ .oobfree = { ++ { 70, 32 }, ++ }, ++}; ++ ++/* 4K page, 8 bit ECC, 8 bit bus width */ ++static struct nand_ecclayout layout_oob_224_x8 = { ++ .eccbytes = 104, ++ .oobfree = { ++ { 91, 32 }, ++ }, ++}; ++ ++/* 4K page, 8 bit ECC, 16 bit bus width */ ++static struct nand_ecclayout layout_oob_224_x16 = { ++ .eccbytes = 112, ++ .oobfree = { ++ { 98, 32 }, ++ }, ++}; ++ ++/* 8K page, 4 bit ECC, 8/16 bit bus width */ ++static struct nand_ecclayout layout_oob_256 = { ++ .eccbytes = 160, ++ .oobfree = { ++ { 151, 64 }, ++ }, ++}; ++ ++/* ++ * this is called before scan_ident, we do some minimal configurations so ++ * that reading ID and ONFI params work ++ */ ++static void qcom_nandc_pre_init(struct qcom_nandc_data *this) ++{ ++ /* kill onenand */ ++ nandc_write(this, SFLASHC_BURST_CFG, 0); ++ ++ /* enable ADM DMA */ ++ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN); ++ ++ /* save the original values of these registers */ ++ this->cmd1 = nandc_read(this, NAND_DEV_CMD1); ++ this->vld = nandc_read(this, NAND_DEV_CMD_VLD); ++ ++ /* initial status value */ ++ this->status = NAND_STATUS_READY | NAND_STATUS_WP; ++} ++ ++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage; ++ bool wide_bus; ++ ++ /* the nand controller fetches codewords/chunks of 512 bytes */ ++ cwperpage = mtd->writesize >> 9; ++ ++ ecc->strength = this->ecc_strength; ++ ++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; ++ ++ if (ecc->strength >= 8) { ++ /* 8 bit ECC defaults to BCH ECC on all platforms */ ++ ecc->bytes = wide_bus ? 14 : 13; ++ } else { ++ /* ++ * if the controller supports BCH for 4 bit ECC, the controller ++ * uses lesser bytes for ECC. If RS is used, the ECC bytes is ++ * always 10 bytes ++ */ ++ if (this->ecc_modes & ECC_BCH_4BIT) ++ ecc->bytes = wide_bus ? 8 : 7; ++ else ++ ecc->bytes = 10; ++ } ++ ++ /* each step consists of 512 bytes of data */ ++ ecc->size = NANDC_STEP_SIZE; ++ ++ ecc->read_page = qcom_nandc_read_page; ++ ecc->read_oob = qcom_nandc_read_oob; ++ ecc->write_page = qcom_nandc_write_page; ++ ecc->write_oob = qcom_nandc_write_oob; ++ ++ /* ++ * the bad block marker is readable only when we read the page with ECC ++ * disabled. all the ops above run with ECC enabled. We need raw read ++ * and write function for oob in order to access bad block marker. ++ */ ++ ecc->read_oob_raw = qcom_nandc_read_oob_raw; ++ ecc->write_oob_raw = qcom_nandc_write_oob_raw; ++ ++ switch (mtd->oobsize) { ++ case 64: ++ ecc->layout = &layout_oob_64; ++ break; ++ case 128: ++ ecc->layout = &layout_oob_128; ++ break; ++ case 224: ++ if (wide_bus) ++ ecc->layout = &layout_oob_224_x16; ++ else ++ ecc->layout = &layout_oob_224_x8; ++ break; ++ case 256: ++ ecc->layout = &layout_oob_256; ++ break; ++ default: ++ dev_err(this->dev, "unsupported NAND device, oobsize %d\n", ++ mtd->oobsize); ++ return -ENODEV; ++ } ++ ++ ecc->mode = NAND_ECC_HW; ++ ++ /* enable ecc by default */ ++ this->use_ecc = true; ++ ++ return 0; ++} ++ ++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = mtd->writesize / ecc->size; ++ int spare_bytes, bad_block_byte; ++ bool wide_bus; ++ int ecc_mode = 0; ++ ++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; ++ ++ if (ecc->strength >= 8) { ++ this->cw_size = 532; ++ ++ spare_bytes = wide_bus ? 0 : 2; ++ ++ this->bch_enabled = true; ++ ecc_mode = 1; ++ } else { ++ this->cw_size = 528; ++ ++ if (this->ecc_modes & ECC_BCH_4BIT) { ++ spare_bytes = wide_bus ? 2 : 4; ++ ++ this->bch_enabled = true; ++ ecc_mode = 0; ++ } else { ++ spare_bytes = wide_bus ? 0 : 1; ++ } ++ } ++ ++ /* ++ * DATA_UD_BYTES varies based on whether the read/write command protects ++ * spare data with ECC too. We protect spare data by default, so we set ++ * it to main + spare data, which are 512 and 4 bytes respectively. ++ */ ++ this->cw_data = 516; ++ ++ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1; ++ ++ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE ++ | this->cw_data << UD_SIZE_BYTES ++ | 0 << DISABLE_STATUS_AFTER_WRITE ++ | 5 << NUM_ADDR_CYCLES ++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS ++ | 0 << STATUS_BFR_READ ++ | 1 << SET_RD_MODE_AFTER_STATUS ++ | spare_bytes << SPARE_SIZE_BYTES; ++ ++ this->cfg1 = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | bad_block_byte << BAD_BLOCK_BYTE_NUM ++ | 0 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | this->bch_enabled << ENABLE_BCH_ECC; ++ ++ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE ++ | this->cw_size << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES; ++ ++ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE; ++ ++ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE ++ | 0 << ECC_SW_RESET ++ | this->cw_data << ECC_NUM_DATA_BYTES ++ | 1 << ECC_FORCE_CLK_OPEN ++ | ecc_mode << ECC_MODE ++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH; ++ ++ this->ecc_buf_cfg = 0x203 << NUM_STEPS; ++ ++ this->clrflashstatus = FS_READY_BSY_N; ++ this->clrreadstatus = 0xc0; ++ ++ dev_dbg(this->dev, ++ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", ++ this->cfg0, this->cfg1, this->ecc_buf_cfg, ++ this->ecc_bch_cfg, this->cw_size, this->cw_data, ++ ecc->strength, ecc->bytes, cwperpage); ++} ++ ++static int qcom_nandc_alloc(struct qcom_nandc_data *this) ++{ ++ int r; ++ ++ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32)); ++ if (r) { ++ dev_err(this->dev, "failed to set DMA mask\n"); ++ return r; ++ } ++ ++ /* ++ * we use the internal buffer for reading ONFI params, reading small ++ * data like ID and status, and preforming read-copy-write operations ++ * when writing to a codeword partially. 532 is the maximum possible ++ * size of a codeword for our nand controller ++ */ ++ this->buf_size = 532; ++ ++ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL); ++ if (!this->data_buffer) ++ return -ENOMEM; ++ ++ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL); ++ if (!this->regs) ++ return -ENOMEM; ++ ++ this->reg_read_buf = devm_kzalloc(this->dev, ++ MAX_REG_RD * sizeof(*this->reg_read_buf), ++ GFP_KERNEL); ++ if (!this->reg_read_buf) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&this->list); ++ ++ this->chan = dma_request_slave_channel(this->dev, "rxtx"); ++ if (!this->chan) { ++ dev_err(this->dev, "failed to request slave channel\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void qcom_nandc_unalloc(struct qcom_nandc_data *this) ++{ ++ dma_release_channel(this->chan); ++} ++ ++static int qcom_nandc_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct device_node *np = this->dev->of_node; ++ struct mtd_part_parser_data ppdata = { .of_node = np }; ++ int r; ++ ++ mtd->priv = chip; ++ mtd->name = "qcom-nandc"; ++ mtd->owner = THIS_MODULE; ++ ++ chip->priv = this; ++ ++ chip->cmdfunc = qcom_nandc_command; ++ chip->select_chip = qcom_nandc_select_chip; ++ chip->read_byte = qcom_nandc_read_byte; ++ chip->read_buf = qcom_nandc_read_buf; ++ chip->write_buf = qcom_nandc_write_buf; ++ ++ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; ++ if (this->bus_width == 16) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW; ++ if (of_get_nand_on_flash_bbt(np)) ++ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; ++ ++ qcom_nandc_pre_init(this); ++ ++ r = nand_scan_ident(mtd, 1, NULL); ++ if (r) ++ return r; ++ ++ r = qcom_nandc_ecc_init(this); ++ if (r) ++ return r; ++ ++ qcom_nandc_hw_post_init(this); ++ ++ r = nand_scan_tail(mtd); ++ if (r) ++ return r; ++ ++ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); ++} ++ ++static int qcom_nandc_parse_dt(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this = platform_get_drvdata(pdev); ++ struct device_node *np = this->dev->of_node; ++ int r; ++ ++ this->ecc_strength = of_get_nand_ecc_strength(np); ++ if (this->ecc_strength < 0) { ++ dev_warn(this->dev, ++ "incorrect ecc strength, setting to 4 bits/step\n"); ++ this->ecc_strength = 4; ++ } ++ ++ this->bus_width = of_get_nand_bus_width(np); ++ if (this->bus_width < 0) { ++ dev_warn(this->dev, "incorrect bus width, setting to 8\n"); ++ this->bus_width = 8; ++ } ++ ++ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci); ++ if (r) { ++ dev_err(this->dev, "command CRCI unspecified\n"); ++ return r; ++ } ++ ++ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci); ++ if (r) { ++ dev_err(this->dev, "data CRCI unspecified\n"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++static int qcom_nandc_probe(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this; ++ const struct of_device_id *match; ++ int r; ++ ++ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); ++ if (!this) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, this); ++ ++ this->pdev = pdev; ++ this->dev = &pdev->dev; ++ ++ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); ++ if (!match) { ++ dev_err(&pdev->dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ if (!match->data) { ++ dev_err(&pdev->dev, "failed to get device data\n"); ++ return -ENODEV; ++ } ++ ++ this->ecc_modes = (u32) match->data; ++ ++ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ this->base = devm_ioremap_resource(&pdev->dev, this->res); ++ if (IS_ERR(this->base)) ++ return PTR_ERR(this->base); ++ ++ this->core_clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(this->core_clk)) ++ return PTR_ERR(this->core_clk); ++ ++ this->aon_clk = devm_clk_get(&pdev->dev, "aon"); ++ if (IS_ERR(this->aon_clk)) ++ return PTR_ERR(this->aon_clk); ++ ++ r = qcom_nandc_parse_dt(pdev); ++ if (r) ++ return r; ++ ++ r = qcom_nandc_alloc(this); ++ if (r) ++ return r; ++ ++ r = clk_prepare_enable(this->core_clk); ++ if (r) ++ goto err_core_clk; ++ ++ r = clk_prepare_enable(this->aon_clk); ++ if (r) ++ goto err_aon_clk; ++ ++ r = qcom_nandc_init(this); ++ if (r) ++ goto err_init; ++ ++ return 0; ++ ++err_init: ++ clk_disable_unprepare(this->aon_clk); ++err_aon_clk: ++ clk_disable_unprepare(this->core_clk); ++err_core_clk: ++ qcom_nandc_unalloc(this); ++ ++ return r; ++} ++ ++static int qcom_nandc_remove(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this = platform_get_drvdata(pdev); ++ ++ qcom_nandc_unalloc(this); ++ ++ clk_disable_unprepare(this->aon_clk); ++ clk_disable_unprepare(this->core_clk); ++ ++ return 0; ++} ++ ++#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) ++ ++/* ++ * data will hold a struct pointer containing more differences once we support ++ * more IPs ++ */ ++static const struct of_device_id qcom_nandc_of_match[] = { ++ { .compatible = "qcom,ebi2-nandc", ++ .data = (void *) EBI2_NANDC_ECC_MODES, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); ++ ++static struct platform_driver qcom_nandc_driver = { ++ .driver = { ++ .name = "qcom-nandc", ++ .of_match_table = qcom_nandc_of_match, ++ }, ++ .probe = qcom_nandc_probe, ++ .remove = qcom_nandc_remove, ++}; ++module_platform_driver(qcom_nandc_driver); ++ ++MODULE_AUTHOR("Archit Taneja "); ++MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += + obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o + obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o + obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ ++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o + + nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch new file mode 100644 index 000000000..6530eb1c8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch @@ -0,0 +1,82 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings +From: Archit Taneja +X-Patchwork-Id: 6927141 +Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:16 +0530 + +Add DT bindings document for the Qualcomm NAND controller driver. + +Cc: devicetree@vger.kernel.org + +v3: +- Don't use '0x' when specifying nand controller address space +- Add optional property for on-flash bbt usage + +Acked-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++ + 1 file changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +@@ -0,0 +1,49 @@ ++* Qualcomm NAND controller ++ ++Required properties: ++- compatible: should be "qcom,ebi2-nand" for IPQ806x ++- reg: MMIO address range ++- clocks: must contain core clock and always on clock ++- clock-names: must contain "core" for the core clock and "aon" for the ++ always on clock ++- dmas: DMA specifier, consisting of a phandle to the ADM DMA ++ controller node and the channel number to be used for ++ NAND. Refer to dma.txt and qcom_adm.txt for more details ++- dma-names: must be "rxtx" ++- qcom,cmd-crci: must contain the ADM command type CRCI block instance ++ number specified for the NAND controller on the given ++ platform ++- qcom,data-crci: must contain the ADM data type CRCI block instance ++ number specified for the NAND controller on the given ++ platform ++ ++Optional properties: ++- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen ++ as default ++ ++- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8 ++ bits. If not present, 4 is chosen as default ++- nand-on-flash-bbt: Create/use on-flash bad block table ++ ++The device tree may optionally contain sub-nodes describing partitions of the ++address space. See partition.txt for more detail. ++ ++Example: ++ ++nand@1ac00000 { ++ compatible = "qcom,ebi2-nandc"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc EBI2_CLK>, ++ <&gcc EBI2_AON_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&adm_dma 3>; ++ dma-names = "rxtx"; ++ qcom,cmd-crci = <15>; ++ qcom,data-crci = <3>; ++ ++ partition@0 { ++ ... ++ }; ++}; diff --git a/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch new file mode 100644 index 000000000..664e7b5b5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch @@ -0,0 +1,51 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x +From: Archit Taneja +X-Patchwork-Id: 6927121 +Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:17 +0530 + +The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding +compatible string. + +Cc: devicetree@vger.kernel.org + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -741,6 +741,22 @@ + + status = "disabled"; + }; ++ ++ nand@1ac00000 { ++ compatible = "qcom,ebi2-nandc"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc EBI2_CLK>, ++ <&gcc EBI2_AON_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&adm_dma 3>; ++ dma-names = "rxtx"; ++ qcom,cmd-crci = <15>; ++ qcom,data-crci = <3>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch new file mode 100644 index 000000000..9de3d8fe8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch @@ -0,0 +1,76 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform +From: Archit Taneja +X-Patchwork-Id: 6927091 +Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:18 +0530 + +Enable the NAND controller node on the AP148 platform. Provide pinmux +information. + +Cc: devicetree@vger.kernel.org + +Signed-off-by: Archit Taneja + +--- +arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -43,6 +43,28 @@ + bias-none; + }; + }; ++ nand_pins: nand_pins { ++ mux { ++ pins = "gpio34", "gpio35", "gpio36", ++ "gpio37", "gpio38", "gpio39", ++ "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-disable; ++ }; ++ pullups { ++ pins = "gpio39"; ++ bias-pull-up; ++ }; ++ hold { ++ pins = "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ bias-bus-hold; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -126,5 +148,19 @@ + status = "ok"; + phy-tx0-term-offset = <7>; + }; ++ ++ nand@1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ ++ nand-ecc-strength = <4>; ++ nand-bus-width = <8>; ++ }; + }; + }; ++ ++&adm_dma { ++ status = "ok"; ++}; diff --git a/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch new file mode 100644 index 000000000..706028259 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -157,6 +157,8 @@ + + nand-ecc-strength = <4>; + nand-bus-width = <8>; ++ ++ linux,part-probe = "qcom-smem"; + }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch b/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch new file mode 100644 index 000000000..8da70340d --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/167-ARM-qcom_rpm_fix_support_for_smb208.patch @@ -0,0 +1,50 @@ + +In commit "regulator: qcom: Rework to single platform device" the smb208 regulator +used in IPQ8064 was left out. + +Add it to that new framework and update Docs accordingly. + +Signed-off-by: Adrian Panella + +--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt ++++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +@@ -59,6 +59,7 @@ Regulator nodes are identified by their + "qcom,rpm-pm8058-regulators" + "qcom,rpm-pm8901-regulators" + "qcom,rpm-pm8921-regulators" ++ "qcom,rpm-smb208-regulators" + + - vdd_l0_l1_lvs-supply: + - vdd_l2_l11_l12-supply: +@@ -156,6 +157,9 @@ pm8921: + l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch, + ncp + ++smb208: ++ s1a, s1b, s2a, s2b ++ + The content of each sub-node is defined by the standard binding for regulators - + see regulator.txt - with additional custom properties described below: + +--- a/drivers/regulator/qcom_rpm-regulator.c ++++ b/drivers/regulator/qcom_rpm-regulator.c +@@ -869,10 +869,19 @@ static const struct rpm_regulator_data r + { } + }; + ++static const struct rpm_regulator_data rpm_smb208_regulators[] = { ++ { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" }, ++ { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" }, ++ { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" }, ++ { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" }, ++ { } ++}; ++ + static const struct of_device_id rpm_of_match[] = { + { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, + { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, + { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, ++ { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators }, + { } + }; + MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch b/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch new file mode 100644 index 000000000..1a15a8c64 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/168-ARM-qcom-add-smb208-DT.patch @@ -0,0 +1,74 @@ +Change DT to use new smb208 regulator driver. + +Signed-off-by: Adrian Panella + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -162,45 +162,37 @@ + #address-cells = <1>; + #size-cells = <0>; + +- smb208_s1a: smb208-s1a { +- compatible = "qcom,rpm-smb208"; +- reg = ; ++ regulators { ++ compatible = "qcom,rpm-smb208-regulators"; + +- regulator-min-microvolt = <1050000>; +- regulator-max-microvolt = <1150000>; ++ smb208_s1a: s1a { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; + +- qcom,switch-mode-frequency = <1200000>; ++ qcom,switch-mode-frequency = <1200000>; + +- }; +- +- smb208_s1b: smb208-s1b { +- compatible = "qcom,rpm-smb208"; +- reg = ; +- +- regulator-min-microvolt = <1050000>; +- regulator-max-microvolt = <1150000>; ++ }; + +- qcom,switch-mode-frequency = <1200000>; +- }; +- +- smb208_s2a: smb208-s2a { +- compatible = "qcom,rpm-smb208"; +- reg = ; ++ smb208_s1b: s1b { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; + +- regulator-min-microvolt = < 800000>; +- regulator-max-microvolt = <1250000>; ++ qcom,switch-mode-frequency = <1200000>; ++ }; + +- qcom,switch-mode-frequency = <1200000>; +- }; ++ smb208_s2a: s2a { ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; + +- smb208_s2b: smb208-s2b { +- compatible = "qcom,rpm-smb208"; +- reg = ; ++ qcom,switch-mode-frequency = <1200000>; ++ }; + +- regulator-min-microvolt = < 800000>; +- regulator-max-microvolt = <1250000>; ++ smb208_s2b: s2b { ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; + +- qcom,switch-mode-frequency = <1200000>; ++ qcom,switch-mode-frequency = <1200000>; ++ }; + }; + }; + diff --git a/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch b/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch new file mode 100644 index 000000000..8f0afb504 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/169-OPP-Support-adjusting-OPP-voltages-at-runtime.patch @@ -0,0 +1,145 @@ +From 111139c943a082364fbbcd9e0cc94cd442481340 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 1 Jun 2015 18:47:56 -0700 +Subject: PM / OPP: Support adjusting OPP voltages at runtime + +On some SoCs the Adaptive Voltage Scaling (AVS) technique is +employed to optimize the operating voltage of a device. At a +given frequency, the hardware monitors dynamic factors and either +makes a suggestion for how much to adjust a voltage for the +current frequency, or it automatically adjusts the voltage +without software intervention. Add an API to the OPP library for +the former case, so that AVS type devices can update the voltages +for an OPP when the hardware determines the voltage should +change. The assumption is that drivers like CPUfreq or devfreq +will register for the OPP notifiers and adjust the voltage +according to suggestions that AVS makes. + +Cc: Nishanth Menon +Cc: Viresh Kumar +Signed-off-by: Stephen Boyd +Acked-by: Viresh Kumar +--- + drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++ + include/linux/pm_opp.h | 10 ++++++ + 2 files changed, 87 insertions(+) + +--- a/drivers/base/power/opp/core.c ++++ b/drivers/base/power/opp/core.c +@@ -1039,6 +1039,83 @@ unlock: + } + + /** ++ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an opp ++ * @dev: device for which we do this operation ++ * @freq: OPP frequency to adjust voltage of ++ * @u_volt: new OPP voltage ++ * ++ * Change the voltage of an OPP with an RCU operation. ++ * ++ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the ++ * copy operation, returns 0 if no modifcation was done OR modification was ++ * successful. ++ * ++ * Locking: The internal device_opp and opp structures are RCU protected. ++ * Hence this function internally uses RCU updater strategy with mutex locks to ++ * keep the integrity of the internal data structures. Callers should ensure ++ * that this function is *NOT* called under RCU protection or in contexts where ++ * mutex locking or synchronize_rcu() blocking calls cannot be used. ++ */ ++int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, ++ unsigned long u_volt) ++{ ++ struct device_opp *dev_opp; ++ struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); ++ int r = 0; ++ ++ /* keep the node allocated */ ++ new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); ++ if (!new_opp) ++ return -ENOMEM; ++ ++ mutex_lock(&dev_opp_list_lock); ++ ++ /* Find the device_opp */ ++ dev_opp = _find_device_opp(dev); ++ if (IS_ERR(dev_opp)) { ++ r = PTR_ERR(dev_opp); ++ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); ++ goto unlock; ++ } ++ ++ /* Do we have the frequency? */ ++ list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { ++ if (tmp_opp->rate == freq) { ++ opp = tmp_opp; ++ break; ++ } ++ } ++ if (IS_ERR(opp)) { ++ r = PTR_ERR(opp); ++ goto unlock; ++ } ++ ++ /* Is update really needed? */ ++ if (opp->u_volt == u_volt) ++ goto unlock; ++ /* copy the old data over */ ++ *new_opp = *opp; ++ ++ /* plug in new node */ ++ new_opp->u_volt = u_volt; ++ ++ list_replace_rcu(&opp->node, &new_opp->node); ++ mutex_unlock(&dev_opp_list_lock); ++ call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); ++ ++ /* Notify the change of the OPP */ ++ srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADJUST_VOLTAGE, ++ new_opp); ++ ++ return 0; ++ ++unlock: ++ mutex_unlock(&dev_opp_list_lock); ++ kfree(new_opp); ++ return r; ++} ++ ++/** + * dev_pm_opp_enable() - Enable a specific OPP + * @dev: device for which we do this operation + * @freq: OPP frequency to enable +--- a/include/linux/pm_opp.h ++++ b/include/linux/pm_opp.h +@@ -22,6 +22,7 @@ struct device; + + enum dev_pm_opp_event { + OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, ++ OPP_EVENT_ADJUST_VOLTAGE, + }; + + #if defined(CONFIG_PM_OPP) +@@ -50,6 +51,9 @@ int dev_pm_opp_add(struct device *dev, u + unsigned long u_volt); + void dev_pm_opp_remove(struct device *dev, unsigned long freq); + ++int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, ++ unsigned long u_volt); ++ + int dev_pm_opp_enable(struct device *dev, unsigned long freq); + + int dev_pm_opp_disable(struct device *dev, unsigned long freq); +@@ -114,6 +118,12 @@ static inline void dev_pm_opp_remove(str + { + } + ++static inline int dev_pm_opp_adjust_voltage(struct device *dev, ++ unsigned long freq, unsigned long u_volt) ++{ ++ return 0; ++} ++ + static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) + { + return 0; diff --git a/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch b/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch new file mode 100644 index 000000000..7be47a350 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/170-OPP-Allow-notifiers-to-call-dev_pm_opp_get_voltage-freq-RCU-free.patch @@ -0,0 +1,114 @@ +From d330eae026b4a73e77ca0422f5cae5207d80f738 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 1 Jun 2015 18:47:57 -0700 +Subject: OPP: Allow notifiers to call dev_pm_opp_get_{voltage, freq} RCU-free + +We pass the dev_pm_opp structure to OPP notifiers but the users +of the notifier need to surround calls to dev_pm_opp_get_*() with +RCU read locks to avoid lockdep warnings. The notifier is already +called with the dev_opp's srcu lock held, so it should be safe to +assume the devm_pm_opp structure is already protected inside the +notifier. Update the lockdep check for this. + +Cc: Krzysztof Kozlowski +Signed-off-by: Stephen Boyd +--- + drivers/base/power/opp/core.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +--- a/drivers/base/power/opp/core.c ++++ b/drivers/base/power/opp/core.c +@@ -31,9 +31,10 @@ static LIST_HEAD(dev_opp_list); + /* Lock to allow exclusive modification to the device and opp lists */ + DEFINE_MUTEX(dev_opp_list_lock); + +-#define opp_rcu_lockdep_assert() \ ++#define opp_rcu_lockdep_assert(s) \ + do { \ + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ ++ !(s && srcu_read_lock_held(s)) && \ + !lockdep_is_held(&dev_opp_list_lock), \ + "Missing rcu_read_lock() or " \ + "dev_opp_list_lock protection"); \ +@@ -91,7 +92,7 @@ struct device_opp *_find_device_opp(stru + { + struct device_opp *dev_opp; + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(NULL); + + if (IS_ERR_OR_NULL(dev)) { + pr_err("%s: Invalid parameters\n", __func__); +@@ -125,10 +126,11 @@ unsigned long dev_pm_opp_get_voltage(str + struct dev_pm_opp *tmp_opp; + unsigned long v = 0; + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); + +- tmp_opp = rcu_dereference(opp); +- if (IS_ERR_OR_NULL(tmp_opp)) ++ tmp_opp = srcu_dereference_check(opp, &opp->dev_opp->srcu_head.srcu, ++ rcu_read_lock_held()); ++ if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else + v = tmp_opp->u_volt; +@@ -157,9 +159,10 @@ unsigned long dev_pm_opp_get_freq(struct + struct dev_pm_opp *tmp_opp; + unsigned long f = 0; + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); + +- tmp_opp = rcu_dereference(opp); ++ tmp_opp = srcu_dereference_check(opp, &opp->dev_opp->srcu_head.srcu, ++ rcu_read_lock_held()); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else +@@ -191,7 +194,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_o + { + struct dev_pm_opp *tmp_opp; + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(&opp->dev_opp->srcu_head.srcu); + + tmp_opp = rcu_dereference(opp); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { +@@ -246,7 +249,7 @@ struct dev_pm_opp *dev_pm_opp_get_suspen + { + struct device_opp *dev_opp; + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(NULL); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || +@@ -326,7 +329,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(NULL); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { +@@ -374,7 +377,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(NULL); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); +@@ -424,7 +427,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + +- opp_rcu_lockdep_assert(); ++ opp_rcu_lockdep_assert(NULL); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); diff --git a/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch b/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch new file mode 100644 index 000000000..60ebe13b1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/172-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch @@ -0,0 +1,184 @@ +From 175329015c8a0b480240da222822d2f8316f074d Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Mon, 1 Jun 2015 18:47:58 -0700 +Subject: cpufreq-dt: Handle OPP voltage adjust events + +On some SoCs the Adaptive Voltage Scaling (AVS) technique is +employed to optimize the operating voltage of a device. At a +given frequency, the hardware monitors dynamic factors and either +makes a suggestion for how much to adjust a voltage for the +current frequency, or it automatically adjusts the voltage +without software intervention. + +In the former case, an AVS driver will call +dev_pm_opp_modify_voltage() and update the voltage for the +particular OPP the CPUs are using. Add an OPP notifier to +cpufreq-dt so that we can adjust the voltage of the CPU when AVS +updates the OPP. + +Signed-off-by: Stephen Boyd +--- + drivers/cpufreq/cpufreq-dt.c | 72 ++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 66 insertions(+), 6 deletions(-) + +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -34,6 +34,9 @@ struct private_data { + struct regulator *cpu_reg; + struct thermal_cooling_device *cdev; + unsigned int voltage_tolerance; /* in percentage */ ++ struct notifier_block opp_nb; ++ struct mutex lock; ++ unsigned long opp_freq; + }; + + static struct freq_attr *cpufreq_dt_attr[] = { +@@ -42,6 +45,42 @@ static struct freq_attr *cpufreq_dt_attr + NULL, + }; + ++static int opp_notifier(struct notifier_block *nb, unsigned long event, ++ void *data) ++{ ++ struct dev_pm_opp *opp = data; ++ struct private_data *priv = container_of(nb, struct private_data, ++ opp_nb); ++ struct device *cpu_dev = priv->cpu_dev; ++ struct regulator *cpu_reg = priv->cpu_reg; ++ unsigned long volt, tol, freq; ++ int ret = 0; ++ ++ switch (event) { ++ case OPP_EVENT_ADJUST_VOLTAGE: ++ volt = dev_pm_opp_get_voltage(opp); ++ freq = dev_pm_opp_get_freq(opp); ++ tol = volt * priv->voltage_tolerance / 100; ++ ++ mutex_lock(&priv->lock); ++ if (freq == priv->opp_freq) ++ ret = regulator_set_voltage_tol(cpu_reg, volt, ++ tol); ++ mutex_unlock(&priv->lock); ++ if (ret) { ++ dev_err(cpu_dev, ++ "failed to scale voltage up: %d\n", ++ ret); ++ return ret; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static int set_target(struct cpufreq_policy *policy, unsigned int index) + { + struct dev_pm_opp *opp; +@@ -53,6 +92,7 @@ static int set_target(struct cpufreq_pol + unsigned long volt = 0, volt_old = 0, tol = 0; + unsigned int old_freq, new_freq; + long freq_Hz, freq_exact; ++ unsigned long opp_freq = 0; + int ret; + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); +@@ -63,8 +103,8 @@ static int set_target(struct cpufreq_pol + new_freq = freq_Hz / 1000; + old_freq = clk_get_rate(cpu_clk) / 1000; + ++ mutex_lock(&priv->lock); + if (!IS_ERR(cpu_reg)) { +- unsigned long opp_freq; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); +@@ -72,7 +112,8 @@ static int set_target(struct cpufreq_pol + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", + freq_Hz); +- return PTR_ERR(opp); ++ ret = PTR_ERR(opp); ++ goto out; + } + volt = dev_pm_opp_get_voltage(opp); + opp_freq = dev_pm_opp_get_freq(opp); +@@ -93,7 +134,7 @@ static int set_target(struct cpufreq_pol + if (ret) { + dev_err(cpu_dev, "failed to scale voltage up: %d\n", + ret); +- return ret; ++ goto out; + } + } + +@@ -102,7 +143,7 @@ static int set_target(struct cpufreq_pol + dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); + if (!IS_ERR(cpu_reg) && volt_old > 0) + regulator_set_voltage_tol(cpu_reg, volt_old, tol); +- return ret; ++ goto out; + } + + /* scaling down? scale voltage after frequency */ +@@ -112,9 +153,12 @@ static int set_target(struct cpufreq_pol + dev_err(cpu_dev, "failed to scale voltage down: %d\n", + ret); + clk_set_rate(cpu_clk, old_freq * 1000); ++ goto out; + } + } +- ++ priv->opp_freq = opp_freq; ++out: ++ mutex_unlock(&priv->lock); + return ret; + } + +@@ -201,6 +245,7 @@ static int cpufreq_init(struct cpufreq_p + unsigned int transition_latency; + bool need_update = false; + int ret; ++ struct srcu_notifier_head *opp_srcu_head; + + ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); + if (ret) { +@@ -277,6 +322,19 @@ static int cpufreq_init(struct cpufreq_p + goto out_free_opp; + } + ++ mutex_init(&priv->lock); ++ ++ opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev); ++ if (IS_ERR(opp_srcu_head)) { ++ ret = PTR_ERR(opp_srcu_head); ++ goto out_free_priv; ++ } ++ ++ priv->opp_nb.notifier_call = opp_notifier; ++ ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb); ++ if (ret) ++ goto out_free_priv; ++ + of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); + + if (!transition_latency) +@@ -326,7 +384,7 @@ static int cpufreq_init(struct cpufreq_p + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); +- goto out_free_priv; ++ goto out_unregister_nb; + } + + priv->cpu_dev = cpu_dev; +@@ -365,6 +423,8 @@ static int cpufreq_init(struct cpufreq_p + + out_free_cpufreq_table: + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++out_unregister_nb: ++ srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb); + out_free_priv: + kfree(priv); + out_free_opp: diff --git a/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch b/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch new file mode 100644 index 000000000..56930520c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/173-cpufreq-dt-Add-L2-frequency-scaling-support.patch @@ -0,0 +1,161 @@ +From b4629f9e30e865402c643de6d4668be790fc0539 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Tue, 8 Sep 2015 11:24:41 +0300 +Subject: cpufreq-dt: Add L2 frequency scaling support + +Signed-off-by: Georgi Djakov + +Conflicts: + drivers/cpufreq/cpufreq-dt.c +--- + drivers/cpufreq/cpufreq-dt.c | 54 ++++++++++++++++++++++++++++++++++++++------ + include/linux/cpufreq.h | 2 ++ + 2 files changed, 49 insertions(+), 7 deletions(-) + +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -86,11 +86,13 @@ static int set_target(struct cpufreq_pol + struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; ++ struct clk *l2_clk = policy->l2_clk; + struct private_data *priv = policy->driver_data; + struct device *cpu_dev = priv->cpu_dev; + struct regulator *cpu_reg = priv->cpu_reg; + unsigned long volt = 0, volt_old = 0, tol = 0; +- unsigned int old_freq, new_freq; ++ unsigned int old_freq, new_freq, l2_freq; ++ unsigned long new_l2_freq = 0; + long freq_Hz, freq_exact; + unsigned long opp_freq = 0; + int ret; +@@ -146,6 +148,30 @@ static int set_target(struct cpufreq_pol + goto out; + } + ++ if (!IS_ERR(l2_clk) && policy->l2_rate[0] && policy->l2_rate[1] && ++ policy->l2_rate[2]) { ++ static unsigned long krait_l2[CONFIG_NR_CPUS] = { }; ++ int cpu, ret = 0; ++ ++ if (freq_exact >= policy->l2_rate[2]) ++ new_l2_freq = policy->l2_rate[2]; ++ else if (freq_exact >= policy->l2_rate[1]) ++ new_l2_freq = policy->l2_rate[1]; ++ else ++ new_l2_freq = policy->l2_rate[0]; ++ ++ krait_l2[policy->cpu] = new_l2_freq; ++ for_each_present_cpu(cpu) ++ new_l2_freq = max(new_l2_freq, krait_l2[cpu]); ++ ++ l2_freq = clk_get_rate(l2_clk); ++ ++ if (l2_freq != new_l2_freq) { ++ /* scale l2 with the core */ ++ ret = clk_set_rate(l2_clk, new_l2_freq); ++ } ++ } ++ + /* scaling down? scale voltage after frequency */ + if (!IS_ERR(cpu_reg) && new_freq < old_freq) { + ret = regulator_set_voltage_tol(cpu_reg, volt, tol); +@@ -156,18 +182,21 @@ static int set_target(struct cpufreq_pol + goto out; + } + } ++ + priv->opp_freq = opp_freq; ++ + out: + mutex_unlock(&priv->lock); + return ret; + } + + static int allocate_resources(int cpu, struct device **cdev, +- struct regulator **creg, struct clk **cclk) ++ struct regulator **creg, struct clk **cclk, ++ struct clk **l2) + { + struct device *cpu_dev; + struct regulator *cpu_reg; +- struct clk *cpu_clk; ++ struct clk *cpu_clk, *l2_clk = NULL; + int ret = 0; + char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; + +@@ -227,6 +256,10 @@ try_again: + *cdev = cpu_dev; + *creg = cpu_reg; + *cclk = cpu_clk; ++ ++ l2_clk = clk_get(cpu_dev, "l2"); ++ if (!IS_ERR(l2_clk)) ++ *l2 = l2_clk; + } + + return ret; +@@ -236,18 +269,20 @@ static int cpufreq_init(struct cpufreq_p + { + struct cpufreq_frequency_table *freq_table; + struct device_node *np; ++ struct device_node *l2_np; + struct private_data *priv; + struct device *cpu_dev; + struct regulator *cpu_reg; +- struct clk *cpu_clk; + struct dev_pm_opp *suspend_opp; ++ struct clk *cpu_clk, *l2_clk; + unsigned long min_uV = ~0, max_uV = 0; + unsigned int transition_latency; + bool need_update = false; + int ret; + struct srcu_notifier_head *opp_srcu_head; + +- ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); ++ ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk, ++ &l2_clk); + if (ret) { + pr_err("%s: Failed to allocate resources: %d\n", __func__, ret); + return ret; +@@ -398,6 +433,11 @@ static int cpufreq_init(struct cpufreq_p + if (suspend_opp) + policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000; + rcu_read_unlock(); ++ policy->l2_clk = l2_clk; ++ ++ l2_np = of_find_node_by_name(NULL, "qcom,l2"); ++ if (l2_np) ++ of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3); + + ret = cpufreq_table_validate_and_show(policy, freq_table); + if (ret) { +@@ -498,7 +538,7 @@ static int dt_cpufreq_probe(struct platf + { + struct device *cpu_dev; + struct regulator *cpu_reg; +- struct clk *cpu_clk; ++ struct clk *cpu_clk, *l2_clk; + int ret; + + /* +@@ -508,7 +548,7 @@ static int dt_cpufreq_probe(struct platf + * + * FIXME: Is checking this only for CPU0 sufficient ? + */ +- ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); ++ ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk, &l2_clk); + if (ret) + return ret; + +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -67,6 +67,8 @@ struct cpufreq_policy { + unsigned int cpu; /* cpu managing this policy, must be online */ + + struct clk *clk; ++ struct clk *l2_clk; /* L2 clock */ ++ unsigned int l2_rate[3]; /* L2 bus clock rate thresholds */ + struct cpufreq_cpuinfo cpuinfo;/* see above */ + + unsigned int min; /* in kHz */ diff --git a/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch b/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch new file mode 100644 index 000000000..124272154 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/174-cpufreq-dt-Add-missing-rcu_read_lock-for-find_device_opp.patch @@ -0,0 +1,33 @@ +From dafae9c5b39e2871bfd8db0b4bad6e850e42ef49 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 13 Jan 2016 15:10:25 +0200 +Subject: cpufreq-dt: Add missing rcu_read_lock() for find_device_opp() + +The function dev_pm_opp_get_notifier() must be called with held +rcu_read_lock. In order to keep the pointer valid, add rcu_read_lock(). + +Signed-off-by: Georgi Djakov +--- + drivers/cpufreq/cpufreq-dt.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -359,14 +359,17 @@ static int cpufreq_init(struct cpufreq_p + + mutex_init(&priv->lock); + ++ rcu_read_lock(); + opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev); + if (IS_ERR(opp_srcu_head)) { + ret = PTR_ERR(opp_srcu_head); ++ rcu_read_unlock(); + goto out_free_priv; + } + + priv->opp_nb.notifier_call = opp_notifier; + ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb); ++ rcu_read_unlock(); + if (ret) + goto out_free_priv; + diff --git a/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch b/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch new file mode 100644 index 000000000..b5c23b516 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch @@ -0,0 +1,386 @@ +From 7727b1cae43e9abac746ef016c3dbf50ee81a6d6 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 18 Mar 2015 17:23:29 +0200 +Subject: clk: qcom: Add support for regmap mux-div clocks + +Add support for hardware that support switching both parent clocks and the +divider at the same time. This avoids generating intermediate frequencies +from either the old parent clock and new divider or new parent clock and +old divider combinations. + +Signed-off-by: Georgi Djakov +--- + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-regmap-mux-div.c | 288 ++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-regmap-mux-div.h | 63 ++++++++ + 3 files changed, 352 insertions(+) + create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c + create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h + +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-y += clk-regmap-mux-div.o + clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + clk-qcom-y += clk-hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-regmap-mux-div.c +@@ -0,0 +1,288 @@ ++/* ++ * Copyright (c) 2015, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk-regmap-mux-div.h" ++ ++#define CMD_RCGR 0x0 ++#define CMD_RCGR_UPDATE BIT(0) ++#define CMD_RCGR_DIRTY_CFG BIT(4) ++#define CMD_RCGR_ROOT_OFF BIT(31) ++#define CFG_RCGR 0x4 ++ ++static int __mux_div_update_config(struct clk_regmap_mux_div *md) ++{ ++ int ret; ++ u32 val, count; ++ const char *name = clk_hw_get_name(&md->clkr.hw); ++ ++ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset, ++ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE); ++ if (ret) ++ return ret; ++ ++ /* Wait for update to take effect */ ++ for (count = 500; count > 0; count--) { ++ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, ++ &val); ++ if (ret) ++ return ret; ++ if (!(val & CMD_RCGR_UPDATE)) ++ return 0; ++ udelay(1); ++ } ++ ++ pr_err("%s: RCG did not update its configuration", name); ++ return -EBUSY; ++} ++ ++static int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src_sel, ++ u32 src_div) ++{ ++ int ret; ++ u32 val, mask; ++ ++ val = (src_div << md->hid_shift) | (src_sel << md->src_shift); ++ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) | ++ ((BIT(md->src_width) - 1) << md->src_shift); ++ ++ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset, ++ mask, val); ++ if (ret) ++ return ret; ++ ++ return __mux_div_update_config(md); ++} ++ ++static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src_sel, ++ u32 *src_div) ++{ ++ u32 val, div, src; ++ const char *name = clk_hw_get_name(&md->clkr.hw); ++ ++ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); ++ ++ if (val & CMD_RCGR_DIRTY_CFG) { ++ pr_err("%s: RCG configuration is pending\n", name); ++ return; ++ } ++ ++ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); ++ src = (val >> md->src_shift); ++ src &= BIT(md->src_width) - 1; ++ *src_sel = src; ++ ++ div = (val >> md->hid_shift); ++ div &= BIT(md->hid_width) - 1; ++ *src_div = div; ++} ++ ++static int mux_div_enable(struct clk_hw *hw) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ ++ return __mux_div_set_src_div(md, md->src_sel, md->div); ++} ++ ++static inline bool is_better_rate(unsigned long req, unsigned long best, ++ unsigned long new) ++{ ++ return (req <= new && new < best) || (best < req && best < new); ++} ++ ++static int mux_div_determine_rate(struct clk_hw *hw, ++ struct clk_rate_request *req) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ unsigned int i, div, max_div; ++ unsigned long actual_rate, best_rate = 0; ++ unsigned long req_rate = req->rate; ++ ++ for (i = 0; i < clk_hw_get_num_parents(hw); i++) { ++ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); ++ unsigned long parent_rate = clk_hw_get_rate(parent); ++ ++ max_div = BIT(md->hid_width) - 1; ++ for (div = 1; div < max_div; div++) { ++ parent_rate = mult_frac(req_rate, div, 2); ++ parent_rate = clk_hw_round_rate(parent, parent_rate); ++ actual_rate = mult_frac(parent_rate, 2, div); ++ ++ if (is_better_rate(req_rate, best_rate, actual_rate)) { ++ best_rate = actual_rate; ++ req->rate = best_rate; ++ req->best_parent_rate = parent_rate; ++ req->best_parent_hw = parent; ++ } ++ ++ if (actual_rate < req_rate || best_rate <= req_rate) ++ break; ++ } ++ } ++ ++ if (!best_rate) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long prate, u32 src_sel) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ int ret, i; ++ u32 div, max_div, best_src = 0, best_div = 0; ++ unsigned long actual_rate, best_rate = 0; ++ ++ for (i = 0; i < clk_hw_get_num_parents(hw); i++) { ++ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); ++ unsigned long parent_rate = clk_hw_get_rate(parent); ++ ++ max_div = BIT(md->hid_width) - 1; ++ for (div = 1; div < max_div; div++) { ++ parent_rate = mult_frac(rate, div, 2); ++ parent_rate = clk_hw_round_rate(parent, parent_rate); ++ actual_rate = mult_frac(parent_rate, 2, div); ++ ++ if (is_better_rate(rate, best_rate, actual_rate)) { ++ best_rate = actual_rate; ++ best_src = md->parent_map[i].cfg; ++ best_div = div - 1; ++ } ++ ++ if (actual_rate < rate || best_rate <= rate) ++ break; ++ } ++ } ++ ++ ret = __mux_div_set_src_div(md, best_src, best_div); ++ if (!ret) { ++ md->div = best_div; ++ md->src_sel = best_src; ++ } ++ ++ return ret; ++} ++ ++static u8 mux_div_get_parent(struct clk_hw *hw) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ const char *name = clk_hw_get_name(hw); ++ u32 i, div, src; ++ ++ __mux_div_get_src_div(md, &src, &div); ++ ++ for (i = 0; i < clk_hw_get_num_parents(hw); i++) ++ if (src == md->parent_map[i].cfg) ++ return i; ++ ++ pr_err("%s: Can't find parent %d\n", name, src); ++ return 0; ++} ++ ++static int mux_div_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ ++ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div); ++} ++ ++static int mux_div_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long prate) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ ++ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src_sel); ++} ++ ++static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, ++ unsigned long prate, u8 index) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ ++ return __mux_div_set_rate_and_parent(hw, rate, prate, ++ md->parent_map[index].cfg); ++} ++ ++static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ u32 div, src; ++ int i, num_parents = clk_hw_get_num_parents(hw); ++ const char *name = clk_hw_get_name(hw); ++ ++ __mux_div_get_src_div(md, &src, &div); ++ for (i = 0; i < num_parents; i++) ++ if (src == md->parent_map[i].cfg) { ++ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i); ++ unsigned long parent_rate = clk_hw_get_rate(p); ++ ++ return mult_frac(parent_rate, 2, div + 1); ++ } ++ ++ pr_err("%s: Can't find parent %d\n", name, src); ++ return 0; ++} ++ ++static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw, ++ unsigned long *safe_freq) ++{ ++ int i; ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ ++ if (md->safe_freq) ++ *safe_freq = md->safe_freq; ++ ++ for (i = 0; i < clk_hw_get_num_parents(hw); i++) ++ if (md->safe_src == md->parent_map[i].cfg) ++ break; ++ ++ return clk_hw_get_parent_by_index(hw, i); ++} ++ ++static void mux_div_disable(struct clk_hw *hw) ++{ ++ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); ++ struct clk_hw *parent; ++ u32 div; ++ ++ if (!md->safe_freq || !md->safe_src) ++ return; ++ ++ parent = mux_div_get_safe_parent(hw, &md->safe_freq); ++ div = divider_get_val(md->safe_freq, clk_get_rate(parent->clk), NULL, ++ md->hid_width, CLK_DIVIDER_ROUND_CLOSEST); ++ div = 2 * div + 1; ++ ++ __mux_div_set_src_div(md, md->safe_src, div); ++} ++ ++const struct clk_ops clk_regmap_mux_div_ops = { ++ .enable = mux_div_enable, ++ .disable = mux_div_disable, ++ .get_parent = mux_div_get_parent, ++ .set_parent = mux_div_set_parent, ++ .set_rate = mux_div_set_rate, ++ .set_rate_and_parent = mux_div_set_rate_and_parent, ++ .determine_rate = mux_div_determine_rate, ++ .recalc_rate = mux_div_recalc_rate, ++ .get_safe_parent = mux_div_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops); +--- /dev/null ++++ b/drivers/clk/qcom/clk-regmap-mux-div.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (c) 2015, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 __QCOM_CLK_REGMAP_MUX_DIV_H__ ++#define __QCOM_CLK_REGMAP_MUX_DIV_H__ ++ ++#include ++#include "clk-regmap.h" ++#include "clk-rcg.h" ++ ++/** ++ * struct mux_div_clk - combined mux/divider clock ++ * @reg_offset: offset of the mux/divider register ++ * @hid_width: number of bits in half integer divider ++ * @hid_shift: lowest bit of hid value field ++ * @src_width: number of bits in source select ++ * @src_shift: lowest bit of source select field ++ * @div: the divider raw configuration value ++ * @src_sel: the mux index which will be used if the clock is enabled ++ * @safe_src: the safe source mux index for this clock ++ * @safe_freq: When switching rates from A to B, the mux div clock will ++ * instead switch from A -> safe_freq -> B. This allows the ++ * mux_div clock to change rates while enabled, even if this ++ * behavior is not supported by the parent clocks. ++ * If changing the rate of parent A also causes the rate of ++ * parent B to change, then safe_freq must be defined. ++ * safe_freq is expected to have a source clock which is always ++ * on and runs at only one rate. ++ * @parent_map: pointer to parent_map struct ++ * @clkr: handle between common and hardware-specific interfaces ++ */ ++ ++struct clk_regmap_mux_div { ++ u32 reg_offset; ++ u32 hid_width; ++ u32 hid_shift; ++ u32 src_width; ++ u32 src_shift; ++ u32 div; ++ u32 src_sel; ++ u32 safe_src; ++ unsigned long safe_freq; ++ const struct parent_map *parent_map; ++ struct clk_regmap clkr; ++}; ++ ++#define to_clk_regmap_mux_div(_hw) \ ++ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr) ++ ++extern const struct clk_ops clk_regmap_mux_div_ops; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch b/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch new file mode 100644 index 000000000..c8d83e9e5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/180-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch @@ -0,0 +1,53 @@ +From patchwork Wed Nov 2 15:56:58 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v9,3/3] clk: qcom: Always add factor clock for xo clocks +From: Georgi Djakov +X-Patchwork-Id: 9409421 +Message-Id: <20161102155658.32203-4-georgi.djakov@linaro.org> +To: sboyd@codeaurora.org, mturquette@baylibre.com +Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, + robh+dt@kernel.org, mark.rutland@arm.com, + linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + georgi.djakov@linaro.org +Date: Wed, 2 Nov 2016 17:56:58 +0200 + +Currently the RPM/RPM-SMD clock drivers do not register the xo clocks, +so we should always add factor clock. When we later add xo clocks support +into the drivers, we should update this function to skip registration. +By doing so we avoid any DT dependencies. + +Signed-off-by: Georgi Djakov +--- + drivers/clk/qcom/common.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +-- +To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + +--- a/drivers/clk/qcom/common.c ++++ b/drivers/clk/qcom/common.c +@@ -154,15 +154,12 @@ int qcom_cc_register_board_clk(struct de + const char *name, unsigned long rate) + { + bool add_factor = true; +- struct device_node *node; + +- /* The RPM clock driver will add the factor clock if present */ +- if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { +- node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); +- if (of_device_is_available(node)) +- add_factor = false; +- of_node_put(node); +- } ++ /* ++ * TODO: The RPM clock driver currently does not support the xo clock. ++ * When xo is added to the RPM clock driver, we should change this ++ * function to skip registration of xo factor clocks. ++ */ + + return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); + } diff --git a/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch b/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch new file mode 100644 index 000000000..b766ec07e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/181-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch @@ -0,0 +1,29 @@ +From 252a4e72dfd7add3c37604449fd20db29d84c6f8 Mon Sep 17 00:00:00 2001 +From: Lina Iyer +Date: Wed, 25 Mar 2015 14:25:29 -0600 +Subject: ARM: cpuidle: Add cpuidle support for QCOM cpus + +Define ARM_QCOM_CPUIDLE config item to enable cpuidle support. + +Cc: Stephen Boyd +Cc: Arnd Bergmann +Cc: Kevin Hilman +Cc: Daniel Lezcano +Signed-off-by: Lina Iyer +--- + drivers/cpuidle/Kconfig.arm | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/cpuidle/Kconfig.arm ++++ b/drivers/cpuidle/Kconfig.arm +@@ -74,3 +74,10 @@ config ARM_MVEBU_V7_CPUIDLE + depends on ARCH_MVEBU && !ARM64 + help + Select this to enable cpuidle on Armada 370, 38x and XP processors. ++ ++config ARM_QCOM_CPUIDLE ++ bool "CPU Idle Driver for QCOM processors" ++ depends on ARCH_QCOM ++ select ARM_CPUIDLE ++ help ++ Select this to enable cpuidle on QCOM processors. diff --git a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch new file mode 100644 index 000000000..35ba6fbb2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -0,0 +1,62 @@ +From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Wed, 29 Apr 2015 15:21:46 -0700 +Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom + +ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended +on most ARM architectures. This automatically calculate ZRELADDR by +masking PHYS_OFFSET with 0xf8000000. + +However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware +network accelerators, and the bootloader removes this section from the +layout passed from the ATAGS (when used). + +For newer bootloader, when DT is used, this is not a problem, we just +reserve this memory in the device tree. But if the bootloader doesn't +have DT support, then ATAGS have to be used. In this case, the ARM +decompressor will position the kernel in this low mem, which will not be +in the RAM section mapped by the bootloader, which means the kernel will +freeze in the middle of the boot process trying to map the memory. + +As a work around, this patch allows disabling AUTO_ZRELADDR when +ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders +which don't support device-tree, which is the case on certain early +IPQ806x based designs. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/Kconfig | 2 +- + arch/arm/Makefile | 2 ++ + arch/arm/mach-qcom/Makefile.boot | 1 + + 3 files changed, 4 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/mach-qcom/Makefile.boot + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -324,7 +324,7 @@ config ARCH_MULTIPLATFORM + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ select AUTO_ZRELADDR if !ARCH_QCOM + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -256,9 +256,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac + else + MACHINE := + endif ++ifeq ($(CONFIG_ARCH_QCOM),) + ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) + MACHINE := + endif ++endif + + machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) + platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile.boot +@@ -0,0 +1 @@ ++zreladdr-y+= 0x42208000 diff --git a/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch new file mode 100644 index 000000000..8890a9173 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch @@ -0,0 +1,13 @@ +--- a/drivers/mtd/qcom_smem_part.c ++++ b/drivers/mtd/qcom_smem_part.c +@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st + m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); + m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); + ++ /* "rootfs" conflicts with OpenWrt auto mounting */ ++ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs")) ++ m_part->name = "ubi"; ++ + /* + * The last SMEM partition may have its size marked as + * something like 0xffffffff, which means "until the end of the diff --git a/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch b/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch new file mode 100644 index 000000000..f7ea6fd25 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/303-add-saw_l2-and-sns-into-ipq8064-DT.patch @@ -0,0 +1,41 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -50,6 +50,7 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; ++ qcom,saw = <&saw_l2>; + }; + + qcom,l2 { +@@ -277,17 +278,28 @@ + }; + + saw0: regulator@2089000 { +- compatible = "qcom,saw2"; ++ compatible = "qcom,saw2", "syscon"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; + regulator; + }; + + saw1: regulator@2099000 { +- compatible = "qcom,saw2"; ++ compatible = "qcom,saw2", "syscon"; + reg = <0x02099000 0x1000>, <0x02009000 0x1000>; + regulator; + }; + ++ saw_l2: regulator@02012000 { ++ compatible = "qcom,saw2", "syscon"; ++ reg = <0x02012000 0x1000>; ++ regulator; ++ }; ++ ++ sic_non_secure: sic-non-secure@12100000 { ++ compatible = "syscon"; ++ reg = <0x12100000 0x10000>; ++ }; ++ + gsbi2: gsbi@12480000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <2>; diff --git a/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch b/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch new file mode 100644 index 000000000..42b37ca2c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/304-add-cpu-idle-state-into-ipq8064-DT.patch @@ -0,0 +1,58 @@ +From 3985d2c24d96f72211488fc6ebb53b032e4d0a05 Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Sun, 6 Nov 2016 19:02:34 +0300 +Subject: [PATCH] ipq806x: add cpu idle state into ipq8064 DT + +Signed-off-by: Pavel Kubelun +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -18,7 +18,7 @@ + #address-cells = <1>; + #size-cells = <0>; + +- cpu@0 { ++ cpu0: cpu@0 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; +@@ -31,9 +31,10 @@ + clock-latency = <100000>; + cpu-supply = <&smb208_s2a>; + voltage-tolerance = <5>; ++ cpu-idle-states = <&CPU_SPC>; + }; + +- cpu@1 { ++ cpu1: cpu@1 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; +@@ -45,6 +46,7 @@ + clock-names = "cpu", "l2"; + clock-latency = <100000>; + cpu-supply = <&smb208_s2b>; ++ cpu-idle-states = <&CPU_SPC>; + }; + + L2: l2-cache { +@@ -56,6 +58,16 @@ + qcom,l2 { + qcom,l2-rates = <384000000 1000000000 1200000000>; + }; ++ ++ idle-states { ++ CPU_SPC: spc { ++ compatible = "qcom,idle-state-spc", ++ "arm,idle-state"; ++ entry-latency-us = <400>; ++ exit-latency-us = <900>; ++ min-residency-us = <3000>; ++ }; ++ }; + }; + + cpu-pmu { diff --git a/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch b/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch new file mode 100644 index 000000000..be4589582 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/305-add-board-clocks-and-rpmcc-into-DT.patch @@ -0,0 +1,43 @@ +From 6da1b6260843da455cad8180c18d020679fd4a46 Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Sun, 6 Nov 2016 19:07:24 +0300 +Subject: [PATCH] ipq806x: add board clocks and rpmcc into DT + +Signed-off-by: Pavel Kubelun +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -92,6 +92,18 @@ + }; + + clocks { ++ cxo_board { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ }; ++ ++ pxo_board { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ }; ++ + sleep_clk: sleep_clk { + compatible = "fixed-clock"; + clock-frequency = <32768>; +@@ -175,6 +187,11 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ rpmcc: clock-controller { ++ compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc"; ++ #clock-cells = <1>; ++ }; ++ + regulators { + compatible = "qcom,rpm-smb208-regulators"; + diff --git a/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch b/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch new file mode 100644 index 000000000..005f980eb --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/306-add-RPM-msg-RAM-into-DT.patch @@ -0,0 +1,22 @@ +From 9bf6b333d6a4d3a3ef0d3fc7ca0becd008c81820 Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Sun, 6 Nov 2016 19:12:46 +0300 +Subject: [PATCH] ipq806x: add RPM msg RAM into DT + +Signed-off-by: Pavel Kubelun +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -184,6 +184,9 @@ + "err", + "wakeup"; + ++ clocks = <&gcc RPM_MSG_RAM_H_CLK>; ++ clock-names = "ram"; ++ + #address-cells = <1>; + #size-cells = <0>; + diff --git a/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch new file mode 100644 index 000000000..e90f4a6fa --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch @@ -0,0 +1,25 @@ +From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001 +From: Abhishek Sahu +Date: Wed, 18 Nov 2015 12:38:56 +0530 +Subject: ipq806x: gcc: Added the enable regs and mask for PRNG + +kernel got hanged while reading from /dev/hwrng at the +time of PRNG clock enable + +Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172 +Signed-off-by: Abhishek Sahu +--- + drivers/clk/qcom/gcc-ipq806x.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -1240,6 +1240,8 @@ static struct clk_rcg prng_src = { + .parent_map = gcc_pxo_pll8_map, + }, + .clkr = { ++ .enable_reg = 0x2e80, ++ .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "prng_src", + .parent_names = gcc_pxo_pll8, diff --git a/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch b/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch new file mode 100644 index 000000000..1c997ab25 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/308-soc-qcom-ipq806x-Increase-Atomic-Coherent-Pool-size.patch @@ -0,0 +1,52 @@ +From 689a8f1ec58a88152669d21572a1539ad34024cb Mon Sep 17 00:00:00 2001 +From: Varadarajan Narayanan +Date: Mon, 30 Mar 2015 13:25:21 +0530 +Subject: soc: qcom: ipq806x: Increase Atomic Coherent Pool size + +Linux 3.14, by default allocates a 256K Atomic Coherent Pool. +However, Linux 3.4 seems to have allocated ~1.8M for the same. +256K doesn't seem to be enough for the WiFi driver. Hence, +setting the size to be similar to 3.4. + +CRs-Fixed: 810357 + +Change-Id: I5b98a8531dcb33aff451a943f8b83402f9d13fa7 +Signed-off-by: Varadarajan Narayanan +--- + arch/arm/mach-qcom/board.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/arch/arm/mach-qcom/board.c ++++ b/arch/arm/mach-qcom/board.c +@@ -12,6 +12,11 @@ + + #include + ++#include ++#include ++#include ++#include ++ + #include + + static const char * const qcom_dt_match[] __initconst = { +@@ -28,3 +33,19 @@ static const char * const qcom_dt_match[ + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") + .dt_compat = qcom_dt_match, + MACHINE_END ++ ++ ++static int __init qcom_atomic_pool_size_set(void) ++{ ++#define ATOMIC_DMA_COHERENT_POOL_SIZE SZ_2M ++ ++ init_dma_coherent_pool_size(ATOMIC_DMA_COHERENT_POOL_SIZE); ++ ++ return 0; ++} ++ ++/* ++ * This should happen before atomic_pool_init(), which is a ++ * postcore_initcall. ++ */ ++core_initcall(qcom_atomic_pool_size_set); diff --git a/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch new file mode 100644 index 000000000..0eae3e7bf --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch @@ -0,0 +1,38 @@ +From 856371ca1561ca9b3280cc323ff296c7c5e1fa93 Mon Sep 17 00:00:00 2001 +From: Pavel Kubelun +Date: Tue, 22 Nov 2016 17:37:56 +0300 +Subject: [PATCH] ipq806x: clk: gcc: add tsens child node + +Thermal sensors in ipq806x are inside a Global clock controller. +Add a child node into it to be used by the TSENS driver. + +Signed-off-by: Pavel Kubelun + +--- + drivers/clk/qcom/gcc-ipq806x.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -3109,6 +3109,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc + static int gcc_ipq806x_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; ++ struct platform_device *tsens; + struct regmap *regmap; + int ret; + +@@ -3138,6 +3139,13 @@ static int gcc_ipq806x_probe(struct plat + regmap_write(regmap, 0x3cf8, 8); + regmap_write(regmap, 0x3d18, 8); + ++ tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1, ++ NULL, 0); ++ if (IS_ERR(tsens)) ++ return PTR_ERR(tsens); ++ ++ platform_set_drvdata(pdev, tsens); ++ + return 0; + } + diff --git a/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch b/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch new file mode 100644 index 000000000..b2564a540 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch @@ -0,0 +1,150 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -31,6 +31,9 @@ + clock-latency = <100000>; + cpu-supply = <&smb208_s2a>; + voltage-tolerance = <5>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SPC>; + }; + +@@ -46,6 +49,9 @@ + clock-names = "cpu", "l2"; + clock-latency = <100000>; + cpu-supply = <&smb208_s2b>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; + cpu-idle-states = <&CPU_SPC>; + }; + +@@ -70,6 +76,92 @@ + }; + }; + ++ thermal-zones { ++ cpu-thermal0 { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&gcc 5>; ++ coefficients = <1132 0>; ++ ++ trips { ++ cpu_alert0: trip0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ cpu_crit0: trip1 { ++ temperature = <110000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ cpu-thermal1 { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&gcc 6>; ++ coefficients = <1132 0>; ++ ++ trips { ++ cpu_alert1: trip0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ cpu_crit1: trip1 { ++ temperature = <110000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ cpu-thermal2 { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&gcc 7>; ++ coefficients = <1199 0>; ++ ++ trips { ++ cpu_alert2: trip0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ cpu_crit2: trip1 { ++ temperature = <110000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ cpu-thermal3 { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&gcc 8>; ++ coefficients = <1132 0>; ++ ++ trips { ++ cpu_alert3: trip0 { ++ temperature = <75000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ cpu_crit3: trip1 { ++ temperature = <110000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ }; ++ + cpu-pmu { + compatible = "qcom,krait-pmu"; + interrupts = <1 10 0x304>; +@@ -172,6 +264,21 @@ + reg-names = "lpass-lpaif"; + }; + ++ qfprom: qfprom@700000 { ++ compatible = "qcom,qfprom", "syscon"; ++ reg = <0x00700000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ tsens_calib: calib { ++ reg = <0x400 0x10>; ++ }; ++ tsens_backup: backup_calib { ++ reg = <0x410 0x10>; ++ }; ++ }; ++ + rpm@108000 { + compatible = "qcom,rpm-ipq8064"; + reg = <0x108000 0x1000>; +@@ -499,8 +606,12 @@ + gcc: clock-controller@900000 { + compatible = "qcom,gcc-ipq8064"; + reg = <0x00900000 0x4000>; ++ nvmem-cells = <&tsens_calib>, <&tsens_backup>; ++ nvmem-cell-names = "calib", "calib_backup"; + #clock-cells = <1>; + #reset-cells = <1>; ++ #power-domain-cells = <1>; ++ #thermal-sensor-cells = <1>; + }; + + tcsr: syscon@1a400000 { diff --git a/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch new file mode 100644 index 000000000..bb9dd87dd --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch @@ -0,0 +1,81 @@ +--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -12,6 +12,7 @@ Required properties : + + "qcom,rpmcc-msm8916", "qcom,rpmcc" + "qcom,rpmcc-apq8064", "qcom,rpmcc" ++ "qcom,rpmcc-ipq806x", "qcom,rpmcc" + + - #clock-cells : shall contain 1 + +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a + DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); + DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); + ++/* ipq806x */ ++DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); ++DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); ++ + static struct clk_rpm *apq8064_clks[] = { + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, +@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] = + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, + }; + ++static struct clk_rpm *ipq806x_clks[] = { ++ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, ++ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, ++ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, ++ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, ++ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, ++ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, ++ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, ++ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, ++ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, ++ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, ++ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, ++ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, ++ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, ++ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, ++ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, ++ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, ++}; ++ + static const struct rpm_clk_desc rpm_clk_apq8064 = { + .clks = apq8064_clks, + .num_clks = ARRAY_SIZE(apq8064_clks), + }; + ++static const struct rpm_clk_desc rpm_clk_ipq806x = { ++ .clks = ipq806x_clks, ++ .num_clks = ARRAY_SIZE(ipq806x_clks), ++}; ++ + static const struct of_device_id rpm_clk_match_table[] = { + { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, ++ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, + { } + }; + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); +--- a/include/dt-bindings/clock/qcom,rpmcc.h ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -37,6 +37,10 @@ + #define RPM_SYS_FABRIC_A_CLK 19 + #define RPM_SFPB_CLK 20 + #define RPM_SFPB_A_CLK 21 ++#define RPM_NSS_FABRIC_0_CLK 22 ++#define RPM_NSS_FABRIC_0_A_CLK 23 ++#define RPM_NSS_FABRIC_1_CLK 24 ++#define RPM_NSS_FABRIC_1_A_CLK 25 + + /* msm8916 */ + #define RPM_SMD_XO_CLK_SRC 0 diff --git a/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch b/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch new file mode 100644 index 000000000..4b5d480d3 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/315-disable-usb3-phy-suspend.patch @@ -0,0 +1,36 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -678,6 +678,8 @@ + clocks = <&gcc USB30_0_MASTER_CLK>; + clock-names = "core"; + ++ syscon-tcsr = <&tcsr 0xb0 1>; ++ + ranges; + + status = "disabled"; +@@ -689,6 +691,7 @@ + phys = <&hs_phy_0>, <&ss_phy_0>; + phy-names = "usb2-phy", "usb3-phy"; + dr_mode = "host"; ++ snps,dis_u3_susphy_quirk; + }; + }; + +@@ -699,6 +702,8 @@ + clocks = <&gcc USB30_1_MASTER_CLK>; + clock-names = "core"; + ++ syscon-tcsr = <&tcsr 0xb0 0>; ++ + ranges; + + status = "disabled"; +@@ -710,6 +715,7 @@ + phys = <&hs_phy_1>, <&ss_phy_1>; + phy-names = "usb2-phy", "usb3-phy"; + dr_mode = "host"; ++ snps,dis_u3_susphy_quirk; + }; + }; + diff --git a/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch b/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch new file mode 100644 index 000000000..f2faf64f0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/316-dt-add-prng-support.patch @@ -0,0 +1,16 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -336,6 +336,13 @@ + }; + }; + ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; ++ + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; diff --git a/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch b/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch new file mode 100644 index 000000000..c369ab914 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/400-dsa-add-qca.patch @@ -0,0 +1,1068 @@ +From patchwork Fri May 29 01:42:16 2015 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [1/7] net: dsa: add new driver for ar8xxx family +From: Mathieu Olivari +X-Patchwork-Id: 477523 +X-Patchwork-Delegate: davem@davemloft.net +Message-Id: <1432863742-18427-2-git-send-email-mathieu@codeaurora.org> +To: robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, + ijc+devicetree@hellion.org.uk, galak@codeaurora.org, + davem@davemloft.net, mathieu@codeaurora.org, andrew@lunn.ch, + f.fainelli@gmail.com, linux@roeck-us.net, gang.chen.5i5j@gmail.com, + jiri@resnulli.us, leitec@staticky.com, fabf@skynet.be, + alexander.h.duyck@intel.com, pavel.nakonechny@skitlab.ru, + joe@perches.com, sfeldma@gmail.com, nbd@nbd.name, juhosg@openwrt.org +Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Date: Thu, 28 May 2015 18:42:16 -0700 + +This patch contains initial init & registration code for QCA8337. It +will detect a QCA8337 switch, if present and declared in DT/platform. + +Each port will be represented through a standalone net_device interface, +as for other DSA switches. CPU can communicate with any of the ports by +setting an IP@ on ethN interface. Ports cannot communicate with each +other just yet. + +Link status will be reported through polling, and we don't use any +encapsulation. + +Signed-off-by: Mathieu Olivari +--- + drivers/net/dsa/Kconfig | 7 ++ + drivers/net/dsa/Makefile | 1 + + drivers/net/dsa/ar8xxx.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/ar8xxx.h | 82 +++++++++++++ + net/dsa/dsa.c | 1 + + 5 files changed, 394 insertions(+) + create mode 100644 drivers/net/dsa/ar8xxx.c + create mode 100644 drivers/net/dsa/ar8xxx.h + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -65,4 +65,13 @@ config NET_DSA_BCM_SF2 + This enables support for the Broadcom Starfighter 2 Ethernet + switch chips. + ++config NET_DSA_AR8XXX ++ tristate "Qualcomm Atheros AR8XXX Ethernet switch family support" ++ depends on NET_DSA ++ select NET_DSA_TAG_QCA ++ select REGMAP ++ ---help--- ++ This enables support for the Qualcomm Atheros AR8XXX Ethernet ++ switch chips. ++ + endmenu +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -14,3 +14,4 @@ ifdef CONFIG_NET_DSA_MV88E6171 + mv88e6xxx_drv-y += mv88e6171.o + endif + obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o ++obj-$(CONFIG_NET_DSA_AR8XXX) += ar8xxx.o +--- /dev/null ++++ b/drivers/net/dsa/ar8xxx.c +@@ -0,0 +1,529 @@ ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ar8xxx.h" ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++static const struct ar8xxx_mib_desc ar8327_mib[] = { ++ MIB_DESC(1, 0x00, "RxBroad"), ++ MIB_DESC(1, 0x04, "RxPause"), ++ MIB_DESC(1, 0x08, "RxMulti"), ++ MIB_DESC(1, 0x0c, "RxFcsErr"), ++ MIB_DESC(1, 0x10, "RxAlignErr"), ++ MIB_DESC(1, 0x14, "RxRunt"), ++ MIB_DESC(1, 0x18, "RxFragment"), ++ MIB_DESC(1, 0x1c, "Rx64Byte"), ++ MIB_DESC(1, 0x20, "Rx128Byte"), ++ MIB_DESC(1, 0x24, "Rx256Byte"), ++ MIB_DESC(1, 0x28, "Rx512Byte"), ++ MIB_DESC(1, 0x2c, "Rx1024Byte"), ++ MIB_DESC(1, 0x30, "Rx1518Byte"), ++ MIB_DESC(1, 0x34, "RxMaxByte"), ++ MIB_DESC(1, 0x38, "RxTooLong"), ++ MIB_DESC(2, 0x3c, "RxGoodByte"), ++ MIB_DESC(2, 0x44, "RxBadByte"), ++ MIB_DESC(1, 0x4c, "RxOverFlow"), ++ MIB_DESC(1, 0x50, "Filtered"), ++ MIB_DESC(1, 0x54, "TxBroad"), ++ MIB_DESC(1, 0x58, "TxPause"), ++ MIB_DESC(1, 0x5c, "TxMulti"), ++ MIB_DESC(1, 0x60, "TxUnderRun"), ++ MIB_DESC(1, 0x64, "Tx64Byte"), ++ MIB_DESC(1, 0x68, "Tx128Byte"), ++ MIB_DESC(1, 0x6c, "Tx256Byte"), ++ MIB_DESC(1, 0x70, "Tx512Byte"), ++ MIB_DESC(1, 0x74, "Tx1024Byte"), ++ MIB_DESC(1, 0x78, "Tx1518Byte"), ++ MIB_DESC(1, 0x7c, "TxMaxByte"), ++ MIB_DESC(1, 0x80, "TxOverSize"), ++ MIB_DESC(2, 0x84, "TxByte"), ++ MIB_DESC(1, 0x8c, "TxCollision"), ++ MIB_DESC(1, 0x90, "TxAbortCol"), ++ MIB_DESC(1, 0x94, "TxMultiCol"), ++ MIB_DESC(1, 0x98, "TxSingleCol"), ++ MIB_DESC(1, 0x9c, "TxExcDefer"), ++ MIB_DESC(1, 0xa0, "TxDefer"), ++ MIB_DESC(1, 0xa4, "TxLateCol"), ++}; ++ ++u32 ++ar8xxx_mii_read32(struct mii_bus *bus, int phy_id, int regnum) ++{ ++ u16 lo, hi; ++ ++ lo = bus->read(bus, phy_id, regnum); ++ hi = bus->read(bus, phy_id, regnum + 1); ++ ++ return (hi << 16) | lo; ++} ++ ++void ++ar8xxx_mii_write32(struct mii_bus *bus, int phy_id, int regnum, u32 val) ++{ ++ u16 lo, hi; ++ ++ lo = val & 0xffff; ++ hi = (u16)(val >> 16); ++ ++ bus->write(bus, phy_id, regnum, lo); ++ bus->write(bus, phy_id, regnum + 1, hi); ++} ++ ++u32 ar8xxx_read(struct dsa_switch *ds, int reg) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); ++ u16 r1, r2, page; ++ u32 val; ++ ++ split_addr((u32)reg, &r1, &r2, &page); ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, 0x18, 0, page); ++ wait_for_page_switch(); ++ val = ar8xxx_mii_read32(bus, 0x10 | r2, r1); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return val; ++} ++ ++void ar8xxx_write(struct dsa_switch *ds, int reg, u32 val) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); ++ u16 r1, r2, page; ++ ++ split_addr((u32)reg, &r1, &r2, &page); ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, 0x18, 0, page); ++ wait_for_page_switch(); ++ ar8xxx_mii_write32(bus, 0x10 | r2, r1, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++} ++ ++u32 ++ar8xxx_rmw(struct dsa_switch *ds, int reg, u32 mask, u32 val) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); ++ u16 r1, r2, page; ++ u32 ret; ++ ++ split_addr((u32)reg, &r1, &r2, &page); ++ ++ mutex_lock(&bus->mdio_lock); ++ ++ bus->write(bus, 0x18, 0, page); ++ wait_for_page_switch(); ++ ++ ret = ar8xxx_mii_read32(bus, 0x10 | r2, r1); ++ ret &= ~mask; ++ ret |= val; ++ ar8xxx_mii_write32(bus, 0x10 | r2, r1, ret); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static char *ar8xxx_probe(struct device *host_dev, int sw_addr) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); ++ u32 phy_id; ++ ++ if (!bus) ++ return NULL; ++ ++ /* sw_addr is irrelevant as the switch occupies the MDIO bus from ++ * addresses 0 to 4 (PHYs) and 16-23 (for MDIO 32bits protocol). So ++ * we'll probe address 0 to see if we see the right switch family. ++ */ ++ phy_id = mdiobus_read(bus, 0, MII_PHYSID1) << 16; ++ phy_id |= mdiobus_read(bus, 0, MII_PHYSID2); ++ ++ switch (phy_id) { ++ case PHY_ID_QCA8337: ++ return "QCA8337"; ++ default: ++ return NULL; ++ } ++} ++ ++static int ar8xxx_regmap_read(void *ctx, uint32_t reg, uint32_t *val) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)ctx; ++ ++ *val = ar8xxx_read(ds, reg); ++ ++ return 0; ++} ++ ++static int ar8xxx_regmap_write(void *ctx, uint32_t reg, uint32_t val) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)ctx; ++ ++ ar8xxx_write(ds, reg, val); ++ ++ return 0; ++} ++ ++static const struct regmap_range ar8xxx_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0C00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++ ++}; ++ ++static struct regmap_access_table ar8xxx_readable_table = { ++ .yes_ranges = ar8xxx_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(ar8xxx_readable_ranges), ++}; ++ ++struct regmap_config ar8xxx_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x16ac, /* end MIB - Port6 range */ ++ .reg_read = ar8xxx_regmap_read, ++ .reg_write = ar8xxx_regmap_write, ++ .rd_table = &ar8xxx_readable_table, ++}; ++ ++static int ar8xxx_set_pad_ctrl(struct dsa_switch *ds, int port, int mode) ++{ ++ int reg; ++ ++ switch (port) { ++ case 0: ++ reg = AR8327_REG_PORT0_PAD_CTRL; ++ break; ++ case 6: ++ reg = AR8327_REG_PORT6_PAD_CTRL; ++ break; ++ default: ++ pr_err("Can't set PAD_CTRL on port %d\n", port); ++ return -EINVAL; ++ } ++ ++ /* DSA only supports 1 CPU port for now, so we'll take the assumption ++ * that P0 is connected to the CPU master_dev. ++ */ ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ ar8xxx_write(ds, reg, ++ AR8327_PORT_PAD_RGMII_EN | ++ AR8327_PORT_PAD_RGMII_TX_DELAY(3) | ++ AR8327_PORT_PAD_RGMII_RX_DELAY(3)); ++ ++ /* According to the datasheet, RGMII delay is enabled through ++ * PORT5_PAD_CTRL for all ports, rather than individual port ++ * registers ++ */ ++ ar8xxx_write(ds, AR8327_REG_PORT5_PAD_CTRL, ++ AR8327_PORT_PAD_RGMII_RX_DELAY_EN); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ ar8xxx_write(ds, reg, AR8327_PORT_PAD_SGMII_EN); ++ break; ++ default: ++ pr_err("xMII mode %d not supported\n", mode); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ar8xxx_of_setup(struct dsa_switch *ds) ++{ ++ struct device_node *dn = ds->pd->of_node; ++ const char *s_phymode; ++ int ret, mode; ++ u32 phy_id, ctrl; ++ ++ /* If port6-phy-mode property exists, configure it accordingly */ ++ if (!of_property_read_string(dn, "qca,port6-phy-mode", &s_phymode)) { ++ for (mode = 0; mode < PHY_INTERFACE_MODE_MAX; mode++) ++ if (!strcasecmp(s_phymode, phy_modes(mode))) ++ break; ++ ++ if (mode == PHY_INTERFACE_MODE_MAX) ++ pr_err("Unknown phy-mode: \"%s\"\n", s_phymode); ++ ++ ret = ar8xxx_set_pad_ctrl(ds, 6, mode); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* If a phy ID is specified for PORT6 mac, connect them together */ ++ if (!of_property_read_u32(dn, "qca,port6-phy-id", &phy_id)) { ++ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(6), ++ AR8327_PORT_LOOKUP_MEMBER, BIT(phy_to_port(phy_id))); ++ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(phy_to_port(phy_id)), ++ AR8327_PORT_LOOKUP_MEMBER, BIT(6)); ++ ++ /* We want the switch to be pass-through and act like a PHY on ++ * these ports. So BC/MC/UC & IGMP frames need to be accepted ++ */ ++ ctrl = BIT(phy_to_port(phy_id)) | BIT(6); ++ ar8xxx_reg_set(ds, AR8327_REG_GLOBAL_FW_CTRL1, ++ ctrl << AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S | ++ ctrl << AR8327_GLOBAL_FW_CTRL1_BC_DP_S | ++ ctrl << AR8327_GLOBAL_FW_CTRL1_MC_DP_S | ++ ctrl << AR8327_GLOBAL_FW_CTRL1_UC_DP_S); ++ } ++ ++ return 0; ++} ++ ++static int ar8xxx_setup(struct dsa_switch *ds) ++{ ++ struct ar8xxx_priv *priv = ds_to_priv(ds); ++ struct net_device *netdev = ds->dst->pd->of_netdev; ++ int ret, i, phy_mode; ++ ++ /* Start by setting up the register mapping */ ++ priv->regmap = devm_regmap_init(ds->master_dev, NULL, ds, ++ &ar8xxx_regmap_config); ++ ++ if (IS_ERR(priv->regmap)) ++ pr_warn("regmap initialization failed"); ++ ++ /* Initialize CPU port pad mode (xMII type, delays...) */ ++ phy_mode = of_get_phy_mode(netdev->dev.parent->of_node); ++ if (phy_mode < 0) { ++ pr_err("Can't find phy-mode for master device\n"); ++ return phy_mode; ++ } ++ ++ ret = ar8xxx_set_pad_ctrl(ds, 0, phy_mode); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable CPU Port */ ++ ar8xxx_reg_set(ds, AR8327_REG_GLOBAL_FW_CTRL0, ++ AR8327_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ ++ /* Enable MIB counters */ ++ ar8xxx_reg_set(ds, AR8327_REG_MIB, AR8327_MIB_CPU_KEEP); ++ ar8xxx_write(ds, AR8327_REG_MODULE_EN, AR8327_MODULE_EN_MIB); ++ ++ /* Enable QCA header mode on Port 0 */ ++ ar8xxx_write(ds, AR8327_REG_PORT_HDR_CTRL(0), ++ AR8327_PORT_HDR_CTRL_ALL << AR8327_PORT_HDR_CTRL_TX_S | ++ AR8327_PORT_HDR_CTRL_ALL << AR8327_PORT_HDR_CTRL_RX_S); ++ ++ /* Disable forwarding by default on all ports */ ++ for (i = 0; i < AR8327_NUM_PORTS; i++) ++ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(i), ++ AR8327_PORT_LOOKUP_MEMBER, 0); ++ ++ /* Forward all unknown frames to CPU port for Linux processing */ ++ ar8xxx_write(ds, AR8327_REG_GLOBAL_FW_CTRL1, ++ BIT(0) << AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S | ++ BIT(0) << AR8327_GLOBAL_FW_CTRL1_BC_DP_S | ++ BIT(0) << AR8327_GLOBAL_FW_CTRL1_MC_DP_S | ++ BIT(0) << AR8327_GLOBAL_FW_CTRL1_UC_DP_S); ++ ++ /* Setup connection between CPU ports & PHYs */ ++ for (i = 0; i < DSA_MAX_PORTS; i++) { ++ /* CPU port gets connected to all PHYs in the switch */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(0), ++ AR8327_PORT_LOOKUP_MEMBER, ++ ds->phys_port_mask << 1); ++ } ++ ++ /* Invividual PHYs gets connected to CPU port only */ ++ if (ds->phys_port_mask & BIT(i)) { ++ int phy = phy_to_port(i); ++ ++ ar8xxx_rmw(ds, AR8327_PORT_LOOKUP_CTRL(phy), ++ AR8327_PORT_LOOKUP_MEMBER, BIT(0)); ++ ++ /* Disable Auto-learning by default so the switch ++ * doesn't try to forward the frame to another port ++ */ ++ ar8xxx_reg_clear(ds, AR8327_PORT_LOOKUP_CTRL(phy), ++ AR8327_PORT_LOOKUP_LEARN); ++ } ++ } ++ ++ ret = ar8xxx_of_setup(ds); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ar8xxx_set_addr(struct dsa_switch *ds, u8 *addr) ++{ ++ return 0; ++} ++ ++static int ar8xxx_phy_read(struct dsa_switch *ds, int phy, int regnum) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); ++ ++ return mdiobus_read(bus, phy, regnum); ++} ++ ++static int ++ar8xxx_phy_write(struct dsa_switch *ds, int phy, int regnum, u16 val) ++{ ++ struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); ++ ++ return mdiobus_write(bus, phy, regnum, val); ++} ++ ++static void ar8xxx_get_strings(struct dsa_switch *ds, int phy, uint8_t *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++ } ++} ++ ++static void ar8xxx_get_ethtool_stats(struct dsa_switch *ds, int phy, ++ uint64_t *data) ++{ ++ const struct ar8xxx_mib_desc *mib; ++ uint32_t reg, i, port; ++ u64 hi; ++ ++ port = phy_to_port(phy); ++ ++ for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { ++ mib = &ar8327_mib[i]; ++ reg = AR8327_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ data[i] = ar8xxx_read(ds, reg); ++ if (mib->size == 2) { ++ hi = ar8xxx_read(ds, reg + 4); ++ data[i] |= hi << 32; ++ } ++ } ++} ++ ++static int ar8xxx_get_sset_count(struct dsa_switch *ds) ++{ ++ return ARRAY_SIZE(ar8327_mib); ++} ++ ++static void ar8xxx_poll_link(struct dsa_switch *ds) ++{ ++ int i = 0; ++ struct net_device *dev; ++ ++ while ((dev = ds->ports[i++]) != NULL) { ++ u32 status; ++ int link; ++ int speed; ++ int duplex; ++ ++ status = ar8xxx_read(ds, AR8327_REG_PORT_STATUS(i)); ++ link = !!(status & AR8XXX_PORT_STATUS_LINK_UP); ++ duplex = !!(status & AR8XXX_PORT_STATUS_DUPLEX); ++ ++ switch (status & AR8XXX_PORT_STATUS_SPEED) { ++ case AR8XXX_PORT_SPEED_10M: ++ speed = 10; ++ break; ++ case AR8XXX_PORT_SPEED_100M: ++ speed = 100; ++ break; ++ case AR8XXX_PORT_SPEED_1000M: ++ speed = 1000; ++ break; ++ default: ++ speed = 0; ++ } ++ ++ if (!link) { ++ /* This poll happens every ~1s, so we don't want to ++ * print the status every time. Only when the device ++ * transitions from Link UP to Link DOWN ++ */ ++ if (netif_carrier_ok(dev)) ++ netif_carrier_off(dev); ++ continue; ++ } else { ++ /* Same thing here. But we detect a Link UP event */ ++ if (!netif_carrier_ok(dev)) ++ netif_carrier_on(dev); ++ continue; ++ } ++ } ++} ++ ++static struct dsa_switch_driver ar8xxx_switch_driver = { ++ .tag_protocol = DSA_TAG_PROTO_QCA, ++ .priv_size = sizeof(struct ar8xxx_priv), ++ .probe = ar8xxx_probe, ++ .setup = ar8xxx_setup, ++ .set_addr = ar8xxx_set_addr, ++ .poll_link = ar8xxx_poll_link, ++ .phy_read = ar8xxx_phy_read, ++ .phy_write = ar8xxx_phy_write, ++ .get_strings = ar8xxx_get_strings, ++ .get_ethtool_stats = ar8xxx_get_ethtool_stats, ++ .get_sset_count = ar8xxx_get_sset_count, ++}; ++ ++static int __init ar8xxx_init(void) ++{ ++ register_switch_driver(&ar8xxx_switch_driver); ++ return 0; ++} ++module_init(ar8xxx_init); ++ ++static void __exit ar8xxx_cleanup(void) ++{ ++ unregister_switch_driver(&ar8xxx_switch_driver); ++} ++module_exit(ar8xxx_cleanup); ++ ++MODULE_AUTHOR("Mathieu Olivari "); ++MODULE_DESCRIPTION("Driver for AR8XXX ethernet switch family"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ar8xxx"); +--- /dev/null ++++ b/drivers/net/dsa/ar8xxx.h +@@ -0,0 +1,156 @@ ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * 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 __AR8XXX_H ++#define __AR8XXX_H ++ ++#include ++#include ++ ++struct ar8xxx_priv { ++ struct regmap *regmap; ++}; ++ ++struct ar8xxx_mib_desc { ++ unsigned int size; ++ unsigned int offset; ++ const char *name; ++}; ++ ++#define AR8327_NUM_PORTS 7 ++ ++#define PHY_ID_QCA8337 0x004dd036 ++ ++#define AR8327_REG_PORT0_PAD_CTRL 0x004 ++#define AR8327_REG_PORT5_PAD_CTRL 0x008 ++#define AR8327_REG_PORT6_PAD_CTRL 0x00c ++#define AR8327_PORT_PAD_RGMII_EN BIT(26) ++#define AR8327_PORT_PAD_RGMII_TX_DELAY(x) ((0x8 + (x & 0x3)) << 22) ++#define AR8327_PORT_PAD_RGMII_RX_DELAY(x) ((0x10 + (x & 0x3)) << 20) ++#define AR8327_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) ++#define AR8327_PORT_PAD_SGMII_EN BIT(7) ++ ++#define AR8327_REG_MODULE_EN 0x030 ++#define AR8327_MODULE_EN_MIB BIT(0) ++#define AR8327_MODULE_EN_ACL BIT(1) ++#define AR8327_MODULE_EN_L3 BIT(2) ++ ++#define AR8327_REG_MIB 0x034 ++#define AR8327_MIB_CPU_KEEP BIT(20) ++ ++#define AR8327_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) ++#define AR8XXX_PORT_STATUS_SPEED GENMASK(2, 0) ++#define AR8XXX_PORT_STATUS_SPEED_S 0 ++#define AR8XXX_PORT_STATUS_TXMAC BIT(2) ++#define AR8XXX_PORT_STATUS_RXMAC BIT(3) ++#define AR8XXX_PORT_STATUS_TXFLOW BIT(4) ++#define AR8XXX_PORT_STATUS_RXFLOW BIT(5) ++#define AR8XXX_PORT_STATUS_DUPLEX BIT(6) ++#define AR8XXX_PORT_STATUS_LINK_UP BIT(8) ++#define AR8XXX_PORT_STATUS_LINK_AUTO BIT(9) ++#define AR8XXX_PORT_STATUS_LINK_PAUSE BIT(10) ++ ++#define AR8327_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) ++#define AR8327_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) ++#define AR8327_PORT_HDR_CTRL_RX_S 2 ++#define AR8327_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0) ++#define AR8327_PORT_HDR_CTRL_TX_S 0 ++#define AR8327_PORT_HDR_CTRL_ALL 2 ++#define AR8327_PORT_HDR_CTRL_MGMT 1 ++#define AR8327_PORT_HDR_CTRL_NONE 0 ++ ++#define AR8327_REG_GLOBAL_FW_CTRL0 0x620 ++#define AR8327_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) ++ ++#define AR8327_REG_GLOBAL_FW_CTRL1 0x624 ++#define AR8327_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) ++#define AR8327_GLOBAL_FW_CTRL1_IGMP_DP_S 24 ++#define AR8327_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) ++#define AR8327_GLOBAL_FW_CTRL1_BC_DP_S 16 ++#define AR8327_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8) ++#define AR8327_GLOBAL_FW_CTRL1_MC_DP_S 8 ++#define AR8327_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0) ++#define AR8327_GLOBAL_FW_CTRL1_UC_DP_S 0 ++ ++#define AR8327_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) ++#define AR8327_PORT_LOOKUP_MEMBER GENMASK(6, 0) ++#define AR8327_PORT_LOOKUP_IN_MODE GENMASK(9, 8) ++#define AR8327_PORT_LOOKUP_IN_MODE_S 8 ++#define AR8327_PORT_LOOKUP_STATE GENMASK(18, 16) ++#define AR8327_PORT_LOOKUP_STATE_S 16 ++#define AR8327_PORT_LOOKUP_LEARN BIT(20) ++#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25) ++ ++#define AR8327_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) ++ ++/* port speed */ ++enum { ++ AR8XXX_PORT_SPEED_10M = 0, ++ AR8XXX_PORT_SPEED_100M = 1, ++ AR8XXX_PORT_SPEED_1000M = 2, ++ AR8XXX_PORT_SPEED_ERR = 3, ++}; ++ ++static inline int port_to_phy(int port) ++{ ++ if (port >= 1 && port <= 6) ++ return port - 1; ++ ++ return -1; ++} ++ ++static inline int phy_to_port(int phy) ++{ ++ if (phy < 5) ++ return phy + 1; ++ ++ return -1; ++} ++ ++u32 ++ar8xxx_rmw(struct dsa_switch *ds, int reg, u32 mask, u32 val); ++ ++static inline void ++split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) ++{ ++ regaddr >>= 1; ++ *r1 = regaddr & 0x1e; ++ ++ regaddr >>= 5; ++ *r2 = regaddr & 0x7; ++ ++ regaddr >>= 3; ++ *page = regaddr & 0x1ff; ++} ++ ++static inline void ++wait_for_page_switch(void) ++{ ++ udelay(5); ++} ++ ++static inline void ++ar8xxx_reg_set(struct dsa_switch *ds, int reg, u32 val) ++{ ++ ar8xxx_rmw(ds, reg, 0, val); ++} ++ ++static inline void ++ar8xxx_reg_clear(struct dsa_switch *ds, int reg, u32 val) ++{ ++ ar8xxx_rmw(ds, reg, val, 0); ++} ++ ++#endif /* __AR8XXX_H */ +--- a/net/dsa/dsa.c ++++ b/net/dsa/dsa.c +@@ -285,6 +285,11 @@ static int dsa_switch_setup_one(struct d + dst->rcv = brcm_netdev_ops.rcv; + break; + #endif ++#ifdef CONFIG_NET_DSA_TAG_QCA ++ case DSA_TAG_PROTO_QCA: ++ dst->rcv = qca_netdev_ops.rcv; ++ break; ++#endif + case DSA_TAG_PROTO_NONE: + break; + default: +@@ -1041,6 +1046,7 @@ static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa + + static const struct of_device_id dsa_of_match_table[] = { + { .compatible = "brcm,bcm7445-switch-v4.0" }, ++ { .compatible = "qca,ar8xxx", }, + { .compatible = "marvell,dsa", }, + {} + }; +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -26,6 +26,7 @@ enum dsa_tag_protocol { + DSA_TAG_PROTO_TRAILER, + DSA_TAG_PROTO_EDSA, + DSA_TAG_PROTO_BRCM, ++ DSA_TAG_PROTO_QCA, + }; + + #define DSA_MAX_SWITCHES 4 +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -26,6 +26,9 @@ config NET_DSA_HWMON + via the hwmon sysfs interface and exposes the onboard sensors. + + # tagging formats ++config NET_DSA_TAG_QCA ++ bool ++ + config NET_DSA_TAG_BRCM + bool + +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -3,6 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o + dsa_core-y += dsa.o slave.o + + # tagging formats ++dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o + dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o + dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o + dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -78,5 +78,7 @@ extern const struct dsa_device_ops trail + /* tag_brcm.c */ + extern const struct dsa_device_ops brcm_netdev_ops; + ++/* tag_qca.c */ ++extern const struct dsa_device_ops qca_netdev_ops; + + #endif +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1180,6 +1180,11 @@ int dsa_slave_create(struct dsa_switch * + p->xmit = brcm_netdev_ops.xmit; + break; + #endif ++#ifdef CONFIG_NET_DSA_TAG_QCA ++ case DSA_TAG_PROTO_QCA: ++ p->xmit = qca_netdev_ops.xmit; ++ break; ++#endif + default: + p->xmit = dsa_slave_notag_xmit; + break; +--- /dev/null ++++ b/net/dsa/tag_qca.c +@@ -0,0 +1,158 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include "dsa_priv.h" ++ ++#define QCA_HDR_LEN 2 ++#define QCA_HDR_VERSION 0x2 ++ ++#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) ++#define QCA_HDR_RECV_VERSION_S 14 ++#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) ++#define QCA_HDR_RECV_PRIORITY_S 11 ++#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) ++#define QCA_HDR_RECV_TYPE_S 6 ++#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) ++#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) ++ ++#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) ++#define QCA_HDR_XMIT_VERSION_S 14 ++#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) ++#define QCA_HDR_XMIT_PRIORITY_S 11 ++#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) ++#define QCA_HDR_XMIT_CONTROL_S 8 ++#define QCA_HDR_XMIT_FROM_CPU BIT(7) ++#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) ++ ++static inline int reg_to_port(int reg) ++{ ++ if (reg < 5) ++ return reg + 1; ++ ++ return -1; ++} ++ ++static inline int port_to_reg(int port) ++{ ++ if (port >= 1 && port <= 6) ++ return port - 1; ++ ++ return -1; ++} ++ ++static netdev_tx_t qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ u16 *phdr, hdr; ++ ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ if (skb_cow_head(skb, 0) < 0) ++ goto out_free; ++ ++ skb_push(skb, QCA_HDR_LEN); ++ ++ memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN); ++ phdr = (u16 *)(skb->data + 2 * ETH_ALEN); ++ ++ /* Set the version field, and set destination port information */ ++ hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | ++ QCA_HDR_XMIT_FROM_CPU | ++ 1 << reg_to_port(p->port); ++ ++ *phdr = htons(hdr); ++ ++ skb->dev = p->parent->dst->master_netdev; ++ dev_queue_xmit(skb); ++ ++ return NETDEV_TX_OK; ++ ++out_free: ++ kfree_skb(skb); ++ return NETDEV_TX_OK; ++} ++ ++static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, ++ struct packet_type *pt, struct net_device *orig_dev) ++{ ++ struct dsa_switch_tree *dst = dev->dsa_ptr; ++ struct dsa_switch *ds; ++ u8 ver; ++ int port, phy; ++ __be16 *phdr, hdr; ++ ++ if (unlikely(!dst)) ++ goto out_drop; ++ ++ skb = skb_unshare(skb, GFP_ATOMIC); ++ if (!skb) ++ goto out; ++ ++ if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) ++ goto out_drop; ++ ++ /* Ethernet is added by the switch between src addr and Ethertype ++ * At this point, skb->data points to ethertype so header should be ++ * right before ++ */ ++ phdr = (__be16 *)(skb->data - 2); ++ hdr = ntohs(*phdr); ++ ++ /* Make sure the version is correct */ ++ ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; ++ if (unlikely(ver != QCA_HDR_VERSION)) ++ goto out_drop; ++ ++ /* Remove QCA tag and recalculate checksum */ ++ skb_pull_rcsum(skb, QCA_HDR_LEN); ++ memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN, ++ ETH_HLEN - QCA_HDR_LEN); ++ ++ /* This protocol doesn't support cascading multiple switches so it's ++ * safe to assume the switch is first in the tree ++ */ ++ ds = dst->ds[0]; ++ if (!ds) ++ goto out_drop; ++ ++ /* Get source port information */ ++ port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); ++ phy = port_to_reg(port); ++ if (unlikely(phy < 0) || !ds->ports[phy]) ++ goto out_drop; ++ ++ /* Update skb & forward the frame accordingly */ ++ skb_push(skb, ETH_HLEN); ++ skb->pkt_type = PACKET_HOST; ++ skb->dev = ds->ports[phy]; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ ++ skb->dev->stats.rx_packets++; ++ skb->dev->stats.rx_bytes += skb->len; ++ ++ netif_receive_skb(skb); ++ ++ return 0; ++ ++out_drop: ++ kfree_skb(skb); ++out: ++ return 0; ++} ++ ++const struct dsa_device_ops qca_netdev_ops = { ++ .xmit = qca_tag_xmit, ++ .rcv = qca_tag_rcv, ++}; +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/dsa/qca-ar8xxx.txt +@@ -0,0 +1,70 @@ ++* Qualcomm Atheros AR8xxx switch family ++ ++Required properties: ++ ++- compatible: should be "qca,ar8xxx" ++- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt ++- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt ++- #size-cells: must be 0 ++- #address-cells: must be 2, see dsa/dsa.txt ++ ++Subnodes: ++ ++The integrated switch subnode should be specified according to the binding ++described in dsa/dsa.txt. ++ ++Optional properties: ++ ++- qca,port6-phy-mode: if specified, the driver will configure Port 6 in the ++ given phy-mode. See Documentation/devicetree/bindings/net/ethernet.txt for ++ the list of valid phy-mode. ++ ++- qca,port6-phy-id: if specified, the driver will connect Port 6 to the PHY ++ given as a parameter. In this case, Port6 and the corresponding PHY will be ++ isolated from the rest of the switch. From a system perspective, they will ++ act as a regular PHY. ++ ++Example: ++ ++ dsa@0 { ++ compatible = "qca,ar8xxx"; ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ dsa,ethernet = <ðernet0>; ++ dsa,mii-bus = <&mii_bus0>; ++ ++ switch@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0 0>; /* MDIO address 0, switch 0 in tree */ ++ ++ qca,port6-phy-mode = "sgmii"; ++ qca,port6-phy-id = <4>; ++ ++ port@0 { ++ reg = <11>; ++ label = "cpu"; ++ }; ++ ++ port@1 { ++ reg = <0>; ++ label = "lan1"; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ label = "lan2"; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ label = "lan3"; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ label = "lan4"; ++ }; ++ }; ++ }; diff --git a/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch new file mode 100644 index 000000000..102cd76b4 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch @@ -0,0 +1,146 @@ +From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Mon, 11 May 2015 16:32:09 -0700 +Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ + 2 files changed, 85 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -19,8 +19,9 @@ + }; + }; + +- alias { ++ aliases { + serial0 = &uart4; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -65,6 +66,15 @@ + bias-bus-hold; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -160,6 +170,34 @@ + + linux,part-probe = "qcom-smem"; + }; ++ ++ mdio0: mdio { ++ compatible = "virtual,mdio-gpio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; ++ pinctrl-0 = <&mdio0_pins>; ++ pinctrl-names = "default"; ++ ++ phy0: ethernet-phy@0 { ++ device_type = "ethernet-phy"; ++ reg = <0>; ++ qca,ar8327-initvals = < ++ 0x00004 0x7600000 /* PAD0_MODE */ ++ 0x00008 0x1000000 /* PAD5_MODE */ ++ 0x0000c 0x80 /* PAD6_MODE */ ++ 0x000e4 0x6a545 /* MAC_POWER_SEL */ ++ 0x000e0 0xc74164de /* SGMII_CTRL */ ++ 0x0007c 0x4e /* PORT0_STATUS */ ++ 0x00094 0x4e /* PORT6_STATUS */ ++ >; ++ }; ++ ++ phy4: ethernet-phy@4 { ++ device_type = "ethernet-phy"; ++ reg = <4>; ++ }; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -16,6 +16,7 @@ + + alias { + serial0 = &uart2; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -38,6 +39,15 @@ + bias-none; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -140,5 +150,44 @@ + pcie2: pci@1b900000 { + status = "ok"; + }; ++ ++ mdio0: mdio { ++ compatible = "virtual,mdio-gpio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; ++ ++ pinctrl-0 = <&mdio0_pins>; ++ pinctrl-names = "default"; ++ ++ phy0: ethernet-phy@0 { ++ device_type = "ethernet-phy"; ++ reg = <0>; ++ qca,ar8327-initvals = < ++ 0x00004 0x7600000 /* PAD0_MODE */ ++ 0x00008 0x1000000 /* PAD5_MODE */ ++ 0x0000c 0x80 /* PAD6_MODE */ ++ 0x000e4 0x6a545 /* MAC_POWER_SEL */ ++ 0x000e0 0xc74164de /* SGMII_CTRL */ ++ 0x0007c 0x4e /* PORT0_STATUS */ ++ 0x00094 0x4e /* PORT6_STATUS */ ++ >; ++ }; ++ ++ phy4: ethernet-phy@4 { ++ device_type = "ethernet-phy"; ++ reg = <4>; ++ }; ++ ++ phy6: ethernet-phy@6 { ++ device_type = "ethernet-phy"; ++ reg = <6>; ++ }; ++ ++ phy7: ethernet-phy@7 { ++ device_type = "ethernet-phy"; ++ reg = <7>; ++ }; ++ }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch new file mode 100644 index 000000000..b722dfa25 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,216 @@ +From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Mon, 11 May 2015 12:29:18 -0700 +Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ + 3 files changed, 160 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -75,6 +75,16 @@ + bias-disable; + }; + }; ++ ++ rgmii2_pins: rgmii2_pins { ++ mux { ++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; ++ function = "rgmii2"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -198,6 +208,31 @@ + reg = <4>; + }; + }; ++ ++ gmac1: ethernet@37200000 { ++ status = "ok"; ++ phy-mode = "rgmii"; ++ qcom,id = <1>; ++ ++ pinctrl-0 = <&rgmii2_pins>; ++ pinctrl-names = "default"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <2>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -48,6 +48,14 @@ + bias-disable; + }; + }; ++ ++ rgmii0_pins: rgmii0_pins { ++ mux { ++ pins = "gpio2", "gpio66"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -189,5 +197,40 @@ + reg = <7>; + }; + }; ++ ++ gmac0: ethernet@37000000 { ++ status = "ok"; ++ phy-mode = "rgmii"; ++ qcom,id = <0>; ++ phy-handle = <&phy4>; ++ ++ pinctrl-0 = <&rgmii0_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <1>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <2>; ++ phy-handle = <&phy6>; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <3>; ++ phy-handle = <&phy7>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -917,6 +917,92 @@ + + status = "disabled"; + }; ++ ++ nss_common: syscon@03000000 { ++ compatible = "syscon"; ++ reg = <0x03000000 0x0000FFFF>; ++ }; ++ ++ qsgmii_csr: syscon@1bb00000 { ++ compatible = "syscon"; ++ reg = <0x1bb00000 0x000001FF>; ++ }; ++ ++ gmac0: ethernet@37000000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37000000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE1_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE1_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37200000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE2_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE2_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37400000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE3_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE3_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37600000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE4_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE4_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch new file mode 100644 index 000000000..fa78a0aed --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch @@ -0,0 +1,129 @@ +From 16d2871830ff3fe12a6bff582549a9264adff278 Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +Date: Tue, 10 May 2016 20:19:31 +0530 +Subject: [PATCH] spi: qup: Fix fifo and dma support for IPQ806x + +Signed-off-by: Ram Chandra Jangir +--- + drivers/spi/spi-qup.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 52 insertions(+), 2 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #define QUP_CONFIG 0x0000 + #define QUP_STATE 0x0004 +@@ -152,6 +153,7 @@ struct spi_qup { + int use_dma; + struct dma_slave_config rx_conf; + struct dma_slave_config tx_conf; ++ int mode; + }; + + +@@ -370,7 +372,8 @@ static int spi_qup_do_pio(struct spi_mas + return ret; + } + +- spi_qup_fifo_write(qup, xfer); ++ if (qup->mode == QUP_IO_M_MODE_FIFO) ++ spi_qup_fifo_write(qup, xfer); + + return 0; + } +@@ -448,6 +451,7 @@ spi_qup_get_mode(struct spi_master *mast + { + struct spi_qup *qup = spi_master_get_devdata(master); + u32 mode; ++ size_t dma_align = dma_get_cache_alignment(); + + qup->w_size = 4; + +@@ -458,6 +462,14 @@ spi_qup_get_mode(struct spi_master *mast + + qup->n_words = xfer->len / qup->w_size; + ++ if (!IS_ERR_OR_NULL(master->dma_rx) && ++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && ++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && ++ !is_vmalloc_addr(xfer->tx_buf) && ++ !is_vmalloc_addr(xfer->rx_buf) && ++ (xfer->len > 3*qup->in_blk_sz)) ++ qup->use_dma = 1; ++ + if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) + mode = QUP_IO_M_MODE_FIFO; + else +@@ -491,7 +503,7 @@ static int spi_qup_io_config(struct spi_ + return -EIO; + } + +- mode = spi_qup_get_mode(spi->master, xfer); ++ controller->mode = mode = spi_qup_get_mode(spi->master, xfer); + n_words = controller->n_words; + + if (mode == QUP_IO_M_MODE_FIFO) { +@@ -500,6 +512,7 @@ static int spi_qup_io_config(struct spi_ + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); ++ controller->use_dma = 0; + } else if (!controller->use_dma) { + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); +@@ -750,6 +763,38 @@ err_tx: + return ret; + } + ++static void spi_qup_set_cs(struct spi_device *spi, bool val) ++{ ++ struct spi_qup *controller; ++ u32 spi_ioc; ++ u32 spi_ioc_orig; ++ ++ controller = spi_master_get_devdata(spi->master); ++ spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL); ++ spi_ioc_orig = spi_ioc; ++ if (!val) ++ spi_ioc |= SPI_IO_C_FORCE_CS; ++ else ++ spi_ioc &= ~SPI_IO_C_FORCE_CS; ++ ++ if (spi_ioc != spi_ioc_orig) ++ writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL); ++} ++ ++static int spi_qup_setup(struct spi_device *spi) ++{ ++ if (spi->cs_gpio >= 0) { ++ if (spi->mode & SPI_CS_HIGH) ++ gpio_set_value(spi->cs_gpio, 0); ++ else ++ gpio_set_value(spi->cs_gpio, 1); ++ ++ udelay(10); ++ } ++ ++ return 0; ++} ++ + static int spi_qup_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -846,6 +891,11 @@ static int spi_qup_probe(struct platform + if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) + controller->qup_v1 = 1; + ++ if (!controller->qup_v1) ++ master->set_cs = spi_qup_set_cs; ++ else ++ master->setup = spi_qup_setup; ++ + spin_lock_init(&controller->lock); + init_completion(&controller->done); + diff --git a/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch new file mode 100644 index 000000000..338c3a0c2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch @@ -0,0 +1,220 @@ +From 93f99afbc534e00d72d58336061823055ee820f1 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Tue, 12 Apr 2016 09:11:47 -0500 +Subject: [PATCH] spi: qup: Make sure mode is only determined once + +This patch calculates the mode once. All decisions on the current +transaction +is made using the mode instead of use_dma + +Signed-off-by: Andy Gross + +Change-Id: If3cdd924355e037d77dc8201a72895fac0461aa5 +--- + drivers/spi/spi-qup.c | 96 +++++++++++++++++++-------------------------------- + 1 file changed, 36 insertions(+), 60 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -150,13 +150,20 @@ struct spi_qup { + int rx_bytes; + int qup_v1; + +- int use_dma; ++ int mode; + struct dma_slave_config rx_conf; + struct dma_slave_config tx_conf; +- int mode; + }; + + ++static inline bool spi_qup_is_dma_xfer(int mode) ++{ ++ if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM) ++ return true; ++ ++ return false; ++} ++ + static inline bool spi_qup_is_valid_state(struct spi_qup *controller) + { + u32 opstate = readl_relaxed(controller->base + QUP_STATE); +@@ -427,7 +434,7 @@ static irqreturn_t spi_qup_qup_irq(int i + error = -EIO; + } + +- if (!controller->use_dma) { ++ if (!spi_qup_is_dma_xfer(controller->mode)) { + if (opflags & QUP_OP_IN_SERVICE_FLAG) + spi_qup_fifo_read(controller, xfer); + +@@ -446,43 +453,11 @@ static irqreturn_t spi_qup_qup_irq(int i + return IRQ_HANDLED; + } + +-static u32 +-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer) +-{ +- struct spi_qup *qup = spi_master_get_devdata(master); +- u32 mode; +- size_t dma_align = dma_get_cache_alignment(); +- +- qup->w_size = 4; +- +- if (xfer->bits_per_word <= 8) +- qup->w_size = 1; +- else if (xfer->bits_per_word <= 16) +- qup->w_size = 2; +- +- qup->n_words = xfer->len / qup->w_size; +- +- if (!IS_ERR_OR_NULL(master->dma_rx) && +- IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && +- IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && +- !is_vmalloc_addr(xfer->tx_buf) && +- !is_vmalloc_addr(xfer->rx_buf) && +- (xfer->len > 3*qup->in_blk_sz)) +- qup->use_dma = 1; +- +- if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) +- mode = QUP_IO_M_MODE_FIFO; +- else +- mode = QUP_IO_M_MODE_BLOCK; +- +- return mode; +-} +- + /* set clock freq ... bits per word */ + static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + { + struct spi_qup *controller = spi_master_get_devdata(spi->master); +- u32 config, iomode, mode, control; ++ u32 config, iomode, control; + int ret, n_words; + + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { +@@ -503,24 +478,22 @@ static int spi_qup_io_config(struct spi_ + return -EIO; + } + +- controller->mode = mode = spi_qup_get_mode(spi->master, xfer); ++ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8); ++ controller->n_words = xfer->len / controller->w_size; + n_words = controller->n_words; + +- if (mode == QUP_IO_M_MODE_FIFO) { ++ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { ++ controller->mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + controller->use_dma = 0; +- } else if (!controller->use_dma) { +- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); +- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); +- /* must be zero for BLOCK and BAM */ +- writel_relaxed(0, controller->base + QUP_MX_READ_CNT); +- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); +- } else { +- mode = QUP_IO_M_MODE_BAM; ++ } else if (spi->master->can_dma && ++ spi->master->can_dma(spi->master, spi, xfer) && ++ spi->master->cur_msg_mapped) { ++ controller->mode = QUP_IO_M_MODE_BAM; + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + +@@ -541,19 +514,26 @@ static int spi_qup_io_config(struct spi_ + + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + } ++ } else { ++ controller->mode = QUP_IO_M_MODE_BLOCK; ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ /* must be zero for BLOCK and BAM */ ++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + } + + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); + /* Set input and output transfer mode */ + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); + +- if (!controller->use_dma) ++ if (!spi_qup_is_dma_xfer(controller->mode)) + iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); + else + iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; + +- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); +- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); ++ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); ++ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + + writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + +@@ -594,7 +574,7 @@ static int spi_qup_io_config(struct spi_ + config |= xfer->bits_per_word - 1; + config |= QUP_CONFIG_SPI_MODE; + +- if (controller->use_dma) { ++ if (spi_qup_is_dma_xfer(controller->mode)) { + if (!xfer->tx_buf) + config |= QUP_CONFIG_NO_OUTPUT; + if (!xfer->rx_buf) +@@ -612,7 +592,7 @@ static int spi_qup_io_config(struct spi_ + * status change in BAM mode + */ + +- if (mode == QUP_IO_M_MODE_BAM) ++ if (spi_qup_is_dma_xfer(controller->mode)) + mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG; + + writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK); +@@ -646,7 +626,7 @@ static int spi_qup_transfer_one(struct s + controller->tx_bytes = 0; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (controller->use_dma) ++ if (spi_qup_is_dma_xfer(controller->mode)) + ret = spi_qup_do_dma(master, xfer); + else + ret = spi_qup_do_pio(master, xfer); +@@ -670,7 +650,7 @@ exit: + ret = controller->error; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (ret && controller->use_dma) ++ if (ret && spi_qup_is_dma_xfer(controller->mode)) + spi_qup_dma_terminate(master, xfer); + + return ret; +@@ -681,9 +661,7 @@ static bool spi_qup_can_dma(struct spi_m + { + struct spi_qup *qup = spi_master_get_devdata(master); + size_t dma_align = dma_get_cache_alignment(); +- u32 mode; +- +- qup->use_dma = 0; ++ int n_words; + + if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || + IS_ERR_OR_NULL(master->dma_rx) || +@@ -695,12 +673,10 @@ static bool spi_qup_can_dma(struct spi_m + !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) + return false; + +- mode = spi_qup_get_mode(master, xfer); +- if (mode == QUP_IO_M_MODE_FIFO) ++ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8); ++ if (n_words <= (qup->in_fifo_sz / sizeof(u32))) + return false; + +- qup->use_dma = 1; +- + return true; + } + diff --git a/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch new file mode 100644 index 000000000..99f455ef3 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch @@ -0,0 +1,30 @@ +From 8e830bd17e945e74964a5b61353d74e34c0791cd Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Fri, 29 Jan 2016 22:06:50 -0600 +Subject: [PATCH] spi: qup: Fix transaction done signaling + +Wait to signal done until we get all of the interrupts we are expecting +to get for a transaction. If we don't wait for the input done flag, we +can be inbetween transactions when the done flag comes in and this can +mess up the next transaction. + +Change-Id: I08d78376e71590663158d6434a3fb7c0623264c9 +CC: Grant Grundler +CC: Sarthak Kukreti +Signed-off-by: Andy Gross +--- + drivers/spi/spi-qup.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -447,7 +447,8 @@ static irqreturn_t spi_qup_qup_irq(int i + controller->xfer = xfer; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (controller->rx_bytes == xfer->len || error) ++ if ((controller->rx_bytes == xfer->len && ++ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) + complete(&controller->done); + + return IRQ_HANDLED; diff --git a/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch b/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch new file mode 100644 index 000000000..58400314c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/711-stmmac-fix-ipq806x-DMA-configuration.patch @@ -0,0 +1,117 @@ +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -259,6 +259,7 @@ static int ipq806x_gmac_probe(struct pla + { + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; ++ struct stmmac_dma_cfg *dma_cfg; + struct device *dev = &pdev->dev; + struct ipq806x_gmac *gmac; + int val; +@@ -348,6 +349,17 @@ static int ipq806x_gmac_probe(struct pla + plat_dat->bsp_priv = gmac; + plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; + ++ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), ++ GFP_KERNEL); ++ ++ dma_cfg->pbl = 32; ++ dma_cfg->aal = 1; ++ dma_cfg->burst_len = DMA_AXI_BLEN_16 | ++ (7 << DMA_AXI_RD_OSR_LMT_SHIFT) | ++ (7 << DMA_AXI_WR_OSR_LMT_SHIFT); ++ ++ plat_dat->dma_cfg = dma_cfg; ++ + return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + } + +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -73,6 +73,9 @@ + | DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \ + | DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256) + ++#define DMA_AXI_RD_OSR_LMT_SHIFT 16 ++#define DMA_AXI_WR_OSR_LMT_SHIFT 20 ++ + /* Platfrom data for platform device structure's platform_data field */ + + struct stmmac_mdio_bus_data { +@@ -88,6 +91,7 @@ struct stmmac_mdio_bus_data { + + struct stmmac_dma_cfg { + int pbl; ++ int aal; + int fixed_burst; + int mixed_burst; + int burst_len; +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +@@ -31,7 +31,8 @@ + #include "dwmac_dma.h" + + static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, +- int burst_len, u32 dma_tx, u32 dma_rx, int atds) ++ int burst_len, u32 dma_tx, u32 dma_rx, int atds, ++ int aal) + { + u32 value = readl(ioaddr + DMA_BUS_MODE); + int limit; +@@ -62,6 +63,10 @@ static int dwmac1000_dma_init(void __iom + value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | + (pbl << DMA_BUS_MODE_RPBL_SHIFT)); + ++ /* Address Aligned Beats */ ++ if (aal) ++ value |= DMA_BUS_MODE_AAL; ++ + /* Set the Fixed burst mode */ + if (fb) + value |= DMA_BUS_MODE_FB; +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -352,7 +352,7 @@ extern const struct stmmac_desc_ops ndes + struct stmmac_dma_ops { + /* DMA core initialization */ + int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, +- int burst_len, u32 dma_tx, u32 dma_rx, int atds); ++ int burst_len, u32 dma_tx, u32 dma_rx, int atds, int aal); + /* Dump DMA registers */ + void (*dump_regs) (void __iomem *ioaddr); + /* Set tx/rx threshold in the csr6 register +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +@@ -33,7 +33,8 @@ + #include "dwmac_dma.h" + + static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, +- int burst_len, u32 dma_tx, u32 dma_rx, int atds) ++ int burst_len, u32 dma_tx, u32 dma_rx, int atds, ++ int aal) + { + u32 value = readl(ioaddr + DMA_BUS_MODE); + int limit; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1641,9 +1641,11 @@ static int stmmac_init_dma_engine(struct + int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0; + int mixed_burst = 0; + int atds = 0; ++ int aal = 0; + + if (priv->plat->dma_cfg) { + pbl = priv->plat->dma_cfg->pbl; ++ aal = priv->plat->dma_cfg->aal; + fixed_burst = priv->plat->dma_cfg->fixed_burst; + mixed_burst = priv->plat->dma_cfg->mixed_burst; + burst_len = priv->plat->dma_cfg->burst_len; +@@ -1654,7 +1656,7 @@ static int stmmac_init_dma_engine(struct + + return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst, + burst_len, priv->dma_tx_phy, +- priv->dma_rx_phy, atds); ++ priv->dma_rx_phy, atds, aal); + } + + /** diff --git a/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch new file mode 100644 index 000000000..9c7bbeabd --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch @@ -0,0 +1,221 @@ +From ed56e6322b067a898a25bda1774eb1180a832246 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Tue, 2 Feb 2016 17:00:53 -0600 +Subject: [PATCH] spi: qup: Fix DMA mode to work correctly + +This patch fixes a few issues with the DMA mode. The QUP needs to be +placed in the run mode before the DMA transactions are executed. The +conditions for being able to DMA vary between revisions of the QUP. +This is due to v1.1.1 using ADM DMA and later revisions using BAM. + +Change-Id: Ib1f876eaa05d079e0bca4358d2b25b2940986089 +Signed-off-by: Andy Gross +--- + drivers/spi/spi-qup.c | 95 ++++++++++++++++++++++++++++++++++----------------- + 1 file changed, 63 insertions(+), 32 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -143,6 +143,7 @@ struct spi_qup { + + struct spi_transfer *xfer; + struct completion done; ++ struct completion dma_tx_done; + int error; + int w_size; /* bytes per SPI word */ + int n_words; +@@ -285,16 +286,16 @@ static void spi_qup_fifo_write(struct sp + + static void spi_qup_dma_done(void *data) + { +- struct spi_qup *qup = data; ++ struct completion *done = data; + +- complete(&qup->done); ++ complete(done); + } + + static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, + enum dma_transfer_direction dir, +- dma_async_tx_callback callback) ++ dma_async_tx_callback callback, ++ void *data) + { +- struct spi_qup *qup = spi_master_get_devdata(master); + unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; +@@ -313,11 +314,11 @@ static int spi_qup_prep_sg(struct spi_ma + } + + desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); +- if (!desc) +- return -EINVAL; ++ if (IS_ERR_OR_NULL(desc)) ++ return desc ? PTR_ERR(desc) : -EINVAL; + + desc->callback = callback; +- desc->callback_param = qup; ++ desc->callback_param = data; + + cookie = dmaengine_submit(desc); + +@@ -333,18 +334,29 @@ static void spi_qup_dma_terminate(struct + dmaengine_terminate_all(master->dma_rx); + } + +-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer) ++static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer, ++unsigned long timeout) + { ++ struct spi_qup *qup = spi_master_get_devdata(master); + dma_async_tx_callback rx_done = NULL, tx_done = NULL; + int ret; + ++ /* before issuing the descriptors, set the QUP to run */ ++ ret = spi_qup_set_state(qup, QUP_STATE_RUN); ++ if (ret) { ++ dev_warn(qup->dev, "cannot set RUN state\n"); ++ return ret; ++ } ++ + if (xfer->rx_buf) + rx_done = spi_qup_dma_done; +- else if (xfer->tx_buf) ++ ++ if (xfer->tx_buf) + tx_done = spi_qup_dma_done; + + if (xfer->rx_buf) { +- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done); ++ ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, ++ &qup->done); + if (ret) + return ret; + +@@ -352,17 +364,26 @@ static int spi_qup_do_dma(struct spi_mas + } + + if (xfer->tx_buf) { +- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done); ++ ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done, ++ &qup->dma_tx_done); + if (ret) + return ret; + + dma_async_issue_pending(master->dma_tx); + } + +- return 0; ++ if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout)) ++ return -ETIMEDOUT; ++ ++ if (xfer->tx_buf && ++ !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) ++ ret = -ETIMEDOUT; ++ ++ return ret; + } + +-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) ++static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer, ++ unsigned long timeout) + { + struct spi_qup *qup = spi_master_get_devdata(master); + int ret; +@@ -382,6 +403,15 @@ static int spi_qup_do_pio(struct spi_mas + if (qup->mode == QUP_IO_M_MODE_FIFO) + spi_qup_fifo_write(qup, xfer); + ++ ret = spi_qup_set_state(qup, QUP_STATE_RUN); ++ if (ret) { ++ dev_warn(qup->dev, "cannot set RUN state\n"); ++ return ret; ++ } ++ ++ if (!wait_for_completion_timeout(&qup->done, timeout)) ++ return -ETIMEDOUT; ++ + return 0; + } + +@@ -430,7 +460,6 @@ static irqreturn_t spi_qup_qup_irq(int i + dev_warn(controller->dev, "CLK_OVER_RUN\n"); + if (spi_err & SPI_ERROR_CLK_UNDER_RUN) + dev_warn(controller->dev, "CLK_UNDER_RUN\n"); +- + error = -EIO; + } + +@@ -619,6 +648,7 @@ static int spi_qup_transfer_one(struct s + timeout = 100 * msecs_to_jiffies(timeout); + + reinit_completion(&controller->done); ++ reinit_completion(&controller->dma_tx_done); + + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = xfer; +@@ -628,21 +658,13 @@ static int spi_qup_transfer_one(struct s + spin_unlock_irqrestore(&controller->lock, flags); + + if (spi_qup_is_dma_xfer(controller->mode)) +- ret = spi_qup_do_dma(master, xfer); ++ ret = spi_qup_do_dma(master, xfer, timeout); + else +- ret = spi_qup_do_pio(master, xfer); ++ ret = spi_qup_do_pio(master, xfer, timeout); + + if (ret) + goto exit; + +- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { +- dev_warn(controller->dev, "cannot set EXECUTE state\n"); +- goto exit; +- } +- +- if (!wait_for_completion_timeout(&controller->done, timeout)) +- ret = -ETIMEDOUT; +- + exit: + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); +@@ -664,15 +686,23 @@ static bool spi_qup_can_dma(struct spi_m + size_t dma_align = dma_get_cache_alignment(); + int n_words; + +- if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || +- IS_ERR_OR_NULL(master->dma_rx) || +- !IS_ALIGNED((size_t)xfer->rx_buf, dma_align))) +- return false; ++ if (xfer->rx_buf) { ++ if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) || ++ IS_ERR_OR_NULL(master->dma_rx)) ++ return false; + +- if (xfer->tx_buf && (xfer->len % qup->out_blk_sz || +- IS_ERR_OR_NULL(master->dma_tx) || +- !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) +- return false; ++ if (qup->qup_v1 && (xfer->len % qup->in_blk_sz)) ++ return false; ++ } ++ ++ if (xfer->tx_buf) { ++ if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) || ++ IS_ERR_OR_NULL(master->dma_tx)) ++ return false; ++ ++ if (qup->qup_v1 && (xfer->len % qup->out_blk_sz)) ++ return false; ++ } + + n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8); + if (n_words <= (qup->in_fifo_sz / sizeof(u32))) +@@ -875,6 +905,7 @@ static int spi_qup_probe(struct platform + + spin_lock_init(&controller->lock); + init_completion(&controller->done); ++ init_completion(&controller->dma_tx_done); + + iomode = readl_relaxed(base + QUP_IO_M_MODES); + diff --git a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch new file mode 100644 index 000000000..eb0b45cd1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch @@ -0,0 +1,312 @@ +From 148f77310a9ddf4db5036066458d7aed92cea9ae Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Sun, 31 Jan 2016 21:28:13 -0600 +Subject: [PATCH] spi: qup: Fix block mode to work correctly + +This patch corrects the behavior of the BLOCK +transactions. During block transactions, the controller +must be read/written to in block size transactions. + +Signed-off-by: Andy Gross + +Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf +--- + drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 141 insertions(+), 40 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -83,6 +83,8 @@ + #define QUP_IO_M_MODE_BAM 3 + + /* QUP_OPERATIONAL fields */ ++#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) ++#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) + #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) + #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) + #define QUP_OP_IN_SERVICE_FLAG BIT(9) +@@ -156,6 +158,12 @@ struct spi_qup { + struct dma_slave_config tx_conf; + }; + ++static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag) ++{ ++ u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ ++ return opflag & flag; ++} + + static inline bool spi_qup_is_dma_xfer(int mode) + { +@@ -217,29 +225,26 @@ static int spi_qup_set_state(struct spi_ + return 0; + } + +-static void spi_qup_fifo_read(struct spi_qup *controller, +- struct spi_transfer *xfer) ++static void spi_qup_read_from_fifo(struct spi_qup *controller, ++ struct spi_transfer *xfer, u32 num_words) + { + u8 *rx_buf = xfer->rx_buf; +- u32 word, state; +- int idx, shift, w_size; +- +- w_size = controller->w_size; ++ int i, shift, num_bytes; ++ u32 word; + +- while (controller->rx_bytes < xfer->len) { +- +- state = readl_relaxed(controller->base + QUP_OPERATIONAL); +- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) +- break; ++ for (; num_words; num_words--) { + + word = readl_relaxed(controller->base + QUP_INPUT_FIFO); + ++ num_bytes = min_t(int, xfer->len - controller->rx_bytes, ++ controller->w_size); ++ + if (!rx_buf) { +- controller->rx_bytes += w_size; ++ controller->rx_bytes += num_bytes; + continue; + } + +- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { ++ for (i = 0; i < num_bytes; i++, controller->rx_bytes++) { + /* + * The data format depends on bytes per SPI word: + * 4 bytes: 0x12345678 +@@ -247,38 +252,80 @@ static void spi_qup_fifo_read(struct spi + * 1 byte : 0x00000012 + */ + shift = BITS_PER_BYTE; +- shift *= (w_size - idx - 1); ++ shift *= (controller->w_size - i - 1); + rx_buf[controller->rx_bytes] = word >> shift; + } + } + } + +-static void spi_qup_fifo_write(struct spi_qup *controller, ++static void spi_qup_read(struct spi_qup *controller, + struct spi_transfer *xfer) + { +- const u8 *tx_buf = xfer->tx_buf; +- u32 word, state, data; +- int idx, w_size; ++ u32 remainder, words_per_block, num_words; ++ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; ++ ++ remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes, ++ controller->w_size); ++ words_per_block = controller->in_blk_sz >> 2; ++ ++ do { ++ /* ACK by clearing service flag */ ++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++ if (is_block_mode) { ++ num_words = (remainder > words_per_block) ? ++ words_per_block : remainder; ++ } else { ++ if (!spi_qup_is_flag_set(controller, ++ QUP_OP_IN_FIFO_NOT_EMPTY)) ++ break; + +- w_size = controller->w_size; ++ num_words = 1; ++ } ++ ++ /* read up to the maximum transfer size available */ ++ spi_qup_read_from_fifo(controller, xfer, num_words); + +- while (controller->tx_bytes < xfer->len) { ++ remainder -= num_words; + +- state = readl_relaxed(controller->base + QUP_OPERATIONAL); +- if (state & QUP_OP_OUT_FIFO_FULL) ++ /* if block mode, check to see if next block is available */ ++ if (is_block_mode && !spi_qup_is_flag_set(controller, ++ QUP_OP_IN_BLOCK_READ_REQ)) + break; + ++ } while (remainder); ++ ++ /* ++ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block ++ * mode reads, it has to be cleared again at the very end ++ */ ++ if (is_block_mode && spi_qup_is_flag_set(controller, ++ QUP_OP_MAX_INPUT_DONE_FLAG)) ++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++} ++ ++static void spi_qup_write_to_fifo(struct spi_qup *controller, ++ struct spi_transfer *xfer, u32 num_words) ++{ ++ const u8 *tx_buf = xfer->tx_buf; ++ int i, num_bytes; ++ u32 word, data; ++ ++ for (; num_words; num_words--) { + word = 0; +- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { + +- if (!tx_buf) { +- controller->tx_bytes += w_size; +- break; ++ num_bytes = min_t(int, xfer->len - controller->tx_bytes, ++ controller->w_size); ++ if (tx_buf) ++ for (i = 0; i < num_bytes; i++) { ++ data = tx_buf[controller->tx_bytes + i]; ++ word |= data << (BITS_PER_BYTE * (3 - i)); + } + +- data = tx_buf[controller->tx_bytes]; +- word |= data << (BITS_PER_BYTE * (3 - idx)); +- } ++ controller->tx_bytes += num_bytes; + + writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); + } +@@ -291,6 +338,44 @@ static void spi_qup_dma_done(void *data) + complete(done); + } + ++static void spi_qup_write(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; ++ u32 remainder, words_per_block, num_words; ++ ++ remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes, ++ controller->w_size); ++ words_per_block = controller->out_blk_sz >> 2; ++ ++ do { ++ /* ACK by clearing service flag */ ++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++ if (is_block_mode) { ++ num_words = (remainder > words_per_block) ? ++ words_per_block : remainder; ++ } else { ++ if (spi_qup_is_flag_set(controller, ++ QUP_OP_OUT_FIFO_FULL)) ++ break; ++ ++ num_words = 1; ++ } ++ ++ spi_qup_write_to_fifo(controller, xfer, num_words); ++ ++ remainder -= num_words; ++ ++ /* if block mode, check to see if next block is available */ ++ if (is_block_mode && !spi_qup_is_flag_set(controller, ++ QUP_OP_OUT_BLOCK_WRITE_REQ)) ++ break; ++ ++ } while (remainder); ++} ++ + static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, + enum dma_transfer_direction dir, + dma_async_tx_callback callback, +@@ -348,11 +433,13 @@ unsigned long timeout) + return ret; + } + +- if (xfer->rx_buf) +- rx_done = spi_qup_dma_done; ++ if (!qup->qup_v1) { ++ if (xfer->rx_buf) ++ rx_done = spi_qup_dma_done; + +- if (xfer->tx_buf) +- tx_done = spi_qup_dma_done; ++ if (xfer->tx_buf) ++ tx_done = spi_qup_dma_done; ++ } + + if (xfer->rx_buf) { + ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, +@@ -401,7 +488,7 @@ static int spi_qup_do_pio(struct spi_mas + } + + if (qup->mode == QUP_IO_M_MODE_FIFO) +- spi_qup_fifo_write(qup, xfer); ++ spi_qup_write(qup, xfer); + + ret = spi_qup_set_state(qup, QUP_STATE_RUN); + if (ret) { +@@ -434,10 +521,11 @@ static irqreturn_t spi_qup_qup_irq(int i + + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); +- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { +- dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", ++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); ++ dev_err_ratelimited(controller->dev, ++ "unexpected irq %08x %08x %08x\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; + } +@@ -463,12 +551,20 @@ static irqreturn_t spi_qup_qup_irq(int i + error = -EIO; + } + +- if (!spi_qup_is_dma_xfer(controller->mode)) { ++ if (spi_qup_is_dma_xfer(controller->mode)) { ++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); ++ if (opflags & QUP_OP_IN_SERVICE_FLAG && ++ opflags & QUP_OP_MAX_INPUT_DONE_FLAG) ++ complete(&controller->done); ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG && ++ opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG) ++ complete(&controller->dma_tx_done); ++ } else { + if (opflags & QUP_OP_IN_SERVICE_FLAG) +- spi_qup_fifo_read(controller, xfer); ++ spi_qup_read(controller, xfer); + + if (opflags & QUP_OP_OUT_SERVICE_FLAG) +- spi_qup_fifo_write(controller, xfer); ++ spi_qup_write(controller, xfer); + } + + spin_lock_irqsave(&controller->lock, flags); +@@ -476,6 +572,9 @@ static irqreturn_t spi_qup_qup_irq(int i + controller->xfer = xfer; + spin_unlock_irqrestore(&controller->lock, flags); + ++ /* re-read opflags as flags may have changed due to actions above */ ++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ + if ((controller->rx_bytes == xfer->len && + (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) + complete(&controller->done); +@@ -519,11 +618,13 @@ static int spi_qup_io_config(struct spi_ + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); +- controller->use_dma = 0; + } else if (spi->master->can_dma && + spi->master->can_dma(spi->master, spi, xfer) && + spi->master->cur_msg_mapped) { + controller->mode = QUP_IO_M_MODE_BAM; ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ /* must be zero for BLOCK and BAM */ + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + diff --git a/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch new file mode 100644 index 000000000..003996248 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch @@ -0,0 +1,62 @@ +From b69e5e855aaae2dd9f7fc6f4a40af8e6e0cf98ee Mon Sep 17 00:00:00 2001 +From: Matthew McClintock +Date: Thu, 10 Mar 2016 16:44:55 -0600 +Subject: [PATCH] spi: qup: properly detect extra interrupts + +It's possible for a SPI transaction to complete and get another +interrupt and have it processed on the same spi_transfer before the +transfer_one can set it to NULL. + +This masks unexpected interrupts, so let's set the spi_transfer to +NULL in the interrupt once the transaction is done. So we can +properly detect these bad interrupts and print warning messages. + +Change-Id: I0e70ed59fb50e5c48a72a38f74bd178b17c9f24d +Signed-off-by: Matthew McClintock +--- + drivers/spi/spi-qup.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -509,6 +509,7 @@ static irqreturn_t spi_qup_qup_irq(int i + u32 opflags, qup_err, spi_err; + unsigned long flags; + int error = 0; ++ bool done = 0; + + spin_lock_irqsave(&controller->lock, flags); + xfer = controller->xfer; +@@ -567,16 +568,19 @@ static irqreturn_t spi_qup_qup_irq(int i + spi_qup_write(controller, xfer); + } + +- spin_lock_irqsave(&controller->lock, flags); +- controller->error = error; +- controller->xfer = xfer; +- spin_unlock_irqrestore(&controller->lock, flags); +- + /* re-read opflags as flags may have changed due to actions above */ + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + + if ((controller->rx_bytes == xfer->len && + (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) ++ done = true; ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ controller->error = error; ++ controller->xfer = done ? NULL : xfer; ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ if (done) + complete(&controller->done); + + return IRQ_HANDLED; +@@ -769,7 +773,6 @@ static int spi_qup_transfer_one(struct s + exit: + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); +- controller->xfer = NULL; + if (!ret) + ret = controller->error; + spin_unlock_irqrestore(&controller->lock, flags); diff --git a/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch new file mode 100644 index 000000000..a08b442d5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch @@ -0,0 +1,27 @@ +From f57ff801665b868d8607c9e872466b54982564bc Mon Sep 17 00:00:00 2001 +From: Matthew McClintock +Date: Thu, 10 Mar 2016 16:48:27 -0600 +Subject: [PATCH] spi: qup: don't re-read opflags to see if transaction is done + for reads + +For reads, we will get another interrupt so we need to handle things +then. For writes, we can finish up now. + +Change-Id: I4fa95ae7bb9d78f8ba54c613b981b37d4ea81d7e +Signed-off-by: Matthew McClintock +--- + drivers/spi/spi-qup.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -569,7 +569,8 @@ static irqreturn_t spi_qup_qup_irq(int i + } + + /* re-read opflags as flags may have changed due to actions above */ +- opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) ++ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + + if ((controller->rx_bytes == xfer->len && + (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) diff --git a/target/linux/ipq806x/patches-4.4/800-devicetree.patch b/target/linux/ipq806x/patches-4.4/800-devicetree.patch new file mode 100644 index 000000000..916e2efd7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/800-devicetree.patch @@ -0,0 +1,29 @@ +From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 9 Aug 2015 13:02:38 +0200 +Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree + +Signed-off-by: Jonas Gorski +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++ + 2 files changed, 371 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -506,7 +506,14 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8084-ifc6540.dtb \ + qcom-apq8084-mtp.dtb \ + qcom-ipq8064-ap148.dtb \ ++ qcom-ipq8064-c2600.dtb \ ++ qcom-ipq8064-d7800.dtb \ + qcom-ipq8064-db149.dtb \ ++ qcom-ipq8064-ea8500.dtb \ ++ qcom-ipq8064-r7500.dtb \ ++ qcom-ipq8064-r7500v2.dtb \ ++ qcom-ipq8065-nbg6817.dtb \ ++ qcom-ipq8065-r7800.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb diff --git a/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch new file mode 100644 index 000000000..5b419a75d --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/801-override-compiler-flags.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -72,7 +72,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i + # macro, but instead defines a whole series of macros which makes + # testing for a specific architecture or later rather impossible. + arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m +-arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) ++arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15 + arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) + # Only override the compiler option if ARMv6. The ARMv6K extensions are + # always available in ARMv7 diff --git a/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch new file mode 100644 index 000000000..cfdab760a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/802-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,166 @@ +From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 24/53] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- + drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ + drivers/gpio/gpiolib-sysfs.c | 10 +++++- + include/asm-generic/gpio.h | 6 ++++ + include/linux/gpio/consumer.h | 8 +++++ + 4 files changed, 91 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -23,6 +23,8 @@ + #include + #include + #include ++#include ++#include + + #include "gpiolib.h" + +@@ -450,3 +452,69 @@ void of_gpiochip_remove(struct gpio_chip + gpiochip_remove_pin_ranges(chip); + of_node_put(chip->of_node); + } ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int __init of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++}; ++ ++static int __init of_gpio_export_init(void) ++{ ++ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe); ++} ++device_initcall(of_gpio_export_init); +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -544,7 +544,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpiod_data *data; +@@ -604,6 +604,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, chip->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -625,6 +627,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -122,6 +122,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { diff --git a/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch b/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch new file mode 100644 index 000000000..be15ec477 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch @@ -0,0 +1,185 @@ +Author: Adrian Panella +Date: Fri Jun 10 19:10:15 2016 -0500 + +generic: Mangle bootloader's kernel arguments + +The command-line arguments provided by the boot loader will be +appended to a new device tree property: bootloader-args. +If there is a property "append-rootblock" in DT under /chosen +and a root= option in bootloaders command line it will be parsed +and added to DT bootargs with the form: XX. +Only command line ATAG will be processed, the rest of the ATAGs +sent by bootloader will be ignored. +This is usefull in dual boot systems, to get the current root partition +without afecting the rest of the system. + + +Signed-off-by: Adrian Panella + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1928,6 +1928,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN + The command-line arguments provided by the boot loader will be + appended to the the device tree bootargs property. + ++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ bool "Append rootblock parsing bootloader's kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to a new device tree property: bootloader-args. ++ If there is a property "append-rootblock" in DT under /chosen ++ and a root= option in bootloaders command line it will be parsed ++ and added to DT bootargs with the form: XX. ++ Only command line ATAG will be processed, the rest of the ATAGs ++ sent by bootloader will be ignored. ++ + endchoice + + config CMDLINE +--- a/arch/arm/boot/compressed/atags_to_fdt.c ++++ b/arch/arm/boot/compressed/atags_to_fdt.c +@@ -3,6 +3,8 @@ + + #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) + #define do_extend_cmdline 1 ++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#define do_extend_cmdline 1 + #else + #define do_extend_cmdline 0 + #endif +@@ -66,6 +68,59 @@ static uint32_t get_cell_size(const void + return cell_size; + } + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ ++static char *append_rootblock(char *dest, const char *str, int len, void *fdt) ++{ ++ char *ptr, *end; ++ char *root="root="; ++ int i, l; ++ const char *rootblock; ++ ++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually ++ ptr = str - 1; ++ ++ do { ++ //first find an 'r' at the begining or after a space ++ do { ++ ptr++; ++ ptr = strchr(ptr, 'r'); ++ if(!ptr) return dest; ++ ++ } while (ptr != str && *(ptr-1) != ' '); ++ ++ //then check for the rest ++ for(i = 1; i <= 4; i++) ++ if(*(ptr+i) != *(root+i)) break; ++ ++ } while (i != 5); ++ ++ end = strchr(ptr, ' '); ++ end = end ? (end - 1) : (strchr(ptr, 0) - 1); ++ ++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) ++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); ++ ptr = end + 1; ++ ++ /* if append-rootblock property is set use it to append to command line */ ++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); ++ if(rootblock != NULL) { ++ if(*dest != ' ') { ++ *dest = ' '; ++ dest++; ++ len++; ++ } ++ if (len + l + i <= COMMAND_LINE_SIZE) { ++ memcpy(dest, rootblock, l); ++ dest += l - 1; ++ memcpy(dest, ptr, i); ++ dest += i; ++ } ++ } ++ return dest; ++} ++#endif ++ + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) + { + char cmdline[COMMAND_LINE_SIZE]; +@@ -85,12 +140,21 @@ static void merge_fdt_bootargs(void *fdt + + /* and append the ATAG_CMDLINE */ + if (fdt_cmdline) { ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //save original bootloader args ++ //and append ubi.mtd with root partition number to current cmdline ++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); ++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); ++ ++#else + len = strlen(fdt_cmdline); + if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { + *ptr++ = ' '; + memcpy(ptr, fdt_cmdline, len); + ptr += len; + } ++#endif + } + *ptr = '\0'; + +@@ -147,7 +211,9 @@ int atags_to_fdt(void *atag_list, void * + else + setprop_string(fdt, "/chosen", "bootargs", + atag->u.cmdline.cmdline); +- } else if (atag->hdr.tag == ATAG_MEM) { ++ } ++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ else if (atag->hdr.tag == ATAG_MEM) { + if (memcount >= sizeof(mem_reg_property)/4) + continue; + if (!atag->u.mem.size) +@@ -186,6 +252,10 @@ int atags_to_fdt(void *atag_list, void * + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } ++#else ++ ++ } ++#endif + + return fdt_pack(fdt); + } +--- a/init/main.c ++++ b/init/main.c +@@ -88,6 +88,10 @@ + #include + #include + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#include ++#endif ++ + static int kernel_init(void *); + + extern void init_IRQ(void); +@@ -560,6 +564,18 @@ asmlinkage __visible void __init start_k + page_alloc_init(); + + pr_notice("Kernel command line: %s\n", boot_command_line); ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //Show bootloader's original command line for reference ++ if(of_chosen) { ++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); ++ if(prop) ++ pr_notice("Bootloader command line (ignored): %s\n", prop); ++ else ++ pr_notice("Bootloader command line not present\n"); ++ } ++#endif ++ + parse_early_param(); + after_dashes = parse_args("Booting kernel", + static_command_line, __start___param, diff --git a/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch b/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch deleted file mode 100644 index be9c4eb1b..000000000 --- a/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 5e2df5f44e35d79fff2ab8bbb8a726ad5de78a3e Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Thu, 28 Apr 2016 12:55:08 -0500 -Subject: [PATCH 16/69] clk: ipq4019: report accurate fixed clock rates - -This looks like a copy-and-paste gone wrong, but update all -the fixed clock rates to report the correct values. - -Signed-off-by: Matthew McClintock ---- - drivers/clk/qcom/gcc-ipq4019.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq4019.c -+++ b/drivers/clk/qcom/gcc-ipq4019.c -@@ -1327,12 +1327,12 @@ static int gcc_ipq4019_probe(struct plat - { - struct device *dev = &pdev->dev; - -- clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000); -- clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000); -- clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000); -- clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000); -+ clk_register_fixed_rate(dev, "fepll125", "xo", 0, 125000000); -+ clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 125000000); -+ clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 250000000); -+ clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 250000000); - clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000); -- clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000); -+ clk_register_fixed_rate(dev, "fepll500", "xo", 0, 500000000); - clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000); - - return qcom_cc_probe(pdev, &gcc_ipq4019_desc); diff --git a/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch b/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch deleted file mode 100644 index 54ee571cb..000000000 --- a/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 7292bf171cdf2fb48607058f12ddd0d812a87428 Mon Sep 17 00:00:00 2001 -From: Matthew McClintock -Date: Fri, 29 Apr 2016 12:48:02 -0500 -Subject: [PATCH 19/69] qcom: ipq4019: use correct clock for i2c bus 0 - -For the record the mapping is as follows: - -QUP0 = SPI QUP1 -QUP1 = SPI QUP2 -QUP2 = I2C QUP1 -QUP3 = I2C QUP2 - -Signed-off-by: Matthew McClintock ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -175,7 +175,7 @@ - reg = <0x78b7000 0x6000>; - interrupts = ; - clocks = <&gcc GCC_BLSP1_AHB_CLK>, -- <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>; -+ <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>; - clock-names = "iface", "core"; - #address-cells = <1>; - #size-cells = <0>; diff --git a/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch index ad0b09b11..a888b063a 100644 --- a/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch +++ b/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch @@ -34,7 +34,7 @@ Signed-off-by: Ram Chandra Jangir # --- /dev/null +++ b/drivers/mtd/qcom_smem_part.c -@@ -0,0 +1,228 @@ +@@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * @@ -240,10 +240,17 @@ Signed-off-by: Ram Chandra Jangir + return smem_parts->len; +} + ++static const struct of_device_id qcom_smem_of_match_table[] = { ++ { .compatible = "qcom,smem" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table); ++ +static struct mtd_part_parser qcom_smem_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_qcom_smem_partitions, + .name = "qcom-smem", ++ .of_match_table = qcom_smem_of_match_table, +}; + +static int __init qcom_smem_parser_init(void) diff --git a/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch b/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch index bd777858e..56535a3ee 100644 --- a/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch +++ b/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch @@ -32,7 +32,7 @@ Signed-off-by: Andy Gross endmenu --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile -@@ -60,3 +60,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +@@ -60,3 +60,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy- obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o diff --git a/target/linux/ipq806x/patches-4.9/0069-arm-boot-add-dts-files.patch b/target/linux/ipq806x/patches-4.9/0069-arm-boot-add-dts-files.patch index 3e7c91cf3..5b19c1d41 100644 --- a/target/linux/ipq806x/patches-4.9/0069-arm-boot-add-dts-files.patch +++ b/target/linux/ipq806x/patches-4.9/0069-arm-boot-add-dts-files.patch @@ -10,14 +10,9 @@ Signed-off-by: John Crispin --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -618,7 +618,19 @@ dtb-$(CONFIG_ARCH_QCOM) += \ +@@ -618,6 +618,15 @@ dtb-$(CONFIG_ARCH_QCOM) += \ qcom-apq8084-mtp.dtb \ qcom-ipq4019-ap.dk01.1-c1.dtb \ - qcom-ipq4019-ap.dk04.1-c1.dtb \ -+ qcom-ipq4019-fritz4040.dtb \ -+ qcom-ipq4019-a42.dtb \ -+ qcom-ipq4019-rt-ac58u.dtb \ -+ qcom-ipq4019-rt-acrh17.dtb \ qcom-ipq8064-ap148.dtb \ + qcom-ipq8064-c2600.dtb \ + qcom-ipq8064-d7800.dtb \ @@ -25,6 +20,7 @@ Signed-off-by: John Crispin + qcom-ipq8064-ea8500.dtb \ + qcom-ipq8064-r7500.dtb \ + qcom-ipq8064-r7500v2.dtb \ ++ qcom-ipq8064-wpq864.dtb \ + qcom-ipq8065-nbg6817.dtb \ + qcom-ipq8065-r7800.dtb \ qcom-msm8660-surf.dtb \ diff --git a/target/linux/ipq806x/patches-4.9/0074-ipq806x-usb-Control-USB-master-reset.patch b/target/linux/ipq806x/patches-4.9/0074-ipq806x-usb-Control-USB-master-reset.patch index d5ff86829..9e0135b49 100644 --- a/target/linux/ipq806x/patches-4.9/0074-ipq806x-usb-Control-USB-master-reset.patch +++ b/target/linux/ipq806x/patches-4.9/0074-ipq806x-usb-Control-USB-master-reset.patch @@ -16,8 +16,6 @@ Signed-off-by: Vasudevan Murugesan (limited to 'drivers/usb/dwc3/dwc3-of-simple.c') -diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c -index f9e92ef..49bf556 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -26,6 +26,7 @@ @@ -36,10 +34,10 @@ index f9e92ef..49bf556 100644 + struct reset_control *mstr_rst_30_1; }; - static int dwc3_of_simple_probe(struct platform_device *pdev) -@@ -89,6 +92,20 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) - simple->clks[i] = clk; - } + static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) +@@ -102,6 +105,20 @@ static int dwc3_of_simple_probe(struct p + if (ret) + return ret; + simple->mstr_rst_30_0 = devm_reset_control_get(dev, "usb30_0_mstr_rst"); + @@ -58,7 +56,7 @@ index f9e92ef..49bf556 100644 ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { for (i = 0; i < simple->num_clocks; i++) { -@@ -117,6 +134,12 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) +@@ -130,6 +147,12 @@ static int dwc3_of_simple_remove(struct clk_put(simple->clks[i]); } @@ -71,5 +69,3 @@ index f9e92ef..49bf556 100644 of_platform_depopulate(dev); pm_runtime_put_sync(dev); --- -cgit v1.1 diff --git a/target/linux/ipq806x/patches-4.9/308-dts-ipq4019-add-both-IPQ4019-wifi-block-definitions.patch b/target/linux/ipq806x/patches-4.9/308-dts-ipq4019-add-both-IPQ4019-wifi-block-definitions.patch deleted file mode 100644 index d12ca4bef..000000000 --- a/target/linux/ipq806x/patches-4.9/308-dts-ipq4019-add-both-IPQ4019-wifi-block-definitions.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 6091a49b0b06bf838fed80498c4f5f40d0fbd447 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Sat, 19 Nov 2016 01:22:46 +0100 -Subject: [PATCH] dts: ipq4019: add both IPQ4019 wifi block definitions - -The IPQ4019 has two ath10k blocks on the AHB. Both wifi's -are already supported by ath10k. - -Signed-off-by: Christian Lamparter ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 84 +++++++++++++++++++++++++++++++++++++ - 1 file changed, 84 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -378,5 +378,89 @@ - dr_mode = "host"; - }; - }; -+ -+ wifi0: wifi@a000000 { -+ compatible = "qcom,ipq4019-wifi"; -+ reg = <0xa000000 0x200000>; -+ resets = <&gcc WIFI0_CPU_INIT_RESET -+ &gcc WIFI0_RADIO_SRIF_RESET -+ &gcc WIFI0_RADIO_WARM_RESET -+ &gcc WIFI0_RADIO_COLD_RESET -+ &gcc WIFI0_CORE_WARM_RESET -+ &gcc WIFI0_CORE_COLD_RESET>; -+ reset-names = "wifi_cpu_init", "wifi_radio_srif", -+ "wifi_radio_warm", "wifi_radio_cold", -+ "wifi_core_warm", "wifi_core_cold"; -+ clocks = <&gcc GCC_WCSS2G_CLK -+ &gcc GCC_WCSS2G_REF_CLK -+ &gcc GCC_WCSS2G_RTC_CLK>; -+ clock-names = "wifi_wcss_cmd", "wifi_wcss_ref", -+ "wifi_wcss_rtc"; -+ interrupts = <0 32 IRQ_TYPE_EDGE_RISING -+ 0 33 IRQ_TYPE_EDGE_RISING -+ 0 34 IRQ_TYPE_EDGE_RISING -+ 0 35 IRQ_TYPE_EDGE_RISING -+ 0 36 IRQ_TYPE_EDGE_RISING -+ 0 37 IRQ_TYPE_EDGE_RISING -+ 0 38 IRQ_TYPE_EDGE_RISING -+ 0 39 IRQ_TYPE_EDGE_RISING -+ 0 40 IRQ_TYPE_EDGE_RISING -+ 0 41 IRQ_TYPE_EDGE_RISING -+ 0 42 IRQ_TYPE_EDGE_RISING -+ 0 43 IRQ_TYPE_EDGE_RISING -+ 0 44 IRQ_TYPE_EDGE_RISING -+ 0 45 IRQ_TYPE_EDGE_RISING -+ 0 46 IRQ_TYPE_EDGE_RISING -+ 0 47 IRQ_TYPE_EDGE_RISING -+ 0 168 IRQ_TYPE_NONE>; -+ interrupt-names = "msi0", "msi1", "msi2", "msi3", -+ "msi4", "msi5", "msi6", "msi7", -+ "msi8", "msi9", "msi10", "msi11", -+ "msi12", "msi13", "msi14", "msi15", -+ "legacy"; -+ status = "disabled"; -+ }; -+ -+ wifi1: wifi@a800000 { -+ compatible = "qcom,ipq4019-wifi"; -+ reg = <0xa800000 0x200000>; -+ resets = <&gcc WIFI1_CPU_INIT_RESET -+ &gcc WIFI1_RADIO_SRIF_RESET -+ &gcc WIFI1_RADIO_WARM_RESET -+ &gcc WIFI1_RADIO_COLD_RESET -+ &gcc WIFI1_CORE_WARM_RESET -+ &gcc WIFI1_CORE_COLD_RESET>; -+ reset-names = "wifi_cpu_init", "wifi_radio_srif", -+ "wifi_radio_warm", "wifi_radio_cold", -+ "wifi_core_warm", "wifi_core_cold"; -+ clocks = <&gcc GCC_WCSS5G_CLK -+ &gcc GCC_WCSS5G_REF_CLK -+ &gcc GCC_WCSS5G_RTC_CLK>; -+ clock-names = "wifi_wcss_cmd", "wifi_wcss_ref", -+ "wifi_wcss_rtc"; -+ interrupts = <0 48 IRQ_TYPE_EDGE_RISING -+ 0 49 IRQ_TYPE_EDGE_RISING -+ 0 50 IRQ_TYPE_EDGE_RISING -+ 0 51 IRQ_TYPE_EDGE_RISING -+ 0 52 IRQ_TYPE_EDGE_RISING -+ 0 53 IRQ_TYPE_EDGE_RISING -+ 0 54 IRQ_TYPE_EDGE_RISING -+ 0 55 IRQ_TYPE_EDGE_RISING -+ 0 56 IRQ_TYPE_EDGE_RISING -+ 0 57 IRQ_TYPE_EDGE_RISING -+ 0 58 IRQ_TYPE_EDGE_RISING -+ 0 59 IRQ_TYPE_EDGE_RISING -+ 0 60 IRQ_TYPE_EDGE_RISING -+ 0 61 IRQ_TYPE_EDGE_RISING -+ 0 62 IRQ_TYPE_EDGE_RISING -+ 0 63 IRQ_TYPE_EDGE_RISING -+ 0 169 IRQ_TYPE_NONE>; -+ interrupt-names = "msi0", "msi1", "msi2", "msi3", -+ "msi4", "msi5", "msi6", "msi7", -+ "msi8", "msi9", "msi10", "msi11", -+ "msi12", "msi13", "msi14", "msi15", -+ "legacy"; -+ status = "disabled"; -+ }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.9/309-dts-ipq4019-add-pseudo-random-number-generator.patch b/target/linux/ipq806x/patches-4.9/309-dts-ipq4019-add-pseudo-random-number-generator.patch deleted file mode 100644 index abcbb6ee5..000000000 --- a/target/linux/ipq806x/patches-4.9/309-dts-ipq4019-add-pseudo-random-number-generator.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 26fa6fdc627b523277c7a79907233596b2f8a3ef Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Sat, 19 Nov 2016 03:29:04 +0100 -Subject: [PATCH] dts: ipq4019: add pseudo random number generator - -This architecture has a pseudo random number generator -supported by the existing "qcom,prng" binding. - -Signed-off-by: Christian Lamparter ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -271,6 +271,13 @@ - regulator; - }; - -+ rng@22000 { -+ compatible = "qcom,prng"; -+ reg = <0x22000 0x140>; -+ clocks = <&gcc GCC_PRNG_AHB_CLK>; -+ clock-names = "core"; -+ }; -+ - serial@78af000 { - compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; - reg = <0x78af000 0x200>; diff --git a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch deleted file mode 100644 index d0a6a26a5..000000000 --- a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch +++ /dev/null @@ -1,1328 +0,0 @@ -From fc6cf61517b8b4ab4678659936fc7572f699d6e7 Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Tue, 28 Mar 2017 14:00:00 +0530 -Subject: [PATCH] ipq4019: pinctrl: Updated various Pin definitions - -Populate default values for various GPIO functions - -Signed-off-by: Ram Chandra Jangir ---- - drivers/pinctrl/qcom/pinctrl-ipq4019.c | 1189 +++++++++++++++++++++++++++++--- - 1 file changed, 1111 insertions(+), 78 deletions(-) - ---- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c -+++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c -@@ -276,16 +276,531 @@ DECLARE_QCA_GPIO_PINS(99); - - - enum ipq4019_functions { -+ qca_mux_rmii0_refclk, -+ qca_mux_wifi0_rfsilient0, -+ qca_mux_wifi1_rfsilient0, -+ qca_mux_smart2, -+ qca_mux_led4, -+ qca_mux_wifi0_cal, -+ qca_mux_wifi1_cal, -+ qca_mux_wifi_wci0, -+ qca_mux_rmii0_dv, -+ qca_mux_wifi_wci1, -+ qca_mux_rmii1_refclk, -+ qca_mux_blsp_spi1, -+ qca_mux_led5, -+ qca_mux_rmii10, -+ qca_mux_led6, -+ qca_mux_rmii11, -+ qca_mux_led7, -+ qca_mux_rmii1_dv, -+ qca_mux_led8, -+ qca_mux_rmii1_tx, -+ qca_mux_aud_pin, -+ qca_mux_led9, -+ qca_mux_rmii1_rx, -+ qca_mux_led10, -+ qca_mux_wifi0_rfsilient1, -+ qca_mux_wifi1_rfsilient1, -+ qca_mux_led11, -+ qca_mux_boot7, -+ qca_mux_qpic_pad, -+ qca_mux_pcie_clk, -+ qca_mux_tm_clk0, -+ qca_mux_wifi00, -+ qca_mux_wifi10, -+ qca_mux_mdio1, -+ qca_mux_prng_rosc, -+ qca_mux_dbg_out, -+ qca_mux_tm0, -+ qca_mux_wifi01, -+ qca_mux_wifi11, -+ qca_mux_atest_char3, -+ qca_mux_pmu0, -+ qca_mux_boot8, -+ qca_mux_tm1, -+ qca_mux_atest_char2, -+ qca_mux_pmu1, -+ qca_mux_boot9, -+ qca_mux_tm2, -+ qca_mux_atest_char1, -+ qca_mux_tm_ack, -+ qca_mux_wifi03, -+ qca_mux_wifi13, -+ qca_mux_qpic_pad4, -+ qca_mux_atest_char0, -+ qca_mux_tm3, -+ qca_mux_wifi02, -+ qca_mux_wifi12, -+ qca_mux_qpic_pad5, -+ qca_mux_smart3, -+ qca_mux_wcss0_dbg14, -+ qca_mux_tm4, -+ qca_mux_wifi04, -+ qca_mux_wifi14, -+ qca_mux_qpic_pad6, -+ qca_mux_wcss0_dbg15, -+ qca_mux_qdss_tracectl_a, -+ qca_mux_boot18, -+ qca_mux_tm5, -+ qca_mux_qpic_pad7, -+ qca_mux_atest_char, -+ qca_mux_wcss0_dbg4, -+ qca_mux_qdss_traceclk_a, -+ qca_mux_boot19, -+ qca_mux_tm6, -+ qca_mux_wcss0_dbg5, -+ qca_mux_qdss_cti_trig_out_a0, -+ qca_mux_boot14, -+ qca_mux_tm7, -+ qca_mux_chip_rst, -+ qca_mux_wcss0_dbg6, -+ qca_mux_qdss_cti_trig_out_b0, -+ qca_mux_boot11, -+ qca_mux_tm8, -+ qca_mux_wcss0_dbg7, -+ qca_mux_wcss1_dbg7, -+ qca_mux_boot20, -+ qca_mux_tm9, -+ qca_mux_qpic_pad1, -+ qca_mux_wcss0_dbg8, -+ qca_mux_wcss1_dbg8, -+ qca_mux_qpic_pad2, -+ qca_mux_wcss0_dbg9, -+ qca_mux_wcss1_dbg9, -+ qca_mux_qpic_pad3, -+ qca_mux_wcss0_dbg10, -+ qca_mux_wcss1_dbg10, -+ qca_mux_qpic_pad0, -+ qca_mux_wcss0_dbg11, -+ qca_mux_wcss1_dbg11, -+ qca_mux_qpic_pad8, -+ qca_mux_wcss0_dbg12, -+ qca_mux_wcss1_dbg12, -+ qca_mux_wifi034, -+ qca_mux_wifi134, -+ qca_mux_jtag_tdi, - qca_mux_gpio, -+ qca_mux_i2s_rx_bclk, -+ qca_mux_jtag_tck, -+ qca_mux_i2s_rx_fsync, -+ qca_mux_jtag_tms, -+ qca_mux_i2s_rxd, -+ qca_mux_smart0, -+ qca_mux_jtag_tdo, -+ qca_mux_jtag_rst, -+ qca_mux_jtag_trst, -+ qca_mux_mdio0, -+ qca_mux_wcss0_dbg18, -+ qca_mux_wcss1_dbg18, -+ qca_mux_qdss_tracedata_a, -+ qca_mux_mdc, -+ qca_mux_wcss0_dbg19, -+ qca_mux_wcss1_dbg19, - qca_mux_blsp_uart1, -+ qca_mux_wifi0_uart, -+ qca_mux_wifi1_uart, -+ qca_mux_smart1, -+ qca_mux_wcss0_dbg20, -+ qca_mux_wcss1_dbg20, -+ qca_mux_wifi0_uart0, -+ qca_mux_wifi1_uart0, -+ qca_mux_wcss0_dbg21, -+ qca_mux_wcss1_dbg21, - qca_mux_blsp_i2c0, -+ qca_mux_wcss0_dbg22, -+ qca_mux_wcss1_dbg22, -+ qca_mux_wcss0_dbg23, -+ qca_mux_wcss1_dbg23, -+ qca_mux_blsp_spi0, - qca_mux_blsp_i2c1, -+ qca_mux_wcss0_dbg24, -+ qca_mux_wcss1_dbg24, -+ qca_mux_wcss0_dbg25, -+ qca_mux_wcss1_dbg25, -+ qca_mux_wcss0_dbg26, -+ qca_mux_wcss1_dbg26, -+ qca_mux_wcss0_dbg, -+ qca_mux_wcss1_dbg, - qca_mux_blsp_uart0, -- qca_mux_blsp_spi1, -- qca_mux_blsp_spi0, -+ qca_mux_led0, -+ qca_mux_wcss0_dbg28, -+ qca_mux_wcss1_dbg28, -+ qca_mux_led1, -+ qca_mux_wcss0_dbg29, -+ qca_mux_wcss1_dbg29, -+ qca_mux_wifi0_uart1, -+ qca_mux_wifi1_uart1, -+ qca_mux_wcss0_dbg30, -+ qca_mux_wcss1_dbg30, -+ qca_mux_wcss0_dbg31, -+ qca_mux_wcss1_dbg31, -+ qca_mux_i2s_rx_mclk, -+ qca_mux_wcss0_dbg16, -+ qca_mux_wcss1_dbg16, -+ qca_mux_wcss0_dbg17, -+ qca_mux_wcss1_dbg17, -+ qca_mux_rgmii0, -+ qca_mux_sdio0, -+ qca_mux_rgmii1, -+ qca_mux_sdio1, -+ qca_mux_rgmii2, -+ qca_mux_i2s_tx_mclk, -+ qca_mux_sdio2, -+ qca_mux_rgmii3, -+ qca_mux_i2s_tx_bclk, -+ qca_mux_sdio3, -+ qca_mux_rgmii_rx, -+ qca_mux_i2s_tx_fsync, -+ qca_mux_sdio_clk, -+ qca_mux_rgmii_txc, -+ qca_mux_i2s_td1, -+ qca_mux_sdio_cmd, -+ qca_mux_i2s_td2, -+ qca_mux_sdio4, -+ qca_mux_i2s_td3, -+ qca_mux_sdio5, -+ qca_mux_audio_pwm0, -+ qca_mux_sdio6, -+ qca_mux_audio_pwm1, -+ qca_mux_wcss0_dbg27, -+ qca_mux_wcss1_dbg27, -+ qca_mux_sdio7, -+ qca_mux_rgmii_rxc, -+ qca_mux_audio_pwm2, -+ qca_mux_rgmii_tx, -+ qca_mux_audio_pwm3, -+ qca_mux_boot2, -+ qca_mux_i2s_spdif_in, -+ qca_mux_i2s_spdif_out, -+ qca_mux_rmii00, -+ qca_mux_led2, -+ qca_mux_rmii01, -+ qca_mux_wifi0_wci, -+ qca_mux_wifi1_wci, -+ qca_mux_boot4, -+ qca_mux_rmii0_tx, -+ qca_mux_boot5, -+ qca_mux_rmii0_rx, -+ qca_mux_pcie_clk1, -+ qca_mux_led3, -+ qca_mux_sdio_cd, - qca_mux_NA, - }; - -+static const char * const rmii0_refclk_groups[] = { -+ "gpio40", -+}; -+static const char * const wifi0_rfsilient0_groups[] = { -+ "gpio40", -+}; -+static const char * const wifi1_rfsilient0_groups[] = { -+ "gpio40", -+}; -+static const char * const smart2_groups[] = { -+ "gpio40", "gpio41", "gpio48", "gpio49", -+}; -+static const char * const led4_groups[] = { -+ "gpio40", -+}; -+static const char * const wifi0_cal_groups[] = { -+ "gpio41", "gpio51", -+}; -+static const char * const wifi1_cal_groups[] = { -+ "gpio41", "gpio51", -+}; -+static const char * const wifi_wci0_groups[] = { -+ "gpio42", -+}; -+static const char * const rmii0_dv_groups[] = { -+ "gpio43", -+}; -+static const char * const wifi_wci1_groups[] = { -+ "gpio43", -+}; -+static const char * const rmii1_refclk_groups[] = { -+ "gpio44", -+}; -+static const char * const blsp_spi1_groups[] = { -+ "gpio44", "gpio45", "gpio46", "gpio47", -+}; -+static const char * const led5_groups[] = { -+ "gpio44", -+}; -+static const char * const rmii10_groups[] = { -+ "gpio45", "gpio50", -+}; -+static const char * const led6_groups[] = { -+ "gpio45", -+}; -+static const char * const rmii11_groups[] = { -+ "gpio46", "gpio51", -+}; -+static const char * const led7_groups[] = { -+ "gpio46", -+}; -+static const char * const rmii1_dv_groups[] = { -+ "gpio47", -+}; -+static const char * const led8_groups[] = { -+ "gpio47", -+}; -+static const char * const rmii1_tx_groups[] = { -+ "gpio48", -+}; -+static const char * const aud_pin_groups[] = { -+ "gpio48", "gpio49", "gpio50", "gpio51", -+}; -+static const char * const led9_groups[] = { -+ "gpio48", -+}; -+static const char * const rmii1_rx_groups[] = { -+ "gpio49", -+}; -+static const char * const led10_groups[] = { -+ "gpio49", -+}; -+static const char * const wifi0_rfsilient1_groups[] = { -+ "gpio50", -+}; -+static const char * const wifi1_rfsilient1_groups[] = { -+ "gpio50", -+}; -+static const char * const led11_groups[] = { -+ "gpio50", -+}; -+static const char * const boot7_groups[] = { -+ "gpio51", -+}; -+static const char * const qpic_pad_groups[] = { -+ "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", "gpio61", "gpio62", -+ "gpio63", "gpio69", -+}; -+static const char * const pcie_clk_groups[] = { -+ "gpio52", -+}; -+static const char * const tm_clk0_groups[] = { -+ "gpio52", -+}; -+static const char * const wifi00_groups[] = { -+ "gpio52", -+}; -+static const char * const wifi10_groups[] = { -+ "gpio52", -+}; -+static const char * const mdio1_groups[] = { -+ "gpio53", -+}; -+static const char * const prng_rosc_groups[] = { -+ "gpio53", -+}; -+static const char * const dbg_out_groups[] = { -+ "gpio53", -+}; -+static const char * const tm0_groups[] = { -+ "gpio53", -+}; -+static const char * const wifi01_groups[] = { -+ "gpio53", -+}; -+static const char * const wifi11_groups[] = { -+ "gpio53", -+}; -+static const char * const atest_char3_groups[] = { -+ "gpio54", -+}; -+static const char * const pmu0_groups[] = { -+ "gpio54", -+}; -+static const char * const boot8_groups[] = { -+ "gpio54", -+}; -+static const char * const tm1_groups[] = { -+ "gpio54", -+}; -+static const char * const atest_char2_groups[] = { -+ "gpio55", -+}; -+static const char * const pmu1_groups[] = { -+ "gpio55", -+}; -+static const char * const boot9_groups[] = { -+ "gpio55", -+}; -+static const char * const tm2_groups[] = { -+ "gpio55", -+}; -+static const char * const atest_char1_groups[] = { -+ "gpio56", -+}; -+static const char * const tm_ack_groups[] = { -+ "gpio56", -+}; -+static const char * const wifi03_groups[] = { -+ "gpio56", -+}; -+static const char * const wifi13_groups[] = { -+ "gpio56", -+}; -+static const char * const qpic_pad4_groups[] = { -+ "gpio57", -+}; -+static const char * const atest_char0_groups[] = { -+ "gpio57", -+}; -+static const char * const tm3_groups[] = { -+ "gpio57", -+}; -+static const char * const wifi02_groups[] = { -+ "gpio57", -+}; -+static const char * const wifi12_groups[] = { -+ "gpio57", -+}; -+static const char * const qpic_pad5_groups[] = { -+ "gpio58", -+}; -+static const char * const smart3_groups[] = { -+ "gpio58", "gpio59", "gpio60", "gpio61", -+}; -+static const char * const wcss0_dbg14_groups[] = { -+ "gpio58", -+}; -+static const char * const tm4_groups[] = { -+ "gpio58", -+}; -+static const char * const wifi04_groups[] = { -+ "gpio58", -+}; -+static const char * const wifi14_groups[] = { -+ "gpio58", -+}; -+static const char * const qpic_pad6_groups[] = { -+ "gpio59", -+}; -+static const char * const wcss0_dbg15_groups[] = { -+ "gpio59", -+}; -+static const char * const qdss_tracectl_a_groups[] = { -+ "gpio59", -+}; -+static const char * const boot18_groups[] = { -+ "gpio59", -+}; -+static const char * const tm5_groups[] = { -+ "gpio59", -+}; -+static const char * const qpic_pad7_groups[] = { -+ "gpio60", -+}; -+static const char * const atest_char_groups[] = { -+ "gpio60", -+}; -+static const char * const wcss0_dbg4_groups[] = { -+ "gpio60", -+}; -+static const char * const qdss_traceclk_a_groups[] = { -+ "gpio60", -+}; -+static const char * const boot19_groups[] = { -+ "gpio60", -+}; -+static const char * const tm6_groups[] = { -+ "gpio60", -+}; -+static const char * const wcss0_dbg5_groups[] = { -+ "gpio61", -+}; -+static const char * const qdss_cti_trig_out_a0_groups[] = { -+ "gpio61", -+}; -+static const char * const boot14_groups[] = { -+ "gpio61", -+}; -+static const char * const tm7_groups[] = { -+ "gpio61", -+}; -+static const char * const chip_rst_groups[] = { -+ "gpio62", -+}; -+static const char * const wcss0_dbg6_groups[] = { -+ "gpio62", -+}; -+static const char * const qdss_cti_trig_out_b0_groups[] = { -+ "gpio62", -+}; -+static const char * const boot11_groups[] = { -+ "gpio62", -+}; -+static const char * const tm8_groups[] = { -+ "gpio62", -+}; -+static const char * const wcss0_dbg7_groups[] = { -+ "gpio63", -+}; -+static const char * const wcss1_dbg7_groups[] = { -+ "gpio63", -+}; -+static const char * const boot20_groups[] = { -+ "gpio63", -+}; -+static const char * const tm9_groups[] = { -+ "gpio63", -+}; -+static const char * const qpic_pad1_groups[] = { -+ "gpio64", -+}; -+static const char * const wcss0_dbg8_groups[] = { -+ "gpio64", -+}; -+static const char * const wcss1_dbg8_groups[] = { -+ "gpio64", -+}; -+static const char * const qpic_pad2_groups[] = { -+ "gpio65", -+}; -+static const char * const wcss0_dbg9_groups[] = { -+ "gpio65", -+}; -+static const char * const wcss1_dbg9_groups[] = { -+ "gpio65", -+}; -+static const char * const qpic_pad3_groups[] = { -+ "gpio66", -+}; -+static const char * const wcss0_dbg10_groups[] = { -+ "gpio66", -+}; -+static const char * const wcss1_dbg10_groups[] = { -+ "gpio66", -+}; -+static const char * const qpic_pad0_groups[] = { -+ "gpio67", -+}; -+static const char * const wcss0_dbg11_groups[] = { -+ "gpio67", -+}; -+static const char * const wcss1_dbg11_groups[] = { -+ "gpio67", -+}; -+static const char * const qpic_pad8_groups[] = { -+ "gpio68", -+}; -+static const char * const wcss0_dbg12_groups[] = { -+ "gpio68", -+}; -+static const char * const wcss1_dbg12_groups[] = { -+ "gpio68", -+}; -+static const char * const wifi034_groups[] = { -+ "gpio98", -+}; -+static const char * const wifi134_groups[] = { -+ "gpio98", -+}; -+static const char * const jtag_tdi_groups[] = { -+ "gpio0", -+}; - static const char * const gpio_groups[] = { - "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", - "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", -@@ -303,13 +818,103 @@ static const char * const gpio_groups[] - "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", - "gpio99", - }; -- -+static const char * const i2s_rx_bclk_groups[] = { -+ "gpio0", "gpio21", "gpio60", -+}; -+static const char * const jtag_tck_groups[] = { -+ "gpio1", -+}; -+static const char * const i2s_rx_fsync_groups[] = { -+ "gpio1", "gpio22", "gpio61", -+}; -+static const char * const jtag_tms_groups[] = { -+ "gpio2", -+}; -+static const char * const i2s_rxd_groups[] = { -+ "gpio2", "gpio23", "gpio63", -+}; -+static const char * const smart0_groups[] = { -+ "gpio0", "gpio1", "gpio2", "gpio5", "gpio44", "gpio45", "gpio46", -+ "gpio47", -+}; -+static const char * const jtag_tdo_groups[] = { -+ "gpio3", -+}; -+static const char * const jtag_rst_groups[] = { -+ "gpio4", -+}; -+static const char * const jtag_trst_groups[] = { -+ "gpio5", -+}; -+static const char * const mdio0_groups[] = { -+ "gpio6", -+}; -+static const char * const wcss0_dbg18_groups[] = { -+ "gpio6", "gpio22", "gpio39", -+}; -+static const char * const wcss1_dbg18_groups[] = { -+ "gpio6", "gpio22", "gpio39", -+}; -+static const char * const qdss_tracedata_a_groups[] = { -+ "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio16", -+ "gpio17", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", -+ "gpio43", -+}; -+static const char * const mdc_groups[] = { -+ "gpio7", "gpio52", -+}; -+static const char * const wcss0_dbg19_groups[] = { -+ "gpio7", "gpio23", "gpio40", -+}; -+static const char * const wcss1_dbg19_groups[] = { -+ "gpio7", "gpio23", "gpio40", -+}; - static const char * const blsp_uart1_groups[] = { - "gpio8", "gpio9", "gpio10", "gpio11", - }; -+static const char * const wifi0_uart_groups[] = { -+ "gpio8", "gpio9", "gpio11", "gpio19", "gpio62", -+}; -+static const char * const wifi1_uart_groups[] = { -+ "gpio8", "gpio11", "gpio19", "gpio62", "gpio63", -+}; -+static const char * const smart1_groups[] = { -+ "gpio8", "gpio9", "gpio16", "gpio17", "gpio58", "gpio59", "gpio60", -+ "gpio61", -+}; -+static const char * const wcss0_dbg20_groups[] = { -+ "gpio8", "gpio24", "gpio41", -+}; -+static const char * const wcss1_dbg20_groups[] = { -+ "gpio8", "gpio24", "gpio41", -+}; -+static const char * const wifi0_uart0_groups[] = { -+ "gpio9", "gpio10", -+}; -+static const char * const wifi1_uart0_groups[] = { -+ "gpio9", "gpio10", -+}; -+static const char * const wcss0_dbg21_groups[] = { -+ "gpio9", "gpio25", "gpio42", -+}; -+static const char * const wcss1_dbg21_groups[] = { -+ "gpio9", "gpio25", "gpio42", -+}; - static const char * const blsp_i2c0_groups[] = { - "gpio10", "gpio11", "gpio20", "gpio21", "gpio58", "gpio59", - }; -+static const char * const wcss0_dbg22_groups[] = { -+ "gpio10", "gpio26", "gpio43", -+}; -+static const char * const wcss1_dbg22_groups[] = { -+ "gpio10", "gpio26", "gpio43", -+}; -+static const char * const wcss0_dbg23_groups[] = { -+ "gpio11", "gpio27", "gpio44", -+}; -+static const char * const wcss1_dbg23_groups[] = { -+ "gpio11", "gpio27", "gpio44", -+}; - static const char * const blsp_spi0_groups[] = { - "gpio12", "gpio13", "gpio14", "gpio15", "gpio45", - "gpio54", "gpio55", "gpio56", "gpio57", -@@ -317,94 +922,582 @@ static const char * const blsp_spi0_grou - static const char * const blsp_i2c1_groups[] = { - "gpio12", "gpio13", "gpio34", "gpio35", - }; -+static const char * const wcss0_dbg24_groups[] = { -+ "gpio12", "gpio28", "gpio45", -+}; -+static const char * const wcss1_dbg24_groups[] = { -+ "gpio12", "gpio28", "gpio45", -+}; -+static const char * const wcss0_dbg25_groups[] = { -+ "gpio13", "gpio29", "gpio46", -+}; -+static const char * const wcss1_dbg25_groups[] = { -+ "gpio13", "gpio29", "gpio46", -+}; -+static const char * const wcss0_dbg26_groups[] = { -+ "gpio14", "gpio30", "gpio47", -+}; -+static const char * const wcss1_dbg26_groups[] = { -+ "gpio14", "gpio30", "gpio47", -+}; -+static const char * const wcss0_dbg_groups[] = { -+ "gpio15", "gpio69", -+}; -+static const char * const wcss1_dbg_groups[] = { -+ "gpio15", -+}; - static const char * const blsp_uart0_groups[] = { - "gpio16", "gpio17", "gpio60", "gpio61", - }; --static const char * const blsp_spi1_groups[] = { -- "gpio44", "gpio45", "gpio46", "gpio47", -+static const char * const led0_groups[] = { -+ "gpio16", "gpio36", "gpio60", -+}; -+static const char * const wcss0_dbg28_groups[] = { -+ "gpio16", "gpio32", "gpio49", -+}; -+static const char * const wcss1_dbg28_groups[] = { -+ "gpio16", "gpio32", "gpio49", -+}; -+static const char * const led1_groups[] = { -+ "gpio17", "gpio37", "gpio61", -+}; -+static const char * const wcss0_dbg29_groups[] = { -+ "gpio17", "gpio33", "gpio50", -+}; -+static const char * const wcss1_dbg29_groups[] = { -+ "gpio17", "gpio33", "gpio50", -+}; -+static const char * const wifi0_uart1_groups[] = { -+ "gpio18", "gpio63", -+}; -+static const char * const wifi1_uart1_groups[] = { -+ "gpio18", "gpio63", -+}; -+static const char * const wcss0_dbg30_groups[] = { -+ "gpio18", "gpio34", "gpio51", -+}; -+static const char * const wcss1_dbg30_groups[] = { -+ "gpio18", "gpio34", "gpio51", -+}; -+static const char * const wcss0_dbg31_groups[] = { -+ "gpio19", "gpio35", "gpio52", -+}; -+static const char * const wcss1_dbg31_groups[] = { -+ "gpio19", "gpio35", -+}; -+static const char * const i2s_rx_mclk_groups[] = { -+ "gpio20", "gpio58", -+}; -+static const char * const wcss0_dbg16_groups[] = { -+ "gpio20", "gpio37", -+}; -+static const char * const wcss1_dbg16_groups[] = { -+ "gpio20", "gpio37", -+}; -+static const char * const wcss0_dbg17_groups[] = { -+ "gpio21", "gpio38", -+}; -+static const char * const wcss1_dbg17_groups[] = { -+ "gpio21", "gpio38", -+}; -+static const char * const rgmii0_groups[] = { -+ "gpio22", "gpio28", -+}; -+static const char * const sdio0_groups[] = { -+ "gpio23", -+}; -+static const char * const rgmii1_groups[] = { -+ "gpio23", "gpio29", -+}; -+static const char * const sdio1_groups[] = { -+ "gpio24", -+}; -+static const char * const rgmii2_groups[] = { -+ "gpio24", "gpio30", -+}; -+static const char * const i2s_tx_mclk_groups[] = { -+ "gpio24", "gpio52", -+}; -+static const char * const sdio2_groups[] = { -+ "gpio25", -+}; -+static const char * const rgmii3_groups[] = { -+ "gpio25", "gpio31", -+}; -+static const char * const i2s_tx_bclk_groups[] = { -+ "gpio25", "gpio53", "gpio60", -+}; -+static const char * const sdio3_groups[] = { -+ "gpio26", -+}; -+static const char * const rgmii_rx_groups[] = { -+ "gpio26", -+}; -+static const char * const i2s_tx_fsync_groups[] = { -+ "gpio26", "gpio57", "gpio61", -+}; -+static const char * const sdio_clk_groups[] = { -+ "gpio27", -+}; -+static const char * const rgmii_txc_groups[] = { -+ "gpio27", -+}; -+static const char * const i2s_td1_groups[] = { -+ "gpio27", "gpio54", "gpio63", -+}; -+static const char * const sdio_cmd_groups[] = { -+ "gpio28", -+}; -+static const char * const i2s_td2_groups[] = { -+ "gpio28", "gpio55", -+}; -+static const char * const sdio4_groups[] = { -+ "gpio29", -+}; -+static const char * const i2s_td3_groups[] = { -+ "gpio29", "gpio56", -+}; -+static const char * const sdio5_groups[] = { -+ "gpio30", -+}; -+static const char * const audio_pwm0_groups[] = { -+ "gpio30", "gpio64", -+}; -+static const char * const sdio6_groups[] = { -+ "gpio31", -+}; -+static const char * const audio_pwm1_groups[] = { -+ "gpio31", "gpio65", -+}; -+static const char * const wcss0_dbg27_groups[] = { -+ "gpio31", "gpio48", -+}; -+static const char * const wcss1_dbg27_groups[] = { -+ "gpio31", "gpio48", -+}; -+static const char * const sdio7_groups[] = { -+ "gpio32", -+}; -+static const char * const rgmii_rxc_groups[] = { -+ "gpio32", -+}; -+static const char * const audio_pwm2_groups[] = { -+ "gpio32", "gpio66", -+}; -+static const char * const rgmii_tx_groups[] = { -+ "gpio33", -+}; -+static const char * const audio_pwm3_groups[] = { -+ "gpio33", "gpio67", -+}; -+static const char * const boot2_groups[] = { -+ "gpio33", -+}; -+static const char * const i2s_spdif_in_groups[] = { -+ "gpio34", "gpio59", "gpio63", -+}; -+static const char * const i2s_spdif_out_groups[] = { -+ "gpio35", "gpio62", "gpio63", -+}; -+static const char * const rmii00_groups[] = { -+ "gpio36", "gpio41", -+}; -+static const char * const led2_groups[] = { -+ "gpio36", "gpio38", "gpio58", -+}; -+static const char * const rmii01_groups[] = { -+ "gpio37", "gpio42", -+}; -+static const char * const wifi0_wci_groups[] = { -+ "gpio37", -+}; -+static const char * const wifi1_wci_groups[] = { -+ "gpio37", -+}; -+static const char * const boot4_groups[] = { -+ "gpio37", -+}; -+static const char * const rmii0_tx_groups[] = { -+ "gpio38", -+}; -+static const char * const boot5_groups[] = { -+ "gpio38", -+}; -+static const char * const rmii0_rx_groups[] = { -+ "gpio39", -+}; -+static const char * const pcie_clk1_groups[] = { -+ "gpio39", -+}; -+static const char * const led3_groups[] = { -+ "gpio39", -+}; -+static const char * const sdio_cd_groups[] = { -+ "gpio22", - }; - - static const struct msm_function ipq4019_functions[] = { -+ FUNCTION(rmii0_refclk), -+ FUNCTION(wifi0_rfsilient0), -+ FUNCTION(wifi1_rfsilient0), -+ FUNCTION(smart2), -+ FUNCTION(led4), -+ FUNCTION(wifi0_cal), -+ FUNCTION(wifi1_cal), -+ FUNCTION(wifi_wci0), -+ FUNCTION(rmii0_dv), -+ FUNCTION(wifi_wci1), -+ FUNCTION(rmii1_refclk), -+ FUNCTION(blsp_spi1), -+ FUNCTION(led5), -+ FUNCTION(rmii10), -+ FUNCTION(led6), -+ FUNCTION(rmii11), -+ FUNCTION(led7), -+ FUNCTION(rmii1_dv), -+ FUNCTION(led8), -+ FUNCTION(rmii1_tx), -+ FUNCTION(aud_pin), -+ FUNCTION(led9), -+ FUNCTION(rmii1_rx), -+ FUNCTION(led10), -+ FUNCTION(wifi0_rfsilient1), -+ FUNCTION(wifi1_rfsilient1), -+ FUNCTION(led11), -+ FUNCTION(boot7), -+ FUNCTION(qpic_pad), -+ FUNCTION(pcie_clk), -+ FUNCTION(tm_clk0), -+ FUNCTION(wifi00), -+ FUNCTION(wifi10), -+ FUNCTION(mdio1), -+ FUNCTION(prng_rosc), -+ FUNCTION(dbg_out), -+ FUNCTION(tm0), -+ FUNCTION(wifi01), -+ FUNCTION(wifi11), -+ FUNCTION(atest_char3), -+ FUNCTION(pmu0), -+ FUNCTION(boot8), -+ FUNCTION(tm1), -+ FUNCTION(atest_char2), -+ FUNCTION(pmu1), -+ FUNCTION(boot9), -+ FUNCTION(tm2), -+ FUNCTION(atest_char1), -+ FUNCTION(tm_ack), -+ FUNCTION(wifi03), -+ FUNCTION(wifi13), -+ FUNCTION(qpic_pad4), -+ FUNCTION(atest_char0), -+ FUNCTION(tm3), -+ FUNCTION(wifi02), -+ FUNCTION(wifi12), -+ FUNCTION(qpic_pad5), -+ FUNCTION(smart3), -+ FUNCTION(wcss0_dbg14), -+ FUNCTION(tm4), -+ FUNCTION(wifi04), -+ FUNCTION(wifi14), -+ FUNCTION(qpic_pad6), -+ FUNCTION(wcss0_dbg15), -+ FUNCTION(qdss_tracectl_a), -+ FUNCTION(boot18), -+ FUNCTION(tm5), -+ FUNCTION(qpic_pad7), -+ FUNCTION(atest_char), -+ FUNCTION(wcss0_dbg4), -+ FUNCTION(qdss_traceclk_a), -+ FUNCTION(boot19), -+ FUNCTION(tm6), -+ FUNCTION(wcss0_dbg5), -+ FUNCTION(qdss_cti_trig_out_a0), -+ FUNCTION(boot14), -+ FUNCTION(tm7), -+ FUNCTION(chip_rst), -+ FUNCTION(wcss0_dbg6), -+ FUNCTION(qdss_cti_trig_out_b0), -+ FUNCTION(boot11), -+ FUNCTION(tm8), -+ FUNCTION(wcss0_dbg7), -+ FUNCTION(wcss1_dbg7), -+ FUNCTION(boot20), -+ FUNCTION(tm9), -+ FUNCTION(qpic_pad1), -+ FUNCTION(wcss0_dbg8), -+ FUNCTION(wcss1_dbg8), -+ FUNCTION(qpic_pad2), -+ FUNCTION(wcss0_dbg9), -+ FUNCTION(wcss1_dbg9), -+ FUNCTION(qpic_pad3), -+ FUNCTION(wcss0_dbg10), -+ FUNCTION(wcss1_dbg10), -+ FUNCTION(qpic_pad0), -+ FUNCTION(wcss0_dbg11), -+ FUNCTION(wcss1_dbg11), -+ FUNCTION(qpic_pad8), -+ FUNCTION(wcss0_dbg12), -+ FUNCTION(wcss1_dbg12), -+ FUNCTION(wifi034), -+ FUNCTION(wifi134), -+ FUNCTION(jtag_tdi), - FUNCTION(gpio), -+ FUNCTION(i2s_rx_bclk), -+ FUNCTION(jtag_tck), -+ FUNCTION(i2s_rx_fsync), -+ FUNCTION(jtag_tms), -+ FUNCTION(i2s_rxd), -+ FUNCTION(smart0), -+ FUNCTION(jtag_tdo), -+ FUNCTION(jtag_rst), -+ FUNCTION(jtag_trst), -+ FUNCTION(mdio0), -+ FUNCTION(wcss0_dbg18), -+ FUNCTION(wcss1_dbg18), -+ FUNCTION(qdss_tracedata_a), -+ FUNCTION(mdc), -+ FUNCTION(wcss0_dbg19), -+ FUNCTION(wcss1_dbg19), - FUNCTION(blsp_uart1), -+ FUNCTION(wifi0_uart), -+ FUNCTION(wifi1_uart), -+ FUNCTION(smart1), -+ FUNCTION(wcss0_dbg20), -+ FUNCTION(wcss1_dbg20), -+ FUNCTION(wifi0_uart0), -+ FUNCTION(wifi1_uart0), -+ FUNCTION(wcss0_dbg21), -+ FUNCTION(wcss1_dbg21), - FUNCTION(blsp_i2c0), -+ FUNCTION(wcss0_dbg22), -+ FUNCTION(wcss1_dbg22), -+ FUNCTION(wcss0_dbg23), -+ FUNCTION(wcss1_dbg23), -+ FUNCTION(blsp_spi0), - FUNCTION(blsp_i2c1), -+ FUNCTION(wcss0_dbg24), -+ FUNCTION(wcss1_dbg24), -+ FUNCTION(wcss0_dbg25), -+ FUNCTION(wcss1_dbg25), -+ FUNCTION(wcss0_dbg26), -+ FUNCTION(wcss1_dbg26), -+ FUNCTION(wcss0_dbg), -+ FUNCTION(wcss1_dbg), - FUNCTION(blsp_uart0), -- FUNCTION(blsp_spi1), -- FUNCTION(blsp_spi0), -+ FUNCTION(led0), -+ FUNCTION(wcss0_dbg28), -+ FUNCTION(wcss1_dbg28), -+ FUNCTION(led1), -+ FUNCTION(wcss0_dbg29), -+ FUNCTION(wcss1_dbg29), -+ FUNCTION(wifi0_uart1), -+ FUNCTION(wifi1_uart1), -+ FUNCTION(wcss0_dbg30), -+ FUNCTION(wcss1_dbg30), -+ FUNCTION(wcss0_dbg31), -+ FUNCTION(wcss1_dbg31), -+ FUNCTION(i2s_rx_mclk), -+ FUNCTION(wcss0_dbg16), -+ FUNCTION(wcss1_dbg16), -+ FUNCTION(wcss0_dbg17), -+ FUNCTION(wcss1_dbg17), -+ FUNCTION(rgmii0), -+ FUNCTION(sdio0), -+ FUNCTION(rgmii1), -+ FUNCTION(sdio1), -+ FUNCTION(rgmii2), -+ FUNCTION(i2s_tx_mclk), -+ FUNCTION(sdio2), -+ FUNCTION(rgmii3), -+ FUNCTION(i2s_tx_bclk), -+ FUNCTION(sdio3), -+ FUNCTION(rgmii_rx), -+ FUNCTION(i2s_tx_fsync), -+ FUNCTION(sdio_clk), -+ FUNCTION(rgmii_txc), -+ FUNCTION(i2s_td1), -+ FUNCTION(sdio_cmd), -+ FUNCTION(i2s_td2), -+ FUNCTION(sdio4), -+ FUNCTION(i2s_td3), -+ FUNCTION(sdio5), -+ FUNCTION(audio_pwm0), -+ FUNCTION(sdio6), -+ FUNCTION(audio_pwm1), -+ FUNCTION(wcss0_dbg27), -+ FUNCTION(wcss1_dbg27), -+ FUNCTION(sdio7), -+ FUNCTION(rgmii_rxc), -+ FUNCTION(audio_pwm2), -+ FUNCTION(rgmii_tx), -+ FUNCTION(audio_pwm3), -+ FUNCTION(boot2), -+ FUNCTION(i2s_spdif_in), -+ FUNCTION(i2s_spdif_out), -+ FUNCTION(rmii00), -+ FUNCTION(led2), -+ FUNCTION(rmii01), -+ FUNCTION(wifi0_wci), -+ FUNCTION(wifi1_wci), -+ FUNCTION(boot4), -+ FUNCTION(rmii0_tx), -+ FUNCTION(boot5), -+ FUNCTION(rmii0_rx), -+ FUNCTION(pcie_clk1), -+ FUNCTION(led3), -+ FUNCTION(sdio_cd), - }; - - static const struct msm_pingroup ipq4019_groups[] = { -- PINGROUP(0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(4, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(5, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(6, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(7, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(8, blsp_uart1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(9, blsp_uart1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(10, blsp_uart1, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(11, blsp_uart1, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(12, blsp_spi0, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(13, blsp_spi0, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(14, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(15, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(16, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(17, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(18, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(19, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(20, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(21, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(22, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(23, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(24, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(25, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(27, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(28, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(29, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(30, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(31, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(32, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(33, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(34, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(35, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(36, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(38, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(39, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(40, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(41, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(42, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(44, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(45, NA, blsp_spi1, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(46, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(47, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(48, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(49, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(50, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(51, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(52, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(53, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(54, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(55, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(56, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(57, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(58, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(59, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(60, NA, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(61, NA, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(63, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(65, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(66, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(67, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(68, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(69, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(0, jtag_tdi, smart0, i2s_rx_bclk, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA, NA, NA), -+ PINGROUP(1, jtag_tck, smart0, i2s_rx_fsync, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA, NA, NA), -+ PINGROUP(2, jtag_tms, smart0, i2s_rxd, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA, NA), -+ PINGROUP(3, jtag_tdo, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA), -+ PINGROUP(4, jtag_rst, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA), -+ PINGROUP(5, jtag_trst, smart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA), -+ PINGROUP(6, mdio0, NA, wcss0_dbg18, wcss1_dbg18, NA, qdss_tracedata_a, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(7, mdc, NA, wcss0_dbg19, wcss1_dbg19, NA, qdss_tracedata_a, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(8, blsp_uart1, wifi0_uart, wifi1_uart, smart1, NA, -+ wcss0_dbg20, wcss1_dbg20, NA, qdss_tracedata_a, NA, NA, NA, -+ NA, NA), -+ PINGROUP(9, blsp_uart1, wifi0_uart0, wifi1_uart0, smart1, wifi0_uart, -+ NA, wcss0_dbg21, wcss1_dbg21, NA, qdss_tracedata_a, NA, NA, -+ NA, NA), -+ PINGROUP(10, blsp_uart1, wifi0_uart0, wifi1_uart0, blsp_i2c0, NA, -+ wcss0_dbg22, wcss1_dbg22, NA, qdss_tracedata_a, NA, NA, NA, -+ NA, NA), -+ PINGROUP(11, blsp_uart1, wifi0_uart, wifi1_uart, blsp_i2c0, NA, -+ wcss0_dbg23, wcss1_dbg23, NA, qdss_tracedata_a, NA, NA, NA, -+ NA, NA), -+ PINGROUP(12, blsp_spi0, blsp_i2c1, NA, wcss0_dbg24, wcss1_dbg24, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(13, blsp_spi0, blsp_i2c1, NA, wcss0_dbg25, wcss1_dbg25, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(14, blsp_spi0, NA, wcss0_dbg26, wcss1_dbg26, NA, NA, NA, NA, -+ NA, NA, NA, NA, NA, NA), -+ PINGROUP(15, blsp_spi0, NA, wcss0_dbg, wcss1_dbg, NA, NA, NA, NA, NA, -+ NA, NA, NA, NA, NA), -+ PINGROUP(16, blsp_uart0, led0, smart1, NA, wcss0_dbg28, wcss1_dbg28, -+ NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA), -+ PINGROUP(17, blsp_uart0, led1, smart1, NA, wcss0_dbg29, wcss1_dbg29, -+ NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA), -+ PINGROUP(18, wifi0_uart1, wifi1_uart1, NA, wcss0_dbg30, wcss1_dbg30, -+ NA, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(19, wifi0_uart, wifi1_uart, NA, wcss0_dbg31, wcss1_dbg31, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(20, blsp_i2c0, i2s_rx_mclk, NA, wcss0_dbg16, wcss1_dbg16, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(21, blsp_i2c0, i2s_rx_bclk, NA, wcss0_dbg17, wcss1_dbg17, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(22, rgmii0, i2s_rx_fsync, NA, wcss0_dbg18, wcss1_dbg18, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(23, sdio0, rgmii1, i2s_rxd, NA, wcss0_dbg19, wcss1_dbg19, NA, -+ NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(24, sdio1, rgmii2, i2s_tx_mclk, NA, wcss0_dbg20, wcss1_dbg20, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(25, sdio2, rgmii3, i2s_tx_bclk, NA, wcss0_dbg21, wcss1_dbg21, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(26, sdio3, rgmii_rx, i2s_tx_fsync, NA, wcss0_dbg22, -+ wcss1_dbg22, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(27, sdio_clk, rgmii_txc, i2s_td1, NA, wcss0_dbg23, -+ wcss1_dbg23, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(28, sdio_cmd, rgmii0, i2s_td2, NA, wcss0_dbg24, wcss1_dbg24, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(29, sdio4, rgmii1, i2s_td3, NA, wcss0_dbg25, wcss1_dbg25, NA, -+ NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(30, sdio5, rgmii2, audio_pwm0, NA, wcss0_dbg26, wcss1_dbg26, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(31, sdio6, rgmii3, audio_pwm1, NA, wcss0_dbg27, wcss1_dbg27, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(32, sdio7, rgmii_rxc, audio_pwm2, NA, wcss0_dbg28, -+ wcss1_dbg28, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(33, rgmii_tx, audio_pwm3, NA, wcss0_dbg29, wcss1_dbg29, NA, -+ boot2, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(34, blsp_i2c1, i2s_spdif_in, NA, wcss0_dbg30, wcss1_dbg30, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(35, blsp_i2c1, i2s_spdif_out, NA, wcss0_dbg31, wcss1_dbg31, -+ NA, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(36, rmii00, led2, led0, NA, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA), -+ PINGROUP(37, rmii01, wifi0_wci, wifi1_wci, led1, NA, NA, wcss0_dbg16, -+ wcss1_dbg16, NA, qdss_tracedata_a, boot4, NA, NA, NA), -+ PINGROUP(38, rmii0_tx, led2, NA, NA, wcss0_dbg17, wcss1_dbg17, NA, -+ qdss_tracedata_a, boot5, NA, NA, NA, NA, NA), -+ PINGROUP(39, rmii0_rx, pcie_clk1, led3, NA, NA, wcss0_dbg18, -+ wcss1_dbg18, NA, NA, qdss_tracedata_a, NA, NA, NA, NA), -+ PINGROUP(40, rmii0_refclk, wifi0_rfsilient0, wifi1_rfsilient0, smart2, -+ led4, NA, NA, wcss0_dbg19, wcss1_dbg19, NA, NA, -+ qdss_tracedata_a, NA, NA), -+ PINGROUP(41, rmii00, wifi0_cal, wifi1_cal, smart2, NA, NA, wcss0_dbg20, -+ wcss1_dbg20, NA, NA, qdss_tracedata_a, NA, NA, NA), -+ PINGROUP(42, rmii01, wifi_wci0, NA, NA, wcss0_dbg21, wcss1_dbg21, NA, -+ NA, qdss_tracedata_a, NA, NA, NA, NA, NA), -+ PINGROUP(43, rmii0_dv, wifi_wci1, NA, NA, wcss0_dbg22, wcss1_dbg22, NA, -+ NA, qdss_tracedata_a, NA, NA, NA, NA, NA), -+ PINGROUP(44, rmii1_refclk, blsp_spi1, smart0, led5, NA, NA, -+ wcss0_dbg23, wcss1_dbg23, NA, NA, NA, NA, NA, NA), -+ PINGROUP(45, rmii10, blsp_spi1, blsp_spi0, smart0, led6, NA, NA, -+ wcss0_dbg24, wcss1_dbg24, NA, NA, NA, NA, NA), -+ PINGROUP(46, rmii11, blsp_spi1, smart0, led7, NA, NA, wcss0_dbg25, -+ wcss1_dbg25, NA, NA, NA, NA, NA, NA), -+ PINGROUP(47, rmii1_dv, blsp_spi1, smart0, led8, NA, NA, wcss0_dbg26, -+ wcss1_dbg26, NA, NA, NA, NA, NA, NA), -+ PINGROUP(48, rmii1_tx, aud_pin, smart2, led9, NA, NA, wcss0_dbg27, -+ wcss1_dbg27, NA, NA, NA, NA, NA, NA), -+ PINGROUP(49, rmii1_rx, aud_pin, smart2, led10, NA, NA, wcss0_dbg28, -+ wcss1_dbg28, NA, NA, NA, NA, NA, NA), -+ PINGROUP(50, rmii10, aud_pin, wifi0_rfsilient1, wifi1_rfsilient1, -+ led11, NA, NA, wcss0_dbg29, wcss1_dbg29, NA, NA, NA, NA, NA), -+ PINGROUP(51, rmii11, aud_pin, wifi0_cal, wifi1_cal, NA, NA, -+ wcss0_dbg30, wcss1_dbg30, NA, boot7, NA, NA, NA, NA), -+ PINGROUP(52, qpic_pad, mdc, pcie_clk, i2s_tx_mclk, NA, NA, wcss0_dbg31, -+ tm_clk0, wifi00, wifi10, NA, NA, NA, NA), -+ PINGROUP(53, qpic_pad, mdio1, i2s_tx_bclk, prng_rosc, dbg_out, tm0, -+ wifi01, wifi11, NA, NA, NA, NA, NA, NA), -+ PINGROUP(54, qpic_pad, blsp_spi0, i2s_td1, atest_char3, pmu0, NA, NA, -+ boot8, tm1, NA, NA, NA, NA, NA), -+ PINGROUP(55, qpic_pad, blsp_spi0, i2s_td2, atest_char2, pmu1, NA, NA, -+ boot9, tm2, NA, NA, NA, NA, NA), -+ PINGROUP(56, qpic_pad, blsp_spi0, i2s_td3, atest_char1, NA, tm_ack, -+ wifi03, wifi13, NA, NA, NA, NA, NA, NA), -+ PINGROUP(57, qpic_pad4, blsp_spi0, i2s_tx_fsync, atest_char0, NA, tm3, -+ wifi02, wifi12, NA, NA, NA, NA, NA, NA), -+ PINGROUP(58, qpic_pad5, led2, blsp_i2c0, smart3, smart1, i2s_rx_mclk, -+ NA, wcss0_dbg14, tm4, wifi04, wifi14, NA, NA, NA), -+ PINGROUP(59, qpic_pad6, blsp_i2c0, smart3, smart1, i2s_spdif_in, NA, -+ NA, wcss0_dbg15, qdss_tracectl_a, boot18, tm5, NA, NA, NA), -+ PINGROUP(60, qpic_pad7, blsp_uart0, smart1, smart3, led0, i2s_tx_bclk, -+ i2s_rx_bclk, atest_char, NA, wcss0_dbg4, qdss_traceclk_a, -+ boot19, tm6, NA), -+ PINGROUP(61, qpic_pad, blsp_uart0, smart1, smart3, led1, i2s_tx_fsync, -+ i2s_rx_fsync, NA, NA, wcss0_dbg5, qdss_cti_trig_out_a0, -+ boot14, tm7, NA), -+ PINGROUP(62, qpic_pad, chip_rst, wifi0_uart, wifi1_uart, i2s_spdif_out, -+ NA, NA, wcss0_dbg6, qdss_cti_trig_out_b0, boot11, tm8, NA, NA, -+ NA), -+ PINGROUP(63, qpic_pad, wifi0_uart1, wifi1_uart1, wifi1_uart, i2s_td1, -+ i2s_rxd, i2s_spdif_out, i2s_spdif_in, NA, wcss0_dbg7, -+ wcss1_dbg7, boot20, tm9, NA), -+ PINGROUP(64, qpic_pad1, audio_pwm0, NA, wcss0_dbg8, wcss1_dbg8, NA, NA, -+ NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(65, qpic_pad2, audio_pwm1, NA, wcss0_dbg9, wcss1_dbg9, NA, NA, -+ NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(66, qpic_pad3, audio_pwm2, NA, wcss0_dbg10, wcss1_dbg10, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(67, qpic_pad0, audio_pwm3, NA, wcss0_dbg11, wcss1_dbg11, NA, -+ NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(68, qpic_pad8, NA, wcss0_dbg12, wcss1_dbg12, NA, NA, NA, NA, -+ NA, NA, NA, NA, NA, NA), -+ PINGROUP(69, qpic_pad, NA, wcss0_dbg, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA, NA), - PINGROUP(70, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(71, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(72, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -@@ -433,7 +1526,8 @@ static const struct msm_pingroup ipq4019 - PINGROUP(95, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(96, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(97, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -- PINGROUP(98, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), -+ PINGROUP(98, wifi034, wifi134, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -+ NA, NA), - PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - }; - -@@ -460,6 +1554,7 @@ static const struct of_device_id ipq4019 - static struct platform_driver ipq4019_pinctrl_driver = { - .driver = { - .name = "ipq4019-pinctrl", -+ .owner = THIS_MODULE, - .of_match_table = ipq4019_pinctrl_of_match, - }, - .probe = ipq4019_pinctrl_probe, diff --git a/target/linux/ipq806x/patches-4.9/859-msm-pinctrl-Add-support-to-configure-ipq40xx-GPIO_PU.patch b/target/linux/ipq806x/patches-4.9/859-msm-pinctrl-Add-support-to-configure-ipq40xx-GPIO_PU.patch deleted file mode 100644 index 07cf01b26..000000000 --- a/target/linux/ipq806x/patches-4.9/859-msm-pinctrl-Add-support-to-configure-ipq40xx-GPIO_PU.patch +++ /dev/null @@ -1,236 +0,0 @@ -From e77af7de404eb464f7da9e0daeb8b362cc66a7ba Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Tue, 9 May 2017 11:45:00 +0530 -Subject: [PATCH] msm: pinctrl: Add support to configure ipq40xx GPIO_PULL bits - -GPIO_PULL bits configurations in TLMM_GPIO_CFG register -differs for IPQ40xx from rest of the other qcom SoC's. -This change add support to configure the msm_gpio_pull -bits for ipq40xx, It is required to fix the proper -configurations of gpio-pull bits for nand pins mux. - -IPQ40xx SoC: -2'b10: Internal pull up enable. -2'b11: Unsupport - -For other SoC's: -2'b10: Keeper -2'b11: Pull-Up - -Signed-off-by: Ram Chandra Jangir ---- - drivers/pinctrl/qcom/pinctrl-apq8064.c | 1 + - drivers/pinctrl/qcom/pinctrl-apq8084.c | 1 + - drivers/pinctrl/qcom/pinctrl-ipq4019.c | 8 ++++++++ - drivers/pinctrl/qcom/pinctrl-ipq8064.c | 1 + - drivers/pinctrl/qcom/pinctrl-mdm9615.c | 1 + - drivers/pinctrl/qcom/pinctrl-msm.c | 21 ++++++++------------- - drivers/pinctrl/qcom/pinctrl-msm.h | 19 +++++++++++++++++++ - drivers/pinctrl/qcom/pinctrl-msm8660.c | 1 + - drivers/pinctrl/qcom/pinctrl-msm8916.c | 1 + - drivers/pinctrl/qcom/pinctrl-msm8960.c | 1 + - drivers/pinctrl/qcom/pinctrl-msm8x74.c | 1 + - 11 files changed, 43 insertions(+), 13 deletions(-) - ---- a/drivers/pinctrl/qcom/pinctrl-apq8064.c -+++ b/drivers/pinctrl/qcom/pinctrl-apq8064.c -@@ -597,6 +597,7 @@ static const struct msm_pinctrl_soc_data - .groups = apq8064_groups, - .ngroups = ARRAY_SIZE(apq8064_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int apq8064_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-apq8084.c -+++ b/drivers/pinctrl/qcom/pinctrl-apq8084.c -@@ -1206,6 +1206,7 @@ static const struct msm_pinctrl_soc_data - .groups = apq8084_groups, - .ngroups = ARRAY_SIZE(apq8084_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int apq8084_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c -+++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c -@@ -1531,6 +1531,13 @@ static const struct msm_pingroup ipq4019 - PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), - }; - -+static const struct msm_pinctrl_gpio_pull ipq4019_gpio_pull = { -+ .no_pull = 0, -+ .pull_down = 1, -+ .keeper = 0, -+ .pull_up = 2, -+}; -+ - static const struct msm_pinctrl_soc_data ipq4019_pinctrl = { - .pins = ipq4019_pins, - .npins = ARRAY_SIZE(ipq4019_pins), -@@ -1539,6 +1546,7 @@ static const struct msm_pinctrl_soc_data - .groups = ipq4019_groups, - .ngroups = ARRAY_SIZE(ipq4019_groups), - .ngpios = 100, -+ .gpio_pull = &ipq4019_gpio_pull, - }; - - static int ipq4019_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c -+++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c -@@ -630,6 +630,7 @@ static const struct msm_pinctrl_soc_data - .groups = ipq8064_groups, - .ngroups = ARRAY_SIZE(ipq8064_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int ipq8064_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-mdm9615.c -+++ b/drivers/pinctrl/qcom/pinctrl-mdm9615.c -@@ -444,6 +444,7 @@ static const struct msm_pinctrl_soc_data - .groups = mdm9615_groups, - .ngroups = ARRAY_SIZE(mdm9615_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int mdm9615_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-msm.c -+++ b/drivers/pinctrl/qcom/pinctrl-msm.c -@@ -203,11 +203,6 @@ static int msm_config_reg(struct msm_pin - return 0; - } - --#define MSM_NO_PULL 0 --#define MSM_PULL_DOWN 1 --#define MSM_KEEPER 2 --#define MSM_PULL_UP 3 -- - static unsigned msm_regval_to_drive(u32 val) - { - return (val + 1) * 2; -@@ -238,16 +233,16 @@ static int msm_config_group_get(struct p - /* Convert register value to pinconf value */ - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: -- arg = arg == MSM_NO_PULL; -+ arg = arg == pctrl->soc->gpio_pull->no_pull; - break; - case PIN_CONFIG_BIAS_PULL_DOWN: -- arg = arg == MSM_PULL_DOWN; -+ arg = arg == pctrl->soc->gpio_pull->pull_down; - break; - case PIN_CONFIG_BIAS_BUS_HOLD: -- arg = arg == MSM_KEEPER; -+ arg = arg == pctrl->soc->gpio_pull->keeper; - break; - case PIN_CONFIG_BIAS_PULL_UP: -- arg = arg == MSM_PULL_UP; -+ arg = arg == pctrl->soc->gpio_pull->pull_up; - break; - case PIN_CONFIG_DRIVE_STRENGTH: - arg = msm_regval_to_drive(arg); -@@ -304,16 +299,16 @@ static int msm_config_group_set(struct p - /* Convert pinconf values to register values */ - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: -- arg = MSM_NO_PULL; -+ arg = pctrl->soc->gpio_pull->no_pull; - break; - case PIN_CONFIG_BIAS_PULL_DOWN: -- arg = MSM_PULL_DOWN; -+ arg = pctrl->soc->gpio_pull->pull_down; - break; - case PIN_CONFIG_BIAS_BUS_HOLD: -- arg = MSM_KEEPER; -+ arg = pctrl->soc->gpio_pull->keeper; - break; - case PIN_CONFIG_BIAS_PULL_UP: -- arg = MSM_PULL_UP; -+ arg = pctrl->soc->gpio_pull->pull_up; - break; - case PIN_CONFIG_DRIVE_STRENGTH: - /* Check for invalid values */ ---- a/drivers/pinctrl/qcom/pinctrl-msm.h -+++ b/drivers/pinctrl/qcom/pinctrl-msm.h -@@ -98,6 +98,16 @@ struct msm_pingroup { - }; - - /** -+ * struct msm_pinctrl_gpio_pull - pinctrl pull value bit field descriptor -+ */ -+struct msm_pinctrl_gpio_pull { -+ unsigned no_pull; -+ unsigned pull_down; -+ unsigned keeper; -+ unsigned pull_up; -+}; -+ -+/** - * struct msm_pinctrl_soc_data - Qualcomm pin controller driver configuration - * @pins: An array describing all pins the pin controller affects. - * @npins: The number of entries in @pins. -@@ -106,6 +116,7 @@ struct msm_pingroup { - * @groups: An array describing all pin groups the pin SoC supports. - * @ngroups: The numbmer of entries in @groups. - * @ngpio: The number of pingroups the driver should expose as GPIOs. -+ * @gpio_pull_val: The pull value bit field descriptor. - */ - struct msm_pinctrl_soc_data { - const struct pinctrl_pin_desc *pins; -@@ -115,6 +126,14 @@ struct msm_pinctrl_soc_data { - const struct msm_pingroup *groups; - unsigned ngroups; - unsigned ngpios; -+ const struct msm_pinctrl_gpio_pull *gpio_pull; -+}; -+ -+static const struct msm_pinctrl_gpio_pull msm_gpio_pull = { -+ .no_pull = 0, -+ .pull_down = 1, -+ .keeper = 2, -+ .pull_up = 3, - }; - - int msm_pinctrl_probe(struct platform_device *pdev, ---- a/drivers/pinctrl/qcom/pinctrl-msm8660.c -+++ b/drivers/pinctrl/qcom/pinctrl-msm8660.c -@@ -979,6 +979,7 @@ static const struct msm_pinctrl_soc_data - .groups = msm8660_groups, - .ngroups = ARRAY_SIZE(msm8660_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int msm8660_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-msm8916.c -+++ b/drivers/pinctrl/qcom/pinctrl-msm8916.c -@@ -967,6 +967,7 @@ static const struct msm_pinctrl_soc_data - .groups = msm8916_groups, - .ngroups = ARRAY_SIZE(msm8916_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int msm8916_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-msm8960.c -+++ b/drivers/pinctrl/qcom/pinctrl-msm8960.c -@@ -1244,6 +1244,7 @@ static const struct msm_pinctrl_soc_data - .groups = msm8960_groups, - .ngroups = ARRAY_SIZE(msm8960_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int msm8960_pinctrl_probe(struct platform_device *pdev) ---- a/drivers/pinctrl/qcom/pinctrl-msm8x74.c -+++ b/drivers/pinctrl/qcom/pinctrl-msm8x74.c -@@ -1069,6 +1069,7 @@ static const struct msm_pinctrl_soc_data - .groups = msm8x74_groups, - .ngroups = ARRAY_SIZE(msm8x74_groups), - .ngpios = NUM_GPIO_PINGROUPS, -+ .gpio_pull = &msm_gpio_pull, - }; - - static int msm8x74_pinctrl_probe(struct platform_device *pdev) diff --git a/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch b/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch deleted file mode 100644 index 5a3c02c70..000000000 --- a/target/linux/ipq806x/patches-4.9/860-qcom-mtd-nand-Add-bam_dma-support-in-qcom_nand-drive.patch +++ /dev/null @@ -1,370 +0,0 @@ -From 074036f9de6b8c5fc642e8e2540950f6a35aa804 Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Thu, 20 Apr 2017 10:31:10 +0530 -Subject: [PATCH] qcom: mtd: nand: Add bam_dma support in qcom_nand driver - -The current driver only support ADM DMA so this patch adds the -BAM DMA support in current NAND driver with compatible string -qcom,ebi2-nandc-bam. -Added bam channels and data buffers, NAND BAM uses 3 channels: -command, data tx and data rx, while ADM uses only single channel. -So this patch adds the BAM channel in device tree and using the -same in NAND driver allocation function. - -Signed-off-by: Ram Chandra Jangir ---- - .../devicetree/bindings/mtd/qcom_nandc.txt | 69 +++++++-- - drivers/mtd/nand/qcom_nandc.c | 160 +++++++++++++++++---- - 2 files changed, 190 insertions(+), 39 deletions(-) - ---- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt -+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt -@@ -1,21 +1,26 @@ - * Qualcomm NAND controller - - Required properties: --- compatible: should be "qcom,ipq806x-nand" -+- compatible: "qcom,ipq806x-nand" for IPQ8064 which uses -+ ADM DMA. -+ "qcom,ebi2-nand-bam" - nand drivers using BAM DMA -+ like IPQ4019. - - reg: MMIO address range - - clocks: must contain core clock and always on clock - - clock-names: must contain "core" for the core clock and "aon" for the - always on clock - - dmas: DMA specifier, consisting of a phandle to the ADM DMA -- controller node and the channel number to be used for -- NAND. Refer to dma.txt and qcom_adm.txt for more details --- dma-names: must be "rxtx" --- qcom,cmd-crci: must contain the ADM command type CRCI block instance -- number specified for the NAND controller on the given -- platform --- qcom,data-crci: must contain the ADM data type CRCI block instance -- number specified for the NAND controller on the given -- platform -+ or BAM DMA controller node and the channel number to -+ be used for NAND. Refer to dma.txt, qcom_adm.txt(ADM) -+ and qcom_bam_dma.txt(BAM) for more details -+- dma-names: "rxtx" - ADM -+ "tx", "rx", "cmd" - BAM -+- qcom,cmd-crci: Only required for ADM DMA. must contain the ADM command -+ type CRCI block instance number specified for the NAND -+ controller on the given platform. -+- qcom,data-crci: Only required for ADM DMA. must contain the ADM data -+ type CRCI block instance number specified for the NAND -+ controller on the given platform. - - #address-cells: <1> - subnodes give the chip-select number - - #size-cells: <0> - -@@ -44,7 +49,7 @@ partition.txt for more detail. - Example: - - nand@1ac00000 { -- compatible = "qcom,ebi2-nandc"; -+ compatible = "qcom,ipq806x-nand","qcom.qcom_nand"; - reg = <0x1ac00000 0x800>; - - clocks = <&gcc EBI2_CLK>, -@@ -58,6 +63,48 @@ nand@1ac00000 { - - #address-cells = <1>; - #size-cells = <0>; -+ -+ nandcs@0 { -+ compatible = "qcom,nandcs"; -+ reg = <0>; -+ -+ nand-ecc-strength = <4>; -+ nand-ecc-step-size = <512>; -+ nand-bus-width = <8>; -+ -+ partitions { -+ compatible = "fixed-partitions"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ partition@0 { -+ label = "boot-nand"; -+ reg = <0 0x58a0000>; -+ }; -+ -+ partition@58a0000 { -+ label = "fs-nand"; -+ reg = <0x58a0000 0x4000000>; -+ }; -+ }; -+ }; -+}; -+ -+nand@79B0000 { -+ compatible = "qcom,ebi2-nandc-bam"; -+ reg = <0x79B0000 0x1000>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&qpicbam 0>, -+ <&qpicbam 1>, -+ <&qpicbam 2>; -+ dma-names = "tx", "rx", "cmd"; -+ -+ #address-cells = <1>; -+ #size-cells = <0>; - - nandcs@0 { - compatible = "qcom,nandcs"; ---- a/drivers/mtd/nand/qcom_nandc.c -+++ b/drivers/mtd/nand/qcom_nandc.c -@@ -234,6 +234,7 @@ struct nandc_regs { - * by upper layers directly - * @buf_size/count/start: markers for chip->read_buf/write_buf functions - * @reg_read_buf: local buffer for reading back registers via DMA -+ * @reg_read_buf_phys: contains dma address for register read buffer - * @reg_read_pos: marker for data read in reg_read_buf - * - * @regs: a contiguous chunk of memory for DMA register -@@ -242,7 +243,10 @@ struct nandc_regs { - * @cmd1/vld: some fixed controller register values - * @ecc_modes: supported ECC modes by the current controller, - * initialized via DT match data -- */ -+ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used -+ * @dma_bam_enabled: flag to tell whether nand controller is using -+ * bam dma -+*/ - struct qcom_nand_controller { - struct nand_hw_control controller; - struct list_head host_list; -@@ -255,17 +259,28 @@ struct qcom_nand_controller { - struct clk *core_clk; - struct clk *aon_clk; - -- struct dma_chan *chan; -- unsigned int cmd_crci; -- unsigned int data_crci; - struct list_head desc_list; -+ union { -+ struct { -+ struct dma_chan *tx_chan; -+ struct dma_chan *rx_chan; -+ struct dma_chan *cmd_chan; -+ }; -+ struct { -+ struct dma_chan *chan; -+ unsigned int cmd_crci; -+ unsigned int data_crci; -+ }; -+ }; - - u8 *data_buffer; -+ bool dma_bam_enabled; - int buf_size; - int buf_count; - int buf_start; - - __le32 *reg_read_buf; -+ dma_addr_t reg_read_buf_phys; - int reg_read_pos; - - struct nandc_regs *regs; -@@ -324,6 +339,17 @@ struct qcom_nand_host { - u32 clrreadstatus; - }; - -+/* -+ * This data type corresponds to the nand driver data which will be used at -+ * driver probe time -+ * @ecc_modes - ecc mode for nand -+ * @dma_bam_enabled - whether this driver is using bam -+ */ -+struct qcom_nand_driver_data { -+ u32 ecc_modes; -+ bool dma_bam_enabled; -+}; -+ - static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) - { - return container_of(chip, struct qcom_nand_host, chip); -@@ -1949,16 +1975,46 @@ static int qcom_nandc_alloc(struct qcom_ - if (!nandc->regs) - return -ENOMEM; - -- nandc->reg_read_buf = devm_kzalloc(nandc->dev, -- MAX_REG_RD * sizeof(*nandc->reg_read_buf), -- GFP_KERNEL); -- if (!nandc->reg_read_buf) -- return -ENOMEM; -+ if (!nandc->dma_bam_enabled) { -+ nandc->reg_read_buf = devm_kzalloc(nandc->dev, -+ MAX_REG_RD * -+ sizeof(*nandc->reg_read_buf), -+ GFP_KERNEL); - -- nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); -- if (!nandc->chan) { -- dev_err(nandc->dev, "failed to request slave channel\n"); -- return -ENODEV; -+ if (!nandc->reg_read_buf) -+ return -ENOMEM; -+ -+ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); -+ if (!nandc->chan) { -+ dev_err(nandc->dev, "failed to request slave channel\n"); -+ return -ENODEV; -+ } -+ } else { -+ nandc->reg_read_buf = dmam_alloc_coherent(nandc->dev, -+ MAX_REG_RD * -+ sizeof(*nandc->reg_read_buf), -+ &nandc->reg_read_buf_phys, GFP_KERNEL); -+ -+ if (!nandc->reg_read_buf) -+ return -ENOMEM; -+ -+ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); -+ if (!nandc->tx_chan) { -+ dev_err(nandc->dev, "failed to request tx channel\n"); -+ return -ENODEV; -+ } -+ -+ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); -+ if (!nandc->rx_chan) { -+ dev_err(nandc->dev, "failed to request rx channel\n"); -+ return -ENODEV; -+ } -+ -+ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); -+ if (!nandc->cmd_chan) { -+ dev_err(nandc->dev, "failed to request cmd channel\n"); -+ return -ENODEV; -+ } - } - - INIT_LIST_HEAD(&nandc->desc_list); -@@ -1971,8 +2027,35 @@ static int qcom_nandc_alloc(struct qcom_ - - static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) - { -- dma_release_channel(nandc->chan); --} -+ if (nandc->dma_bam_enabled) { -+ if (nandc->tx_chan) -+ dma_release_channel(nandc->tx_chan); -+ -+ if (nandc->rx_chan) -+ dma_release_channel(nandc->rx_chan); -+ -+ if (nandc->cmd_chan) -+ dma_release_channel(nandc->tx_chan); -+ -+ if (nandc->reg_read_buf) -+ dmam_free_coherent(nandc->dev, MAX_REG_RD * -+ sizeof(*nandc->reg_read_buf), -+ nandc->reg_read_buf, -+ nandc->reg_read_buf_phys); -+ } else { -+ if (nandc->chan) -+ dma_release_channel(nandc->chan); -+ -+ if (nandc->reg_read_buf) -+ devm_kfree(nandc->dev, nandc->reg_read_buf); -+ } -+ -+ if (nandc->regs) -+ devm_kfree(nandc->dev, nandc->regs); -+ -+ if (nandc->data_buffer) -+ devm_kfree(nandc->dev, nandc->data_buffer); -+ } - - /* one time setup of a few nand controller registers */ - static int qcom_nandc_setup(struct qcom_nand_controller *nandc) -@@ -2010,6 +2093,8 @@ static int qcom_nand_host_init(struct qc - mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs); - mtd->owner = THIS_MODULE; - mtd->dev.parent = dev; -+ mtd->priv = chip; -+ chip->priv = nandc; - - chip->cmdfunc = qcom_nandc_command; - chip->select_chip = qcom_nandc_select_chip; -@@ -2057,16 +2142,20 @@ static int qcom_nandc_parse_dt(struct pl - struct device_node *np = nandc->dev->of_node; - int ret; - -- ret = of_property_read_u32(np, "qcom,cmd-crci", &nandc->cmd_crci); -- if (ret) { -- dev_err(nandc->dev, "command CRCI unspecified\n"); -- return ret; -- } -+ if (!nandc->dma_bam_enabled) { -+ ret = of_property_read_u32(np, "qcom,cmd-crci", -+ &nandc->cmd_crci); -+ if (ret) { -+ dev_err(nandc->dev, "command CRCI unspecified\n"); -+ return ret; -+ } - -- ret = of_property_read_u32(np, "qcom,data-crci", &nandc->data_crci); -- if (ret) { -- dev_err(nandc->dev, "data CRCI unspecified\n"); -- return ret; -+ ret = of_property_read_u32(np, "qcom,data-crci", -+ &nandc->data_crci); -+ if (ret) { -+ dev_err(nandc->dev, "data CRCI unspecified\n"); -+ return ret; -+ } - } - - return 0; -@@ -2081,6 +2170,7 @@ static int qcom_nandc_probe(struct platf - struct device_node *dn = dev->of_node, *child; - struct resource *res; - int ret; -+ struct qcom_nand_driver_data *driver_data; - - nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); - if (!nandc) -@@ -2095,7 +2185,10 @@ static int qcom_nandc_probe(struct platf - return -ENODEV; - } - -- nandc->ecc_modes = (unsigned long)dev_data; -+ driver_data = (struct qcom_nand_driver_data *)dev_data; -+ -+ nandc->ecc_modes = driver_data->ecc_modes; -+ nandc->dma_bam_enabled = driver_data->dma_bam_enabled; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nandc->base = devm_ioremap_resource(dev, res); -@@ -2187,7 +2280,15 @@ static int qcom_nandc_remove(struct plat - return 0; - } - --#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) -+struct qcom_nand_driver_data ebi2_nandc_bam_data = { -+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), -+ .dma_bam_enabled = true, -+}; -+ -+struct qcom_nand_driver_data ebi2_nandc_data = { -+ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), -+ .dma_bam_enabled = false, -+}; - - /* - * data will hold a struct pointer containing more differences once we support -@@ -2195,7 +2296,10 @@ static int qcom_nandc_remove(struct plat - */ - static const struct of_device_id qcom_nandc_of_match[] = { - { .compatible = "qcom,ipq806x-nand", -- .data = (void *)EBI2_NANDC_ECC_MODES, -+ .data = (void *) &ebi2_nandc_data, -+ }, -+ { .compatible = "qcom,ebi2-nandc-bam", -+ .data = (void *) &ebi2_nandc_bam_data, - }, - {} - }; diff --git a/target/linux/ipq806x/patches-4.9/861-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch b/target/linux/ipq806x/patches-4.9/861-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch deleted file mode 100644 index 4b9f67220..000000000 --- a/target/linux/ipq806x/patches-4.9/861-qcom-mtd-nand-Added-bam-transaction-and-support-addi.patch +++ /dev/null @@ -1,1267 +0,0 @@ -From 645c7805f2602569263d7ac78050b2c9e91e3377 Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Thu, 20 Apr 2017 10:23:00 +0530 -Subject: [PATCH] qcom: mtd: nand: Added bam transaction and support - additional CSRs - -This patch adds the following for NAND BAM DMA support - - Bam transaction which will be used for any NAND request. - It contains the array of command elements, command and - data sgl. This transaction will be resetted before every - request. - - Allocation function for NAND BAM transaction which will be - called only once at probe time. - - Reset function for NAND BAM transaction which will be called - before any new NAND request. - - Add support for additional CSRs. - NAND_READ_LOCATION - page offset for reading in BAM DMA mode - NAND_ERASED_CW_DETECT_CFG - status for erased code words - NAND_BUFFER_STATUS - status for ECC - -Signed-off-by: Abhishek Sahu -Signed-off-by: Ram Chandra Jangir ---- - drivers/mtd/nand/qcom_nandc.c | 631 +++++++++++++++++++++++++++++++++++---- - include/linux/dma/qcom_bam_dma.h | 149 +++++++++ - 2 files changed, 721 insertions(+), 59 deletions(-) - create mode 100644 include/linux/dma/qcom_bam_dma.h - ---- a/drivers/mtd/nand/qcom_nandc.c -+++ b/drivers/mtd/nand/qcom_nandc.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - - /* NANDc reg offsets */ - #define NAND_FLASH_CMD 0x00 -@@ -53,6 +54,8 @@ - #define NAND_VERSION 0xf08 - #define NAND_READ_LOCATION_0 0xf20 - #define NAND_READ_LOCATION_1 0xf24 -+#define NAND_READ_LOCATION_2 0xf28 -+#define NAND_READ_LOCATION_3 0xf2c - - /* dummy register offsets, used by write_reg_dma */ - #define NAND_DEV_CMD1_RESTORE 0xdead -@@ -135,6 +138,11 @@ - #define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) - #define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) - -+/* NAND_READ_LOCATION_n bits */ -+#define READ_LOCATION_OFFSET 0 -+#define READ_LOCATION_SIZE 16 -+#define READ_LOCATION_LAST 31 -+ - /* Version Mask */ - #define NAND_VERSION_MAJOR_MASK 0xf0000000 - #define NAND_VERSION_MAJOR_SHIFT 28 -@@ -156,6 +164,9 @@ - #define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ - ERASE_START_VLD | SEQ_READ_START_VLD) - -+/* NAND_CTRL bits */ -+#define BAM_MODE_EN BIT(0) -+ - /* - * the NAND controller performs reads/writes with ECC in 516 byte chunks. - * the driver calls the chunks 'step' or 'codeword' interchangeably -@@ -177,12 +188,77 @@ - #define ECC_BCH_4BIT BIT(2) - #define ECC_BCH_8BIT BIT(3) - -+/* Flags used for BAM DMA desc preparation*/ -+/* Don't set the EOT in current tx sgl */ -+#define DMA_DESC_FLAG_NO_EOT (0x0001) -+/* Set the NWD flag in current sgl */ -+#define DMA_DESC_FLAG_BAM_NWD (0x0002) -+/* Close current sgl and start writing in another sgl */ -+#define DMA_DESC_FLAG_BAM_NEXT_SGL (0x0004) -+/* -+ * Erased codeword status is being used two times in single transfer so this -+ * flag will determine the current value of erased codeword status register -+ */ -+#define DMA_DESC_ERASED_CW_SET (0x0008) -+ -+/* Returns the dma address for reg read buffer */ -+#define REG_BUF_DMA_ADDR(chip, vaddr) \ -+ ((chip)->reg_read_buf_phys + \ -+ ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) -+ -+/* Returns the nand register physical address */ -+#define NAND_REG_PHYS_ADDRESS(chip, addr) \ -+ ((chip)->base_dma + (addr)) -+ -+/* command element array size in bam transaction */ -+#define BAM_CMD_ELEMENT_SIZE (256) -+/* command sgl size in bam transaction */ -+#define BAM_CMD_SGL_SIZE (256) -+/* data sgl size in bam transaction */ -+#define BAM_DATA_SGL_SIZE (128) -+ -+/* -+ * This data type corresponds to the BAM transaction which will be used for any -+ * nand request. -+ * @bam_ce - the array of bam command elements -+ * @cmd_sgl - sgl for nand bam command pipe -+ * @tx_sgl - sgl for nand bam consumer pipe -+ * @rx_sgl - sgl for nand bam producer pipe -+ * @bam_ce_index - the index in bam_ce which is available for next sgl request -+ * @pre_bam_ce_index - the index in bam_ce which marks the start position ce -+ * for current sgl. It will be used for size calculation -+ * for current sgl -+ * @cmd_sgl_cnt - no of entries in command sgl. -+ * @tx_sgl_cnt - no of entries in tx sgl. -+ * @rx_sgl_cnt - no of entries in rx sgl. -+ */ -+struct bam_transaction { -+ struct bam_cmd_element bam_ce[BAM_CMD_ELEMENT_SIZE]; -+ struct qcom_bam_sgl cmd_sgl[BAM_CMD_SGL_SIZE]; -+ struct qcom_bam_sgl tx_sgl[BAM_DATA_SGL_SIZE]; -+ struct qcom_bam_sgl rx_sgl[BAM_DATA_SGL_SIZE]; -+ uint32_t bam_ce_index; -+ uint32_t pre_bam_ce_index; -+ uint32_t cmd_sgl_cnt; -+ uint32_t tx_sgl_cnt; -+ uint32_t rx_sgl_cnt; -+}; -+ -+/** -+ * This data type corresponds to the nand dma descriptor -+ * @list - list for desc_info -+ * @dir - DMA transfer direction -+ * @sgl - sgl which will be used for single sgl dma descriptor -+ * @dma_desc - low level dma engine descriptor -+ * @bam_desc_data - used for bam desc mappings -+ */ - struct desc_info { - struct list_head node; - - enum dma_data_direction dir; - struct scatterlist sgl; - struct dma_async_tx_descriptor *dma_desc; -+ struct qcom_bam_custom_data bam_desc_data; - }; - - /* -@@ -210,6 +286,13 @@ struct nandc_regs { - __le32 orig_vld; - - __le32 ecc_buf_cfg; -+ __le32 read_location0; -+ __le32 read_location1; -+ __le32 read_location2; -+ __le32 read_location3; -+ -+ __le32 erased_cw_detect_cfg_clr; -+ __le32 erased_cw_detect_cfg_set; - }; - - /* -@@ -225,6 +308,7 @@ struct nandc_regs { - * @aon_clk: another controller clock - * - * @chan: dma channel -+ * @bam_txn: contains the bam transaction address - * @cmd_crci: ADM DMA CRCI for command flow control - * @data_crci: ADM DMA CRCI for data flow control - * @desc_list: DMA descriptor list (list of desc_infos) -@@ -250,6 +334,7 @@ struct nandc_regs { - struct qcom_nand_controller { - struct nand_hw_control controller; - struct list_head host_list; -+ struct bam_transaction *bam_txn; - - struct device *dev; - -@@ -350,6 +435,45 @@ struct qcom_nand_driver_data { - bool dma_bam_enabled; - }; - -+/* Allocates and Initializes the BAM transaction */ -+struct bam_transaction *alloc_bam_transaction( -+ struct qcom_nand_controller *nandc) -+{ -+ struct bam_transaction *bam_txn; -+ -+ bam_txn = kzalloc(sizeof(*bam_txn), GFP_KERNEL); -+ -+ if (!bam_txn) -+ return NULL; -+ -+ bam_txn->bam_ce_index = 0; -+ bam_txn->pre_bam_ce_index = 0; -+ bam_txn->cmd_sgl_cnt = 0; -+ bam_txn->tx_sgl_cnt = 0; -+ bam_txn->rx_sgl_cnt = 0; -+ -+ qcom_bam_sg_init_table(bam_txn->cmd_sgl, BAM_CMD_SGL_SIZE); -+ qcom_bam_sg_init_table(bam_txn->tx_sgl, BAM_DATA_SGL_SIZE); -+ qcom_bam_sg_init_table(bam_txn->rx_sgl, BAM_DATA_SGL_SIZE); -+ -+ return bam_txn; -+} -+ -+/* Clears the BAM transaction index */ -+void clear_bam_transaction(struct qcom_nand_controller *nandc) -+{ -+ struct bam_transaction *bam_txn = nandc->bam_txn; -+ -+ if (!nandc->dma_bam_enabled) -+ return; -+ -+ bam_txn->bam_ce_index = 0; -+ bam_txn->pre_bam_ce_index = 0; -+ bam_txn->cmd_sgl_cnt = 0; -+ bam_txn->tx_sgl_cnt = 0; -+ bam_txn->rx_sgl_cnt = 0; -+} -+ - static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) - { - return container_of(chip, struct qcom_nand_host, chip); -@@ -406,6 +530,16 @@ static __le32 *offset_to_nandc_reg(struc - return ®s->orig_vld; - case NAND_EBI2_ECC_BUF_CFG: - return ®s->ecc_buf_cfg; -+ case NAND_BUFFER_STATUS: -+ return ®s->clrreadstatus; -+ case NAND_READ_LOCATION_0: -+ return ®s->read_location0; -+ case NAND_READ_LOCATION_1: -+ return ®s->read_location1; -+ case NAND_READ_LOCATION_2: -+ return ®s->read_location2; -+ case NAND_READ_LOCATION_3: -+ return ®s->read_location3; - default: - return NULL; - } -@@ -447,7 +581,7 @@ static void update_rw_regs(struct qcom_n - { - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); -- u32 cmd, cfg0, cfg1, ecc_bch_cfg; -+ u32 cmd, cfg0, cfg1, ecc_bch_cfg, read_location0; - - if (read) { - if (host->use_ecc) -@@ -464,12 +598,20 @@ static void update_rw_regs(struct qcom_n - - cfg1 = host->cfg1; - ecc_bch_cfg = host->ecc_bch_cfg; -+ if (read) -+ read_location0 = (0 << READ_LOCATION_OFFSET) | -+ (host->cw_data << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST); - } else { - cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; - - cfg1 = host->cfg1_raw; - ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ if (read) -+ read_location0 = (0 << READ_LOCATION_OFFSET) | -+ (host->cw_size << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST); - } - - nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); -@@ -480,8 +622,104 @@ static void update_rw_regs(struct qcom_n - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); -+ -+ if (read) -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, read_location0); -+} -+ -+/* -+ * Prepares the command descriptor for BAM DMA which will be used for NAND -+ * register read and write. The command descriptor requires the command -+ * to be formed in command element type so this function uses the command -+ * element from bam transaction ce array and fills the same with required -+ * data. A single SGL can contain multiple command elements so -+ * DMA_DESC_FLAG_BAM_NEXT_SGL will be used for starting the separate SGL -+ * after the current command element. -+ */ -+static int prep_dma_desc_command(struct qcom_nand_controller *nandc, bool read, -+ int reg_off, const void *vaddr, -+ int size, unsigned int flags) -+{ -+ int bam_ce_size; -+ int i; -+ struct bam_cmd_element *bam_ce_buffer; -+ struct bam_transaction *bam_txn = nandc->bam_txn; -+ -+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_index]; -+ -+ /* fill the command desc */ -+ for (i = 0; i < size; i++) { -+ if (read) { -+ qcom_prep_bam_ce(&bam_ce_buffer[i], -+ NAND_REG_PHYS_ADDRESS(nandc, reg_off + 4 * i), -+ BAM_READ_COMMAND, -+ REG_BUF_DMA_ADDR(nandc, -+ (unsigned int *)vaddr + i)); -+ } else { -+ qcom_prep_bam_ce(&bam_ce_buffer[i], -+ NAND_REG_PHYS_ADDRESS(nandc, reg_off + 4 * i), -+ BAM_WRITE_COMMAND, -+ *((unsigned int *)vaddr + i)); -+ } -+ } -+ -+ /* use the separate sgl after this command */ -+ if (flags & DMA_DESC_FLAG_BAM_NEXT_SGL) { -+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->pre_bam_ce_index]; -+ bam_txn->bam_ce_index += size; -+ bam_ce_size = (bam_txn->bam_ce_index - -+ bam_txn->pre_bam_ce_index) * -+ sizeof(struct bam_cmd_element); -+ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].sgl, -+ bam_ce_buffer, -+ bam_ce_size); -+ if (flags & DMA_DESC_FLAG_BAM_NWD) -+ bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].dma_flags = -+ DESC_FLAG_NWD | DESC_FLAG_CMD; -+ else -+ bam_txn->cmd_sgl[bam_txn->cmd_sgl_cnt].dma_flags = -+ DESC_FLAG_CMD; -+ -+ bam_txn->cmd_sgl_cnt++; -+ bam_txn->pre_bam_ce_index = bam_txn->bam_ce_index; -+ } else { -+ bam_txn->bam_ce_index += size; -+ } -+ -+ return 0; - } - -+/* -+ * Prepares the data descriptor for BAM DMA which will be used for NAND -+ * data read and write. -+ */ -+static int prep_dma_desc_data_bam(struct qcom_nand_controller *nandc, bool read, -+ int reg_off, const void *vaddr, -+ int size, unsigned int flags) -+{ -+ struct bam_transaction *bam_txn = nandc->bam_txn; -+ -+ if (read) { -+ sg_set_buf(&bam_txn->rx_sgl[bam_txn->rx_sgl_cnt].sgl, -+ vaddr, size); -+ bam_txn->rx_sgl[bam_txn->rx_sgl_cnt].dma_flags = 0; -+ bam_txn->rx_sgl_cnt++; -+ } else { -+ sg_set_buf(&bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].sgl, -+ vaddr, size); -+ if (flags & DMA_DESC_FLAG_NO_EOT) -+ bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].dma_flags = 0; -+ else -+ bam_txn->tx_sgl[bam_txn->tx_sgl_cnt].dma_flags = -+ DESC_FLAG_EOT; -+ -+ bam_txn->tx_sgl_cnt++; -+ } -+ -+ return 0; -+} -+ -+/* Prepares the dma desciptor for adm dma engine */ - static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, - int reg_off, const void *vaddr, int size, - bool flow_control) -@@ -560,7 +798,7 @@ err: - * @num_regs: number of registers to read - */ - static int read_reg_dma(struct qcom_nand_controller *nandc, int first, -- int num_regs) -+ int num_regs, unsigned int flags) - { - bool flow_control = false; - void *vaddr; -@@ -569,10 +807,18 @@ static int read_reg_dma(struct qcom_nand - if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) - flow_control = true; - -- size = num_regs * sizeof(u32); - vaddr = nandc->reg_read_buf + nandc->reg_read_pos; - nandc->reg_read_pos += num_regs; - -+ if (nandc->dma_bam_enabled) { -+ size = num_regs; -+ -+ return prep_dma_desc_command(nandc, true, first, vaddr, size, -+ flags); -+ } -+ -+ size = num_regs * sizeof(u32); -+ - return prep_dma_desc(nandc, true, first, vaddr, size, flow_control); - } - -@@ -584,7 +830,7 @@ static int read_reg_dma(struct qcom_nand - * @num_regs: number of registers to write - */ - static int write_reg_dma(struct qcom_nand_controller *nandc, int first, -- int num_regs) -+ int num_regs, unsigned int flags) - { - bool flow_control = false; - struct nandc_regs *regs = nandc->regs; -@@ -596,12 +842,29 @@ static int write_reg_dma(struct qcom_nan - if (first == NAND_FLASH_CMD) - flow_control = true; - -+ if (first == NAND_ERASED_CW_DETECT_CFG) { -+ if (flags & DMA_DESC_ERASED_CW_SET) -+ vaddr = ®s->erased_cw_detect_cfg_set; -+ else -+ vaddr = ®s->erased_cw_detect_cfg_clr; -+ } -+ -+ if (first == NAND_EXEC_CMD) -+ flags |= DMA_DESC_FLAG_BAM_NWD; -+ - if (first == NAND_DEV_CMD1_RESTORE) - first = NAND_DEV_CMD1; - - if (first == NAND_DEV_CMD_VLD_RESTORE) - first = NAND_DEV_CMD_VLD; - -+ if (nandc->dma_bam_enabled) { -+ size = num_regs; -+ -+ return prep_dma_desc_command(nandc, false, first, vaddr, size, -+ flags); -+ } -+ - size = num_regs * sizeof(u32); - - return prep_dma_desc(nandc, false, first, vaddr, size, flow_control); -@@ -616,8 +879,12 @@ static int write_reg_dma(struct qcom_nan - * @size: DMA transaction size in bytes - */ - static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, -- const u8 *vaddr, int size) -+ const u8 *vaddr, int size, unsigned int flags) - { -+ if (nandc->dma_bam_enabled) -+ return prep_dma_desc_data_bam(nandc, true, reg_off, vaddr, size, -+ flags); -+ - return prep_dma_desc(nandc, true, reg_off, vaddr, size, false); - } - -@@ -630,8 +897,12 @@ static int read_data_dma(struct qcom_nan - * @size: DMA transaction size in bytes - */ - static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, -- const u8 *vaddr, int size) -+ const u8 *vaddr, int size, unsigned int flags) - { -+ if (nandc->dma_bam_enabled) -+ return prep_dma_desc_data_bam(nandc, false, reg_off, vaddr, -+ size, flags); -+ - return prep_dma_desc(nandc, false, reg_off, vaddr, size, false); - } - -@@ -641,14 +912,57 @@ static int write_data_dma(struct qcom_na - */ - static void config_cw_read(struct qcom_nand_controller *nandc) - { -- write_reg_dma(nandc, NAND_FLASH_CMD, 3); -- write_reg_dma(nandc, NAND_DEV0_CFG0, 3); -- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); - -- write_reg_dma(nandc, NAND_EXEC_CMD, 1); -+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); -+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); -+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); -+ -+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); -+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, -+ DMA_DESC_ERASED_CW_SET); -+ if (nandc->dma_bam_enabled) -+ write_reg_dma(nandc, NAND_READ_LOCATION_0, 1, -+ DMA_DESC_FLAG_BAM_NEXT_SGL); - -- read_reg_dma(nandc, NAND_FLASH_STATUS, 2); -- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1); -+ -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NWD | -+ DMA_DESC_FLAG_BAM_NEXT_SGL); -+ -+ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); -+ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, -+ DMA_DESC_FLAG_BAM_NEXT_SGL); -+} -+ -+/* -+ * Helpers to prepare DMA descriptors for configuring registers -+ * before reading a NAND page with BAM. -+ */ -+static void config_bam_page_read(struct qcom_nand_controller *nandc) -+{ -+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); -+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); -+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); -+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); -+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, -+ DMA_DESC_ERASED_CW_SET | -+ DMA_DESC_FLAG_BAM_NEXT_SGL); -+} -+ -+/* -+ * Helpers to prepare DMA descriptors for configuring registers -+ * before reading each codeword in NAND page with BAM. -+ */ -+static void config_bam_cw_read(struct qcom_nand_controller *nandc) -+{ -+ if (nandc->dma_bam_enabled) -+ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, 0); -+ -+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ -+ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); -+ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, -+ DMA_DESC_FLAG_BAM_NEXT_SGL); - } - - /* -@@ -657,19 +971,20 @@ static void config_cw_read(struct qcom_n - */ - static void config_cw_write_pre(struct qcom_nand_controller *nandc) - { -- write_reg_dma(nandc, NAND_FLASH_CMD, 3); -- write_reg_dma(nandc, NAND_DEV0_CFG0, 3); -- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); -+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, 0); -+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); -+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, -+ DMA_DESC_FLAG_BAM_NEXT_SGL); - } - - static void config_cw_write_post(struct qcom_nand_controller *nandc) - { -- write_reg_dma(nandc, NAND_EXEC_CMD, 1); -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - -- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); -+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - -- write_reg_dma(nandc, NAND_FLASH_STATUS, 1); -- write_reg_dma(nandc, NAND_READ_STATUS, 1); -+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); -+ write_reg_dma(nandc, NAND_READ_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - } - - /* -@@ -683,6 +998,8 @@ static int nandc_param(struct qcom_nand_ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - -+ clear_bam_transaction(nandc); -+ - /* - * NAND_CMD_PARAM is called before we know much about the FLASH chip - * in use. we configure the controller to perform a raw read of 512 -@@ -715,9 +1032,13 @@ static int nandc_param(struct qcom_nand_ - - nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); - nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (0 << READ_LOCATION_OFFSET) | -+ (512 << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); - -- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1); -- write_reg_dma(nandc, NAND_DEV_CMD1, 1); -+ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); -+ write_reg_dma(nandc, NAND_DEV_CMD1, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - - nandc->buf_count = 512; - memset(nandc->data_buffer, 0xff, nandc->buf_count); -@@ -725,11 +1046,12 @@ static int nandc_param(struct qcom_nand_ - config_cw_read(nandc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, -- nandc->buf_count); -+ nandc->buf_count, 0); - - /* restore CMD1 and VLD regs */ -- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1); -- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1); -+ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); -+ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, -+ DMA_DESC_FLAG_BAM_NEXT_SGL); - - return 0; - } -@@ -740,6 +1062,8 @@ static int erase_block(struct qcom_nand_ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - -+ clear_bam_transaction(nandc); -+ - nandc_set_reg(nandc, NAND_FLASH_CMD, - BLOCK_ERASE | PAGE_ACC | LAST_PAGE); - nandc_set_reg(nandc, NAND_ADDR0, page_addr); -@@ -751,14 +1075,15 @@ static int erase_block(struct qcom_nand_ - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - -- write_reg_dma(nandc, NAND_FLASH_CMD, 3); -- write_reg_dma(nandc, NAND_DEV0_CFG0, 2); -- write_reg_dma(nandc, NAND_EXEC_CMD, 1); - -- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); -+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ -+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - -- write_reg_dma(nandc, NAND_FLASH_STATUS, 1); -- write_reg_dma(nandc, NAND_READ_STATUS, 1); -+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); -+ write_reg_dma(nandc, NAND_READ_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - - return 0; - } -@@ -772,16 +1097,19 @@ static int read_id(struct qcom_nand_host - if (column == -1) - return 0; - -+ clear_bam_transaction(nandc); -+ - nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); - nandc_set_reg(nandc, NAND_ADDR0, column); - nandc_set_reg(nandc, NAND_ADDR1, 0); -- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); -+ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, -+ nandc->dma_bam_enabled ? 0 : DM_EN); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - -- write_reg_dma(nandc, NAND_FLASH_CMD, 4); -- write_reg_dma(nandc, NAND_EXEC_CMD, 1); -+ write_reg_dma(nandc, NAND_FLASH_CMD, 4, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - -- read_reg_dma(nandc, NAND_READ_ID, 1); -+ read_reg_dma(nandc, NAND_READ_ID, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - - return 0; - } -@@ -792,28 +1120,108 @@ static int reset(struct qcom_nand_host * - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - -+ clear_bam_transaction(nandc); -+ - nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - -- write_reg_dma(nandc, NAND_FLASH_CMD, 1); -- write_reg_dma(nandc, NAND_EXEC_CMD, 1); -+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); -+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - -- read_reg_dma(nandc, NAND_FLASH_STATUS, 1); -+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, DMA_DESC_FLAG_BAM_NEXT_SGL); - - return 0; - } - -+static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, -+ struct dma_chan *chan, -+ struct qcom_bam_sgl *bam_sgl, -+ int sgl_cnt, -+ enum dma_transfer_direction direction) -+{ -+ struct desc_info *desc; -+ struct dma_async_tx_descriptor *dma_desc; -+ -+ if (!qcom_bam_map_sg(nandc->dev, bam_sgl, sgl_cnt, direction)) { -+ dev_err(nandc->dev, "failure in mapping sgl\n"); -+ return -ENOMEM; -+ } -+ -+ desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) { -+ qcom_bam_unmap_sg(nandc->dev, bam_sgl, sgl_cnt, direction); -+ return -ENOMEM; -+ } -+ -+ -+ desc->bam_desc_data.dir = direction; -+ desc->bam_desc_data.sgl_cnt = sgl_cnt; -+ desc->bam_desc_data.bam_sgl = bam_sgl; -+ -+ dma_desc = dmaengine_prep_dma_custom_mapping(chan, -+ &desc->bam_desc_data, -+ 0); -+ -+ if (!dma_desc) { -+ dev_err(nandc->dev, "failure in cmd prep desc\n"); -+ qcom_bam_unmap_sg(nandc->dev, bam_sgl, sgl_cnt, direction); -+ kfree(desc); -+ return -EINVAL; -+ } -+ -+ desc->dma_desc = dma_desc; -+ -+ list_add_tail(&desc->node, &nandc->desc_list); -+ -+ return 0; -+ -+} -+ - /* helpers to submit/free our list of dma descriptors */ - static int submit_descs(struct qcom_nand_controller *nandc) - { - struct desc_info *desc; - dma_cookie_t cookie = 0; -+ struct bam_transaction *bam_txn = nandc->bam_txn; -+ int r; -+ -+ if (nandc->dma_bam_enabled) { -+ if (bam_txn->rx_sgl_cnt) { -+ r = prepare_bam_async_desc(nandc, nandc->rx_chan, -+ bam_txn->rx_sgl, bam_txn->rx_sgl_cnt, -+ DMA_DEV_TO_MEM); -+ if (r) -+ return r; -+ } -+ -+ if (bam_txn->tx_sgl_cnt) { -+ r = prepare_bam_async_desc(nandc, nandc->tx_chan, -+ bam_txn->tx_sgl, bam_txn->tx_sgl_cnt, -+ DMA_MEM_TO_DEV); -+ if (r) -+ return r; -+ } -+ -+ r = prepare_bam_async_desc(nandc, nandc->cmd_chan, -+ bam_txn->cmd_sgl, bam_txn->cmd_sgl_cnt, -+ DMA_MEM_TO_DEV); -+ if (r) -+ return r; -+ } - - list_for_each_entry(desc, &nandc->desc_list, node) - cookie = dmaengine_submit(desc->dma_desc); - -- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) -- return -ETIMEDOUT; -+ if (nandc->dma_bam_enabled) { -+ dma_async_issue_pending(nandc->tx_chan); -+ dma_async_issue_pending(nandc->rx_chan); -+ -+ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) -+ return -ETIMEDOUT; -+ } else { -+ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) -+ return -ETIMEDOUT; -+ } - - return 0; - } -@@ -824,7 +1232,16 @@ static void free_descs(struct qcom_nand_ - - list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { - list_del(&desc->node); -- dma_unmap_sg(nandc->dev, &desc->sgl, 1, desc->dir); -+ -+ if (nandc->dma_bam_enabled) -+ qcom_bam_unmap_sg(nandc->dev, -+ desc->bam_desc_data.bam_sgl, -+ desc->bam_desc_data.sgl_cnt, -+ desc->bam_desc_data.dir); -+ else -+ dma_unmap_sg(nandc->dev, &desc->sgl, 1, -+ desc->dir); -+ - kfree(desc); - } - } -@@ -1135,6 +1552,9 @@ static int read_page_ecc(struct qcom_nan - struct nand_ecc_ctrl *ecc = &chip->ecc; - int i, ret; - -+ if (nandc->dma_bam_enabled) -+ config_bam_page_read(nandc); -+ - /* queue cmd descs for each codeword */ - for (i = 0; i < ecc->steps; i++) { - int data_size, oob_size; -@@ -1148,11 +1568,36 @@ static int read_page_ecc(struct qcom_nan - oob_size = host->ecc_bytes_hw + host->spare_bytes; - } - -- config_cw_read(nandc); -+ if (nandc->dma_bam_enabled) { -+ if (data_buf && oob_buf) { -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (0 << READ_LOCATION_OFFSET) | -+ (data_size << READ_LOCATION_SIZE) | -+ (0 << READ_LOCATION_LAST)); -+ nandc_set_reg(nandc, NAND_READ_LOCATION_1, -+ (data_size << READ_LOCATION_OFFSET) | -+ (oob_size << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); -+ } else if (data_buf) { -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (0 << READ_LOCATION_OFFSET) | -+ (data_size << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); -+ } else { -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (data_size << READ_LOCATION_OFFSET) | -+ (oob_size << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); -+ } -+ -+ config_bam_cw_read(nandc); -+ } else { -+ config_cw_read(nandc); -+ } - - if (data_buf) - read_data_dma(nandc, FLASH_BUF_ACC, data_buf, -- data_size); -+ data_size, 0); - - /* - * when ecc is enabled, the controller doesn't read the real -@@ -1168,7 +1613,7 @@ static int read_page_ecc(struct qcom_nan - *oob_buf++ = 0xff; - - read_data_dma(nandc, FLASH_BUF_ACC + data_size, -- oob_buf, oob_size); -+ oob_buf, oob_size, 0); - } - - if (data_buf) -@@ -1207,10 +1652,14 @@ static int copy_last_cw(struct qcom_nand - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (0 << READ_LOCATION_OFFSET) | -+ (size << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); - - config_cw_read(nandc); - -- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size); -+ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) -@@ -1233,6 +1682,7 @@ static int qcom_nandc_read_page(struct m - data_buf = buf; - oob_buf = oob_required ? chip->oob_poi : NULL; - -+ clear_bam_transaction(nandc); - ret = read_page_ecc(host, data_buf, oob_buf); - if (ret) { - dev_err(nandc->dev, "failure to read page\n"); -@@ -1252,13 +1702,19 @@ static int qcom_nandc_read_page_raw(stru - u8 *data_buf, *oob_buf; - struct nand_ecc_ctrl *ecc = &chip->ecc; - int i, ret; -+ int read_location; - - data_buf = buf; - oob_buf = chip->oob_poi; - - host->use_ecc = false; -+ -+ clear_bam_transaction(nandc); - update_rw_regs(host, ecc->steps, true); - -+ if (nandc->dma_bam_enabled) -+ config_bam_page_read(nandc); -+ - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; -@@ -1276,21 +1732,49 @@ static int qcom_nandc_read_page_raw(stru - oob_size2 = host->ecc_bytes_hw + host->spare_bytes; - } - -- config_cw_read(nandc); -+ if (nandc->dma_bam_enabled) { -+ read_location = 0; -+ nandc_set_reg(nandc, NAND_READ_LOCATION_0, -+ (read_location << READ_LOCATION_OFFSET) | -+ (data_size1 << READ_LOCATION_SIZE) | -+ (0 << READ_LOCATION_LAST)); -+ read_location += data_size1; -+ -+ nandc_set_reg(nandc, NAND_READ_LOCATION_1, -+ (read_location << READ_LOCATION_OFFSET) | -+ (oob_size1 << READ_LOCATION_SIZE) | -+ (0 << READ_LOCATION_LAST)); -+ read_location += oob_size1; -+ -+ nandc_set_reg(nandc, NAND_READ_LOCATION_2, -+ (read_location << READ_LOCATION_OFFSET) | -+ (data_size2 << READ_LOCATION_SIZE) | -+ (0 << READ_LOCATION_LAST)); -+ read_location += data_size2; -+ -+ nandc_set_reg(nandc, NAND_READ_LOCATION_3, -+ (read_location << READ_LOCATION_OFFSET) | -+ (oob_size2 << READ_LOCATION_SIZE) | -+ (1 << READ_LOCATION_LAST)); -+ -+ config_bam_cw_read(nandc); -+ } else { -+ config_cw_read(nandc); -+ } - -- read_data_dma(nandc, reg_off, data_buf, data_size1); -+ read_data_dma(nandc, reg_off, data_buf, data_size1, 0); - reg_off += data_size1; - data_buf += data_size1; - -- read_data_dma(nandc, reg_off, oob_buf, oob_size1); -+ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); - reg_off += oob_size1; - oob_buf += oob_size1; - -- read_data_dma(nandc, reg_off, data_buf, data_size2); -+ read_data_dma(nandc, reg_off, data_buf, data_size2, 0); - reg_off += data_size2; - data_buf += data_size2; - -- read_data_dma(nandc, reg_off, oob_buf, oob_size2); -+ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); - oob_buf += oob_size2; - } - -@@ -1313,6 +1797,7 @@ static int qcom_nandc_read_oob(struct mt - int ret; - - clear_read_regs(nandc); -+ clear_bam_transaction(nandc); - - host->use_ecc = true; - set_address(host, 0, page); -@@ -1336,6 +1821,7 @@ static int qcom_nandc_write_page(struct - int i, ret; - - clear_read_regs(nandc); -+ clear_bam_transaction(nandc); - - data_buf = (u8 *)buf; - oob_buf = chip->oob_poi; -@@ -1357,7 +1843,8 @@ static int qcom_nandc_write_page(struct - - config_cw_write_pre(nandc); - -- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size); -+ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, -+ i == (ecc->steps - 1) ? DMA_DESC_FLAG_NO_EOT : 0); - - /* - * when ECC is enabled, we don't really need to write anything -@@ -1370,7 +1857,7 @@ static int qcom_nandc_write_page(struct - oob_buf += host->bbm_size; - - write_data_dma(nandc, FLASH_BUF_ACC + data_size, -- oob_buf, oob_size); -+ oob_buf, oob_size, 0); - } - - config_cw_write_post(nandc); -@@ -1400,6 +1887,7 @@ static int qcom_nandc_write_page_raw(str - int i, ret; - - clear_read_regs(nandc); -+ clear_bam_transaction(nandc); - - data_buf = (u8 *)buf; - oob_buf = chip->oob_poi; -@@ -1426,19 +1914,22 @@ static int qcom_nandc_write_page_raw(str - - config_cw_write_pre(nandc); - -- write_data_dma(nandc, reg_off, data_buf, data_size1); -+ write_data_dma(nandc, reg_off, data_buf, data_size1, -+ DMA_DESC_FLAG_NO_EOT); - reg_off += data_size1; - data_buf += data_size1; - -- write_data_dma(nandc, reg_off, oob_buf, oob_size1); -+ write_data_dma(nandc, reg_off, oob_buf, oob_size1, -+ DMA_DESC_FLAG_NO_EOT); - reg_off += oob_size1; - oob_buf += oob_size1; - -- write_data_dma(nandc, reg_off, data_buf, data_size2); -+ write_data_dma(nandc, reg_off, data_buf, data_size2, -+ DMA_DESC_FLAG_NO_EOT); - reg_off += data_size2; - data_buf += data_size2; - -- write_data_dma(nandc, reg_off, oob_buf, oob_size2); -+ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); - oob_buf += oob_size2; - - config_cw_write_post(nandc); -@@ -1474,6 +1965,7 @@ static int qcom_nandc_write_oob(struct m - - host->use_ecc = true; - -+ clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - return ret; -@@ -1493,7 +1985,7 @@ static int qcom_nandc_write_oob(struct m - - config_cw_write_pre(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, -- data_size + oob_size); -+ data_size + oob_size, 0); - config_cw_write_post(nandc); - - ret = submit_descs(nandc); -@@ -1531,6 +2023,7 @@ static int qcom_nandc_block_bad(struct m - */ - host->use_ecc = false; - -+ clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - goto err; -@@ -1561,6 +2054,7 @@ static int qcom_nandc_block_markbad(stru - int page, ret, status = 0; - - clear_read_regs(nandc); -+ clear_bam_transaction(nandc); - - /* - * to mark the BBM as bad, we flash the entire last codeword with 0s. -@@ -1577,7 +2071,8 @@ static int qcom_nandc_block_markbad(stru - update_rw_regs(host, 1, false); - - config_cw_write_pre(nandc); -- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size); -+ write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, -+ host->cw_size, 0); - config_cw_write_post(nandc); - - ret = submit_descs(nandc); -@@ -1937,6 +2432,8 @@ static int qcom_nand_host_setup(struct q - - host->clrflashstatus = FS_READY_BSY_N; - host->clrreadstatus = 0xc0; -+ nandc->regs->erased_cw_detect_cfg_clr = CLR_ERASED_PAGE_DET; -+ nandc->regs->erased_cw_detect_cfg_set = SET_ERASED_PAGE_DET; - - dev_dbg(nandc->dev, - "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", -@@ -2015,6 +2512,12 @@ static int qcom_nandc_alloc(struct qcom_ - dev_err(nandc->dev, "failed to request cmd channel\n"); - return -ENODEV; - } -+ -+ nandc->bam_txn = alloc_bam_transaction(nandc); -+ if (!nandc->bam_txn) { -+ dev_err(nandc->dev, "failed to allocate bam transaction\n"); -+ return -ENOMEM; -+ } - } - - INIT_LIST_HEAD(&nandc->desc_list); -@@ -2050,6 +2553,9 @@ static void qcom_nandc_unalloc(struct qc - devm_kfree(nandc->dev, nandc->reg_read_buf); - } - -+ if (nandc->bam_txn) -+ devm_kfree(nandc->dev, nandc->bam_txn); -+ - if (nandc->regs) - devm_kfree(nandc->dev, nandc->regs); - -@@ -2060,12 +2566,19 @@ static void qcom_nandc_unalloc(struct qc - /* one time setup of a few nand controller registers */ - static int qcom_nandc_setup(struct qcom_nand_controller *nandc) - { -+ u32 nand_ctrl; -+ - /* kill onenand */ - nandc_write(nandc, SFLASHC_BURST_CFG, 0); - nandc_write(nandc, NAND_DEV_CMD_VLD, NAND_DEV_CMD_VLD_VAL); - -- /* enable ADM DMA */ -- nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); -+ /* enable ADM or BAM DMA */ -+ if (!nandc->dma_bam_enabled) { -+ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); -+ } else { -+ nand_ctrl = nandc_read(nandc, NAND_CTRL); -+ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); -+ } - - /* save the original values of these registers */ - nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1); ---- /dev/null -+++ b/include/linux/dma/qcom_bam_dma.h -@@ -0,0 +1,149 @@ -+/* -+ * 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. -+ */ -+ -+#ifndef _QCOM_BAM_DMA_H -+#define _QCOM_BAM_DMA_H -+ -+#include -+ -+#define DESC_FLAG_INT BIT(15) -+#define DESC_FLAG_EOT BIT(14) -+#define DESC_FLAG_EOB BIT(13) -+#define DESC_FLAG_NWD BIT(12) -+#define DESC_FLAG_CMD BIT(11) -+ -+/* -+ * QCOM BAM DMA SGL struct -+ * -+ * @sgl: DMA SGL -+ * @dma_flags: BAM DMA flags -+ */ -+struct qcom_bam_sgl { -+ struct scatterlist sgl; -+ unsigned int dma_flags; -+}; -+ -+/* -+ * This data type corresponds to the native Command Element -+ * supported by BAM DMA Engine. -+ * -+ * @addr - register address. -+ * @command - command type. -+ * @data - for write command: content to be written into peripheral register. -+ * for read command: dest addr to write peripheral register value to. -+ * @mask - register mask. -+ * @reserved - for future usage. -+ * -+ */ -+struct bam_cmd_element { -+ __le32 addr:24; -+ __le32 command:8; -+ __le32 data; -+ __le32 mask; -+ __le32 reserved; -+}; -+ -+/* -+ * This enum indicates the command type in a command element -+ */ -+enum bam_command_type { -+ BAM_WRITE_COMMAND = 0, -+ BAM_READ_COMMAND, -+}; -+ -+/* -+ * qcom_bam_sg_init_table - Init QCOM BAM SGL -+ * @bam_sgl: bam sgl -+ * @nents: number of entries in bam sgl -+ * -+ * This function performs the initialization for each SGL in BAM SGL -+ * with generic SGL API. -+ */ -+static inline void qcom_bam_sg_init_table(struct qcom_bam_sgl *bam_sgl, -+ unsigned int nents) -+{ -+ int i; -+ -+ for (i = 0; i < nents; i++) -+ sg_init_table(&bam_sgl[i].sgl, 1); -+} -+ -+/* -+ * qcom_bam_unmap_sg - Unmap QCOM BAM SGL -+ * @dev: device for which unmapping needs to be done -+ * @bam_sgl: bam sgl -+ * @nents: number of entries in bam sgl -+ * @dir: dma transfer direction -+ * -+ * This function performs the DMA unmapping for each SGL in BAM SGL -+ * with generic SGL API. -+ */ -+static inline void qcom_bam_unmap_sg(struct device *dev, -+ struct qcom_bam_sgl *bam_sgl, int nents, enum dma_data_direction dir) -+{ -+ int i; -+ -+ for (i = 0; i < nents; i++) -+ dma_unmap_sg(dev, &bam_sgl[i].sgl, 1, dir); -+} -+ -+/* -+ * qcom_bam_map_sg - Map QCOM BAM SGL -+ * @dev: device for which mapping needs to be done -+ * @bam_sgl: bam sgl -+ * @nents: number of entries in bam sgl -+ * @dir: dma transfer direction -+ * -+ * This function performs the DMA mapping for each SGL in BAM SGL -+ * with generic SGL API. -+ * -+ * returns 0 on error and > 0 on success -+ */ -+static inline int qcom_bam_map_sg(struct device *dev, -+ struct qcom_bam_sgl *bam_sgl, int nents, enum dma_data_direction dir) -+{ -+ int i, ret = 0; -+ -+ for (i = 0; i < nents; i++) { -+ ret = dma_map_sg(dev, &bam_sgl[i].sgl, 1, dir); -+ if (!ret) -+ break; -+ } -+ -+ /* unmap the mapped sgl from previous loop in case of error */ -+ if (!ret) -+ qcom_bam_unmap_sg(dev, bam_sgl, i, dir); -+ -+ return ret; -+} -+ -+/* -+ * qcom_prep_bam_ce - Wrapper function to prepare a single BAM command element -+ * with the data that is passed to this function. -+ * @bam_ce: bam command element -+ * @addr: target address -+ * @command: command in bam_command_type -+ * @data: actual data for write and dest addr for read -+ */ -+static inline void qcom_prep_bam_ce(struct bam_cmd_element *bam_ce, -+ uint32_t addr, uint32_t command, uint32_t data) -+{ -+ bam_ce->addr = cpu_to_le32(addr); -+ bam_ce->command = cpu_to_le32(command); -+ bam_ce->data = cpu_to_le32(data); -+ bam_ce->mask = 0xFFFFFFFF; -+} -+#endif diff --git a/target/linux/ipq806x/patches-4.9/862-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch b/target/linux/ipq806x/patches-4.9/862-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch deleted file mode 100644 index 796938f2d..000000000 --- a/target/linux/ipq806x/patches-4.9/862-dmaengine-qcom-bam_dma-Add-custom-data-mapping.patch +++ /dev/null @@ -1,209 +0,0 @@ -From 5a7ccdf845d64b385affdcffaf2defbe9848be15 Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir -Date: Thu, 20 Apr 2017 10:39:00 +0530 -Subject: [PATCH] dmaengine: qcom: bam_dma: Add custom data mapping - -Add a new function to support for preparing DMA descriptor -for custom data. - -Signed-off-by: Abhishek Sahu -Signed-off-by: Ram Chandra Jangir ---- - drivers/dma/qcom/bam_dma.c | 97 +++++++++++++++++++++++++++++++++++++--- - include/linux/dma/qcom_bam_dma.h | 14 ++++++ - include/linux/dmaengine.h | 14 ++++++ - 3 files changed, 119 insertions(+), 6 deletions(-) - ---- a/drivers/dma/qcom/bam_dma.c -+++ b/drivers/dma/qcom/bam_dma.c -@@ -49,6 +49,7 @@ - #include - #include - #include -+#include - - #include "../dmaengine.h" - #include "../virt-dma.h" -@@ -61,11 +62,6 @@ struct bam_desc_hw { - - #define BAM_DMA_AUTOSUSPEND_DELAY 100 - --#define DESC_FLAG_INT BIT(15) --#define DESC_FLAG_EOT BIT(14) --#define DESC_FLAG_EOB BIT(13) --#define DESC_FLAG_NWD BIT(12) -- - struct bam_async_desc { - struct virt_dma_desc vd; - -@@ -670,6 +666,93 @@ err_out: - } - - /** -+ * bam_prep_dma_custom_mapping - Prep DMA descriptor from custom data -+ * -+ * @chan: dma channel -+ * @data: custom data -+ * @flags: DMA flags -+ */ -+static struct dma_async_tx_descriptor *bam_prep_dma_custom_mapping( -+ struct dma_chan *chan, -+ void *data, unsigned long flags) -+{ -+ struct bam_chan *bchan = to_bam_chan(chan); -+ struct bam_device *bdev = bchan->bdev; -+ struct bam_async_desc *async_desc; -+ struct qcom_bam_custom_data *desc_data = data; -+ u32 i; -+ struct bam_desc_hw *desc; -+ unsigned int num_alloc = 0; -+ -+ -+ if (!is_slave_direction(desc_data->dir)) { -+ dev_err(bdev->dev, "invalid dma direction\n"); -+ return NULL; -+ } -+ -+ /* calculate number of required entries */ -+ for (i = 0; i < desc_data->sgl_cnt; i++) -+ num_alloc += DIV_ROUND_UP( -+ sg_dma_len(&desc_data->bam_sgl[i].sgl), BAM_FIFO_SIZE); -+ -+ /* allocate enough room to accommodate the number of entries */ -+ async_desc = kzalloc(sizeof(*async_desc) + -+ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); -+ -+ if (!async_desc) -+ goto err_out; -+ -+ if (flags & DMA_PREP_FENCE) -+ async_desc->flags |= DESC_FLAG_NWD; -+ -+ if (flags & DMA_PREP_INTERRUPT) -+ async_desc->flags |= DESC_FLAG_EOT; -+ else -+ async_desc->flags |= DESC_FLAG_INT; -+ -+ async_desc->num_desc = num_alloc; -+ async_desc->curr_desc = async_desc->desc; -+ async_desc->dir = desc_data->dir; -+ -+ /* fill in temporary descriptors */ -+ desc = async_desc->desc; -+ for (i = 0; i < desc_data->sgl_cnt; i++) { -+ unsigned int remainder; -+ unsigned int curr_offset = 0; -+ -+ remainder = sg_dma_len(&desc_data->bam_sgl[i].sgl); -+ -+ do { -+ desc->addr = cpu_to_le32( -+ sg_dma_address(&desc_data->bam_sgl[i].sgl) + -+ curr_offset); -+ -+ if (desc_data->bam_sgl[i].dma_flags) -+ desc->flags |= cpu_to_le16( -+ desc_data->bam_sgl[i].dma_flags); -+ -+ if (remainder > BAM_FIFO_SIZE) { -+ desc->size = cpu_to_le16(BAM_FIFO_SIZE); -+ remainder -= BAM_FIFO_SIZE; -+ curr_offset += BAM_FIFO_SIZE; -+ } else { -+ desc->size = cpu_to_le16(remainder); -+ remainder = 0; -+ } -+ -+ async_desc->length += desc->size; -+ desc++; -+ } while (remainder > 0); -+ } -+ -+ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); -+ -+err_out: -+ kfree(async_desc); -+ return NULL; -+} -+ -+/** - * bam_dma_terminate_all - terminate all transactions on a channel - * @bchan: bam dma channel - * -@@ -960,7 +1043,7 @@ static void bam_start_dma(struct bam_cha - - /* set any special flags on the last descriptor */ - if (async_desc->num_desc == async_desc->xfer_len) -- desc[async_desc->xfer_len - 1].flags = -+ desc[async_desc->xfer_len - 1].flags |= - cpu_to_le16(async_desc->flags); - else - desc[async_desc->xfer_len - 1].flags |= -@@ -1237,6 +1320,8 @@ static int bam_dma_probe(struct platform - bdev->common.device_alloc_chan_resources = bam_alloc_chan; - bdev->common.device_free_chan_resources = bam_free_chan; - bdev->common.device_prep_slave_sg = bam_prep_slave_sg; -+ bdev->common.device_prep_dma_custom_mapping = -+ bam_prep_dma_custom_mapping; - bdev->common.device_config = bam_slave_config; - bdev->common.device_pause = bam_pause; - bdev->common.device_resume = bam_resume; ---- a/include/linux/dma/qcom_bam_dma.h -+++ b/include/linux/dma/qcom_bam_dma.h -@@ -65,6 +65,19 @@ enum bam_command_type { - }; - - /* -+ * QCOM BAM DMA custom data -+ * -+ * @sgl_cnt: number of sgl in bam_sgl -+ * @dir: DMA data transfer direction -+ * @bam_sgl: BAM SGL pointer -+ */ -+struct qcom_bam_custom_data { -+ u32 sgl_cnt; -+ enum dma_transfer_direction dir; -+ struct qcom_bam_sgl *bam_sgl; -+}; -+ -+/* - * qcom_bam_sg_init_table - Init QCOM BAM SGL - * @bam_sgl: bam sgl - * @nents: number of entries in bam sgl ---- a/include/linux/dmaengine.h -+++ b/include/linux/dmaengine.h -@@ -692,6 +692,8 @@ struct dma_filter { - * be called after period_len bytes have been transferred. - * @device_prep_interleaved_dma: Transfer expression in a generic way. - * @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address -+ * @device_prep_dma_custom_mapping: prepares a dma operation from dma driver -+ * specific custom data - * @device_config: Pushes a new configuration to a channel, return 0 or an error - * code - * @device_pause: Pauses any transfer happening on a channel. Returns -@@ -783,6 +785,9 @@ struct dma_device { - struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)( - struct dma_chan *chan, dma_addr_t dst, u64 data, - unsigned long flags); -+ struct dma_async_tx_descriptor *(*device_prep_dma_custom_mapping)( -+ struct dma_chan *chan, void *data, -+ unsigned long flags); - - int (*device_config)(struct dma_chan *chan, - struct dma_slave_config *config); -@@ -899,6 +904,15 @@ static inline struct dma_async_tx_descri - src_sg, src_nents, flags); - } - -+static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_custom_mapping( -+ struct dma_chan *chan, -+ void *data, -+ unsigned long flags) -+{ -+ return chan->device->device_prep_dma_custom_mapping(chan, data, -+ flags); -+} -+ - /** - * dmaengine_terminate_all() - Terminate all active DMA transfers - * @chan: The channel for which to terminate the transfers diff --git a/target/linux/ipq806x/patches-4.9/864-00-v3-1-2-dts-ipq4019-Fix-pinctrl-node-name.patch b/target/linux/ipq806x/patches-4.9/864-00-v3-1-2-dts-ipq4019-Fix-pinctrl-node-name.patch deleted file mode 100644 index dbd5537f8..000000000 --- a/target/linux/ipq806x/patches-4.9/864-00-v3-1-2-dts-ipq4019-Fix-pinctrl-node-name.patch +++ /dev/null @@ -1,44 +0,0 @@ -From patchwork Mon Jul 3 07:47:12 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,1/2] dts: ipq4019: Fix pinctrl node name -From: Varadarajan Narayanan -X-Patchwork-Id: 9822099 -Message-Id: <1499068033-24000-2-git-send-email-varada@codeaurora.org> -To: andy.gross@linaro.org, david.brown@linaro.org, robh+dt@kernel.org, - mark.rutland@arm.com, linux@armlinux.org.uk, - linux-arm-msm@vger.kernel.org, - linux-soc@vger.kernel.org, devicetree@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org -Cc: Varadarajan Narayanan -Date: Mon, 3 Jul 2017 13:17:12 +0530 - -Signed-off-by: Varadarajan Narayanan ---- - arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 2 +- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - ---- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -40,7 +40,7 @@ - clock-frequency = <48000000>; - }; - -- pinctrl@0x01000000 { -+ pinctrl@1000000 { - serial_pins: serial_pinmux { - mux { - pins = "gpio60", "gpio61"; ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -149,7 +149,7 @@ - reg = <0x1800000 0x60000>; - }; - -- tlmm: pinctrl@0x01000000 { -+ tlmm: pinctrl@1000000 { - compatible = "qcom,ipq4019-pinctrl"; - reg = <0x01000000 0x300000>; - gpio-controller; diff --git a/target/linux/ipq806x/patches-4.9/864-00-v3-2-2-dts-ipq4019-Move-xo-and-timer-nodes-to-SoC-dtsi.patch b/target/linux/ipq806x/patches-4.9/864-00-v3-2-2-dts-ipq4019-Move-xo-and-timer-nodes-to-SoC-dtsi.patch deleted file mode 100644 index 3e2c39611..000000000 --- a/target/linux/ipq806x/patches-4.9/864-00-v3-2-2-dts-ipq4019-Move-xo-and-timer-nodes-to-SoC-dtsi.patch +++ /dev/null @@ -1,78 +0,0 @@ -From patchwork Mon Jul 3 07:47:13 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,2/2] dts: ipq4019: Move xo and timer nodes to SoC dtsi -From: Varadarajan Narayanan -X-Patchwork-Id: 9822107 -Message-Id: <1499068033-24000-3-git-send-email-varada@codeaurora.org> -To: andy.gross@linaro.org, david.brown@linaro.org, robh+dt@kernel.org, - mark.rutland@arm.com, linux@armlinux.org.uk, - linux-arm-msm@vger.kernel.org, - linux-soc@vger.kernel.org, devicetree@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org -Cc: Varadarajan Narayanan -Date: Mon, 3 Jul 2017 13:17:13 +0530 - -The node for xo and timer belong to the SoC DTS file. -Else, new board DT files may not inherit these nodes. - -Signed-off-by: Varadarajan Narayanan ---- - arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 19 ------------------- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 15 +++++++++++++++ - 2 files changed, 15 insertions(+), 19 deletions(-) - ---- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -20,26 +20,7 @@ - model = "Qualcomm Technologies, Inc. IPQ4019/AP-DK01.1"; - compatible = "qcom,ipq4019"; - -- clocks { -- xo: xo { -- compatible = "fixed-clock"; -- clock-frequency = <48000000>; -- #clock-cells = <0>; -- }; -- }; -- - soc { -- -- -- timer { -- compatible = "arm,armv7-timer"; -- interrupts = <1 2 0xf08>, -- <1 3 0xf08>, -- <1 4 0xf08>, -- <1 1 0xf08>; -- clock-frequency = <48000000>; -- }; -- - pinctrl@1000000 { - serial_pins: serial_pinmux { - mux { ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -126,6 +126,21 @@ - clock-frequency = <32768>; - #clock-cells = <0>; - }; -+ -+ xo: xo { -+ compatible = "fixed-clock"; -+ clock-frequency = <48000000>; -+ #clock-cells = <0>; -+ }; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ interrupts = <1 2 0xf08>, -+ <1 3 0xf08>, -+ <1 4 0xf08>, -+ <1 1 0xf08>; -+ clock-frequency = <48000000>; - }; - - soc { diff --git a/target/linux/ipq806x/patches-4.9/900-mach-qcom-add-msm_pcie-driver.patch b/target/linux/ipq806x/patches-4.9/900-mach-qcom-add-msm_pcie-driver.patch deleted file mode 100644 index be3a1af15..000000000 --- a/target/linux/ipq806x/patches-4.9/900-mach-qcom-add-msm_pcie-driver.patch +++ /dev/null @@ -1,4883 +0,0 @@ -From a0473413c8a10ea1884f2532b89e029b70e389d0 Mon Sep 17 00:00:00 2001 -From: Chen Minqiang -Date: Sat, 6 Jan 2018 13:59:06 +0800 -Subject: [PATCH 3/3] mach-qcom: add msm_pcie driver - ---- - Documentation/devicetree/bindings/pci/msm_pcie.txt | 164 ++ - arch/arm/mach-qcom/Kconfig | 8 + - arch/arm/mach-qcom/Makefile | 3 + - arch/arm/mach-qcom/include/mach/gpiomux.h | 216 ++ - arch/arm/mach-qcom/include/mach/msm_pcie.h | 134 ++ - arch/arm/mach-qcom/pcie.c | 2389 ++++++++++++++++++++ - arch/arm/mach-qcom/pcie.h | 329 +++ - arch/arm/mach-qcom/pcie_irq.c | 598 +++++ - arch/arm/mach-qcom/pcie_phy.c | 403 ++++ - arch/arm/mach-qcom/pcie_phy.h | 545 +++++ - 10 files changed, 4789 insertions(+) - create mode 100644 Documentation/devicetree/bindings/pci/msm_pcie.txt - create mode 100644 arch/arm/mach-qcom/include/mach/gpiomux.h - create mode 100644 arch/arm/mach-qcom/include/mach/msm_pcie.h - create mode 100644 arch/arm/mach-qcom/pcie.c - create mode 100644 arch/arm/mach-qcom/pcie.h - create mode 100644 arch/arm/mach-qcom/pcie_irq.c - create mode 100644 arch/arm/mach-qcom/pcie_phy.c - create mode 100644 arch/arm/mach-qcom/pcie_phy.h - -diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt -new file mode 100644 -index 0000000..24a2be7 ---- /dev/null -+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt -@@ -0,0 +1,164 @@ -+MSM PCIe -+ -+MSM PCI express root complex -+ -+Required properties: -+ - compatible: should be "qcom,pci-msm" -+ - cell-index: defines root complex ID. -+ - #address-cells: Should provide a value of 0. -+ - reg: should contain PCIe register maps. -+ - reg-names: indicates various resources passed to driver by name. -+ Should be "parf", "phy", "dm_core", "elbi", "conf", "io", "bars". -+ These correspond to different modules within the PCIe core. -+ - interrupts: Should be in the format <0 1 2> and it is an index to the -+ interrupt-map that contains PCIe related interrupts. -+ - #interrupt-cells: Should provide a value of 1. -+ - #interrupt-map-mask: should provide a value of 0xffffffff. -+ - interrupt-map: Must create mapping for the number of interrupts -+ that are defined in above interrupts property. -+ For PCIe device node, it should define 12 mappings for -+ the corresponding PCIe interrupts supporting the specification. -+ - interrupt-names: indicates interrupts passed to driver by name. -+ Should be "int_msi", "int_a", "int_b", "int_c", "int_d", -+ "int_pls_pme", "int_pme_legacy", "int_pls_err", -+ "int_aer_legacy", "int_pls_link_up", -+ "int_pls_link_down", "int_bridge_flush_n" -+ These correspond to the standard PCIe specification to support -+ MSIs, virtual IRQ's (INT#), link state notifications. -+ - perst-gpio: PERST GPIO specified by PCIe spec. -+ - wake-gpio: WAKE GPIO specified by PCIe spec. -+ - -supply: phandle to the regulator device tree node. -+ Refer to the schematics for the corresponding voltage regulators. -+ vreg-1.8-supply: phandle to the analog supply for the PCIe controller. -+ vreg-3.3-supply: phandle to the analog supply for the PCIe controller. -+ vreg-0.9-supply: phandle to the analog supply for the PCIe controller. -+ -+Optional Properties: -+ - qcom,-voltage-level: specifies voltage levels for supply. -+ Should be specified in pairs (max, min, optimal), units uV. -+ - clkreq-gpio: CLKREQ GPIO specified by PCIe spec. -+ - pinctrl-names: The state name of the pin configuration. Only -+ support: "default" -+ - pinctrl-0: For details of pinctrl properties, please refer to: -+ "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt" -+ - clocks: list of clock phandles -+ - clock-names: list of names of clock inputs. -+ Should be "pcie_0_pipe_clk", "pcie_0_ref_clk_src", -+ "pcie_0_aux_clk", "pcie_0_cfg_ahb_clk", -+ "pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk", -+ "pcie_0_ldo"; -+ - max-clock-frequency-hz: list of the maximum operating frequencies stored -+ in the same order of clock names; -+ - qcom,l0s-supported: L0s is supported. -+ - qcom,l1-supported: L1 is supported. -+ - qcom,l1ss-supported: L1 sub-states (L1ss) is supported. -+ - qcom,aux-clk-sync: The AUX clock is synchronous to the Core clock to -+ support L1ss. -+ - qcom,n-fts: The number of fast training sequences sent when the link state -+ is changed from L0s to L0. -+ - qcom,ep-wakeirq: The endpoint will issue wake signal when it is up, and the -+ root complex has the capability to enumerate the endpoint for this case. -+ - qcom,msi-gicm-addr: MSI address for GICv2m. -+ - qcom,msi-gicm-base: MSI IRQ base for GICv2m. -+ - qcom,ext-ref-clk: The reference clock is external. -+ - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become -+ stable after power on, before de-assert the PERST to the endpoint. -+ - qcom,tlp-rd-size: The max TLP read size (Calculation: 128 times 2 to the -+ tlp-rd-size power). -+ - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for -+ below optional properties: -+ - qcom,msm-bus,name -+ - qcom,msm-bus,num-cases -+ - qcom,msm-bus,num-paths -+ - qcom,msm-bus,vectors-KBps -+ - qcom,scm-dev-id: If present then device id value is passed to secure channel -+ manager(scm) driver. scm driver uses this device id to restore PCIe -+ controller related security configuration after coming out of the controller -+ power collapse. -+ -+Example: -+ -+ pcie0: qcom,pcie@fc520000 { -+ compatible = "qcom,msm_pcie"; -+ cell-index = <0>; -+ #address-cells = <0>; -+ reg = <0xfc520000 0x2000>, -+ <0xfc526000 0x1000>, -+ <0xff000000 0x1000>, -+ <0xff001000 0x1000>, -+ <0xff100000 0x1000>, -+ <0xff200000 0x100000>, -+ <0xff300000 0xd00000>; -+ reg-names = "parf", "dm_core", "elbi", -+ "conf", "io", "bars"; -+ interrupt-parent = <&pcie0>; -+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12>; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0xffffffff>; -+ interrupt-map = <0 &intc 0 243 0 -+ 1 &intc 0 244 0 -+ 2 &intc 0 245 0 -+ 3 &intc 0 247 0 -+ 4 &intc 0 248 0 -+ 5 &intc 0 249 0 -+ 6 &intc 0 250 0 -+ 7 &intc 0 251 0 -+ 8 &intc 0 252 0 -+ 9 &intc 0 253 0 -+ 10 &intc 0 254 0 -+ 11 &intc 0 255 0>; -+ interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d", -+ "int_pls_pme", "int_pme_legacy", "int_pls_err", -+ "int_aer_legacy", "int_pls_link_up", -+ "int_pls_link_down", "int_bridge_flush_n"; -+ perst-gpio = <&msmgpio 70 0>; -+ wake-gpio = <&msmgpio 69 0>; -+ clkreq-gpio = <&msmgpio 68 0>; -+ -+ gdsc-vdd-supply = <&gdsc_pcie_0>; -+ vreg-1.8-supply = <&pma8084_l12>; -+ vreg-0.9-supply = <&pma8084_l4>; -+ vreg-3.3-supply = <&wlan_vreg>; -+ -+ qcom,vreg-1.8-voltage-level = <1800000 1800000 1000>; -+ qcom,vreg-0.9-voltage-level = <950000 950000 24000>; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pcie0_clkreq_default &pcie0_perst_default &pcie0_wake_default>; -+ -+ clocks = <&clock_gcc clk_gcc_pcie_0_pipe_clk>, -+ <&clock_rpm clk_ln_bb_clk>, -+ <&clock_gcc clk_gcc_pcie_0_aux_clk>, -+ <&clock_gcc clk_gcc_pcie_0_cfg_ahb_clk>, -+ <&clock_gcc clk_gcc_pcie_0_mstr_axi_clk>, -+ <&clock_gcc clk_gcc_pcie_0_slv_axi_clk>, -+ <&clock_gcc clk_pcie_0_phy_ldo>, -+ <&clock_gcc clk_gcc_pcie_phy_0_reset>; -+ -+ clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src", -+ "pcie_0_aux_clk", "pcie_0_cfg_ahb_clk", -+ "pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk", -+ "pcie_0_ldo"; -+ max-clock-frequency-hz = <125000000>, <0>, <1000000>, -+ <0>, <0>, <0>, <0>; -+ qcom,l0s-supported; -+ qcom,l1-supported; -+ qcom,l1ss-supported; -+ qcom,aux-clk-sync; -+ qcom,n-fts = <0x50>; -+ qcom,ep-wakeirq; -+ qcom,msi-gicm-addr = <0xf9040040>; -+ qcom,msi-gicm-base = <0x160>; -+ qcom,ext-ref-clk; -+ qcom,tlp-rd-size = <0x5>; -+ qcom,ep-latency = <100>; -+ -+ qcom,msm-bus,name = "pcie0"; -+ qcom,msm-bus,num-cases = <2>; -+ qcom,msm-bus,num-paths = <1>; -+ qcom,msm-bus,vectors-KBps = -+ <45 512 0 0>, -+ <45 512 500 800>; -+ -+ qcom,scm-dev-id = <11>; -+ }; -diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig -index b438cd0..38b4009 100644 ---- a/arch/arm/mach-qcom/Kconfig -+++ b/arch/arm/mach-qcom/Kconfig -@@ -10,6 +10,14 @@ menuconfig ARCH_QCOM - help - Support for Qualcomm's devicetree based systems. - -+config MSM_PCIE -+ bool "MSM PCIe Controller driver" -+ depends on PCI && PCI_MSI -+ select PCI_DOMAINS -+ help -+ Enables the PCIe functionality by configures PCIe core on -+ MSM chipset and by enabling the ARM PCI framework extension. -+ - if ARCH_QCOM - - config ARCH_MSM8X60 -diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile -index 12878e9..542691b 100644 ---- a/arch/arm/mach-qcom/Makefile -+++ b/arch/arm/mach-qcom/Makefile -@@ -1 +1,4 @@ -+EXTRA_CFLAGS += -I$(srctree)/arch/arm/mach-qcom/include -+EXTRA_CFLAGS += -I$(srctree)/drivers/bus/msm_bus - obj-$(CONFIG_SMP) += platsmp.o -+obj-$(CONFIG_MSM_PCIE) += pcie.o pcie_irq.o pcie_phy.o -diff --git a/arch/arm/mach-qcom/include/mach/gpiomux.h b/arch/arm/mach-qcom/include/mach/gpiomux.h -new file mode 100644 -index 0000000..2278677 ---- /dev/null -+++ b/arch/arm/mach-qcom/include/mach/gpiomux.h -@@ -0,0 +1,216 @@ -+/* Copyright (c) 2010-2011,2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_H -+#define __ARCH_ARM_MACH_MSM_GPIOMUX_H -+ -+#include -+#include -+ -+enum msm_gpiomux_setting { -+ GPIOMUX_ACTIVE = 0, -+ GPIOMUX_SUSPENDED, -+ GPIOMUX_NSETTINGS -+}; -+ -+enum gpiomux_drv { -+ GPIOMUX_DRV_2MA = 0, -+ GPIOMUX_DRV_4MA, -+ GPIOMUX_DRV_6MA, -+ GPIOMUX_DRV_8MA, -+ GPIOMUX_DRV_10MA, -+ GPIOMUX_DRV_12MA, -+ GPIOMUX_DRV_14MA, -+ GPIOMUX_DRV_16MA, -+}; -+ -+enum gpiomux_func { -+ GPIOMUX_FUNC_GPIO = 0, -+ GPIOMUX_FUNC_1, -+ GPIOMUX_FUNC_2, -+ GPIOMUX_FUNC_3, -+ GPIOMUX_FUNC_4, -+ GPIOMUX_FUNC_5, -+ GPIOMUX_FUNC_6, -+ GPIOMUX_FUNC_7, -+ GPIOMUX_FUNC_8, -+ GPIOMUX_FUNC_9, -+ GPIOMUX_FUNC_A, -+ GPIOMUX_FUNC_B, -+ GPIOMUX_FUNC_C, -+ GPIOMUX_FUNC_D, -+ GPIOMUX_FUNC_E, -+ GPIOMUX_FUNC_F, -+}; -+ -+enum gpiomux_pull { -+ GPIOMUX_PULL_NONE = 0, -+ GPIOMUX_PULL_DOWN, -+ GPIOMUX_PULL_KEEPER, -+ GPIOMUX_PULL_UP, -+}; -+ -+/* Direction settings are only meaningful when GPIOMUX_FUNC_GPIO is selected. -+ * This element is ignored for all other FUNC selections, as the output- -+ * enable pin is not under software control in those cases. See the SWI -+ * for your target for more details. -+ */ -+enum gpiomux_dir { -+ GPIOMUX_IN = 0, -+ GPIOMUX_OUT_HIGH, -+ GPIOMUX_OUT_LOW, -+}; -+ -+struct gpiomux_setting { -+ enum gpiomux_func func; -+ enum gpiomux_drv drv; -+ enum gpiomux_pull pull; -+ enum gpiomux_dir dir; -+}; -+ -+/** -+ * struct msm_gpiomux_config: gpiomux settings for one gpio line. -+ * -+ * A complete gpiomux config is the combination of a drive-strength, -+ * function, pull, and (sometimes) direction. For functions other than GPIO, -+ * the input/output setting is hard-wired according to the function. -+ * -+ * @gpio: The index number of the gpio being described. -+ * @settings: The settings to be installed, specifically: -+ * GPIOMUX_ACTIVE: The setting to be installed when the -+ * line is active, or its reference count is > 0. -+ * GPIOMUX_SUSPENDED: The setting to be installed when -+ * the line is suspended, or its reference count is 0. -+ */ -+struct msm_gpiomux_config { -+ unsigned gpio; -+ struct gpiomux_setting *settings[GPIOMUX_NSETTINGS]; -+}; -+ -+/** -+ * struct msm_gpiomux_configs: a collection of gpiomux configs. -+ * -+ * It is so common to manage blocks of gpiomux configs that the data structure -+ * for doing so has been standardized here as a convenience. -+ * -+ * @cfg: A pointer to the first config in an array of configs. -+ * @ncfg: The number of configs in the array. -+ */ -+struct msm_gpiomux_configs { -+ struct msm_gpiomux_config *cfg; -+ size_t ncfg; -+}; -+ -+/* Provide an enum and an API to write to misc TLMM registers */ -+enum msm_tlmm_misc_reg { -+ TLMM_ETM_MODE_REG = 0x2014, -+ TLMM_SDC2_HDRV_PULL_CTL = 0x2048, -+ TLMM_SPARE_REG = 0x2024, -+ TLMM_CDC_HDRV_CTL = 0x2054, -+ TLMM_CDC_HDRV_PULL_CTL = 0x2058, -+}; -+ -+#ifdef CONFIG_MSM_GPIOMUX -+ -+/* Before using gpiomux, initialize the subsystem by telling it how many -+ * gpios are going to be managed. Calling any other gpiomux functions before -+ * msm_gpiomux_init is unsupported. -+ */ -+int msm_gpiomux_init(size_t ngpio); -+ -+/* DT Variant of msm_gpiomux_init. This will look up the number of gpios from -+ * device tree rather than relying on NR_GPIO_IRQS -+ */ -+int msm_gpiomux_init_dt(void); -+ -+/* Install a block of gpiomux configurations in gpiomux. This is functionally -+ * identical to calling msm_gpiomux_write many times. -+ */ -+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs); -+ -+/* Install a block of gpiomux configurations in gpiomux. Do not however write -+ * to hardware. Just store the settings to be retrieved at a later time -+ */ -+void msm_gpiomux_install_nowrite(struct msm_gpiomux_config *configs, -+ unsigned nconfigs); -+ -+/* Increment a gpio's reference count, possibly activating the line. */ -+int __must_check msm_gpiomux_get(unsigned gpio); -+ -+/* Decrement a gpio's reference count, possibly suspending the line. */ -+int msm_gpiomux_put(unsigned gpio); -+ -+/* Install a new setting in a gpio. To erase a slot, use NULL. -+ * The old setting that was overwritten can be passed back to the caller -+ * old_setting can be NULL if the caller is not interested in the previous -+ * setting -+ * If a previous setting was not available to return (NULL configuration) -+ * - the function returns 1 -+ * else function returns 0 -+ */ -+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which, -+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting); -+ -+/* Architecture-internal function for use by the framework only. -+ * This function can assume the following: -+ * - the gpio value has passed a bounds-check -+ * - the gpiomux spinlock has been obtained -+ * -+ * This function is not for public consumption. External users -+ * should use msm_gpiomux_write. -+ */ -+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val); -+ -+/* Functions that provide an API for drivers to read from and write to -+ * miscellaneous TLMM registers. -+ */ -+int msm_tlmm_misc_reg_read(enum msm_tlmm_misc_reg misc_reg); -+ -+void msm_tlmm_misc_reg_write(enum msm_tlmm_misc_reg misc_reg, int val); -+ -+#else -+static inline int msm_gpiomux_init(size_t ngpio) -+{ -+ return -ENOSYS; -+} -+ -+static inline void -+msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs) {} -+ -+static inline int __must_check msm_gpiomux_get(unsigned gpio) -+{ -+ return -ENOSYS; -+} -+ -+static inline int msm_gpiomux_put(unsigned gpio) -+{ -+ return -ENOSYS; -+} -+ -+static inline int msm_gpiomux_write(unsigned gpio, -+ enum msm_gpiomux_setting which, struct gpiomux_setting *setting, -+ struct gpiomux_setting *old_setting) -+{ -+ return -ENOSYS; -+} -+ -+static inline int msm_tlmm_misc_reg_read(enum msm_tlmm_misc_reg misc_reg) -+{ -+ return -ENOSYS; -+} -+ -+static inline void msm_tlmm_misc_reg_write(enum msm_tlmm_misc_reg misc_reg, -+ int val) -+{ -+} -+ -+#endif -+#endif -diff --git a/arch/arm/mach-qcom/include/mach/msm_pcie.h b/arch/arm/mach-qcom/include/mach/msm_pcie.h -new file mode 100644 -index 0000000..c2fbdea ---- /dev/null -+++ b/arch/arm/mach-qcom/include/mach/msm_pcie.h -@@ -0,0 +1,134 @@ -+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __ASM_ARCH_MSM_PCIE_H -+#define __ASM_ARCH_MSM_PCIE_H -+ -+#include -+#include -+ -+enum msm_pcie_config { -+ MSM_PCIE_CONFIG_INVALID = 0, -+ MSM_PCIE_CONFIG_NO_CFG_RESTORE = 0x1, -+ MSM_PCIE_CONFIG_LINKDOWN = 0x2, -+ MSM_PCIE_CONFIG_NO_RECOVERY = 0x4, -+}; -+ -+enum msm_pcie_pm_opt { -+ MSM_PCIE_SUSPEND, -+ MSM_PCIE_RESUME, -+ MSM_PCIE_REQ_EXIT_L1, -+}; -+ -+enum msm_pcie_event { -+ MSM_PCIE_EVENT_INVALID = 0, -+ MSM_PCIE_EVENT_LINKDOWN = 0x1, -+ MSM_PCIE_EVENT_LINKUP = 0x2, -+ MSM_PCIE_EVENT_WAKEUP = 0x4, -+ MSM_PCIE_EVENT_WAKE_RECOVERY = 0x8, -+ MSM_PCIE_EVENT_NO_ACCESS = 0x10, -+}; -+ -+enum msm_pcie_trigger { -+ MSM_PCIE_TRIGGER_CALLBACK, -+ MSM_PCIE_TRIGGER_COMPLETION, -+}; -+ -+struct msm_pcie_notify { -+ enum msm_pcie_event event; -+ void *user; -+ void *data; -+ u32 options; -+}; -+ -+struct msm_pcie_register_event { -+ u32 events; -+ void *user; -+ enum msm_pcie_trigger mode; -+ void (*callback)(struct msm_pcie_notify *notify); -+ struct msm_pcie_notify notify; -+ struct completion *completion; -+ u32 options; -+}; -+ -+/** -+ * msm_pcie_pm_control - control the power state of a PCIe link. -+ * @pm_opt: power management operation -+ * @busnr: bus number of PCIe endpoint -+ * @user: handle of the caller -+ * @data: private data from the caller -+ * @options: options for pm control -+ * -+ * This function gives PCIe endpoint device drivers the control to change -+ * the power state of a PCIe link for their device. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, -+ void *data, u32 options); -+ -+/** -+ * msm_pcie_register_event - register an event with PCIe bus driver. -+ * @reg: event structure -+ * -+ * This function gives PCIe endpoint device drivers an option to register -+ * events with PCIe bus driver. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_register_event(struct msm_pcie_register_event *reg); -+ -+/** -+ * msm_pcie_deregister_event - deregister an event with PCIe bus driver. -+ * @reg: event structure -+ * -+ * This function gives PCIe endpoint device drivers an option to deregister -+ * events with PCIe bus driver. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_deregister_event(struct msm_pcie_register_event *reg); -+ -+/** -+ * msm_pcie_recover_config - recover config space. -+ * @dev: pci device structure -+ * -+ * This function recovers the config space of both RC and Endpoint. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_recover_config(struct pci_dev *dev); -+ -+/** -+ * msm_pcie_shadow_control - control the shadowing of PCIe config space. -+ * @dev: pci device structure -+ * @enable: shadowing should be enabled or disabled -+ * -+ * This function gives PCIe endpoint device drivers the control to enable -+ * or disable the shadowing of PCIe config space. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_shadow_control(struct pci_dev *dev, bool enable); -+ -+/* -+ * msm_pcie_access_control - access control to PCIe address range. -+ * @dev: pci device structure -+ * @enable: enable or disable the access -+ * -+ * This function gives PCIe endpoint device drivers the control to enable -+ * or disable the access to PCIe address range. -+ * -+ * Return: 0 on success, negative value on error -+ */ -+int msm_pcie_access_control(struct pci_dev *dev, bool enable); -+#endif -diff --git a/arch/arm/mach-qcom/pcie.c b/arch/arm/mach-qcom/pcie.c -new file mode 100644 -index 0000000..b471479 ---- /dev/null -+++ b/arch/arm/mach-qcom/pcie.c -@@ -0,0 +1,2389 @@ -+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+/* -+ * MSM PCIe controller driver. -+ */ -+ -+#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 "msm-bus.h" -+#include "msm-bus-board.h" -+ -+#include "pcie.h" -+ -+/* Root Complex Port vendor/device IDs */ -+#define PCIE_VENDOR_ID_RCP 0x17cb -+#ifdef CONFIG_ARCH_MDM9630 -+#define PCIE_DEVICE_ID_RCP 0x300 -+#else -+#define PCIE_DEVICE_ID_RCP 0x0101 -+#endif -+ -+#define PCIE20_PARF_SYS_CTRL 0x00 -+#define PCIE20_PARF_PM_CTRL 0x20 -+#define PCIE20_PARF_PM_STTS 0x24 -+#define PCIE20_PARF_PCS_DEEMPH 0x34 -+#define PCIE20_PARF_PCS_SWING 0x38 -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_CONFIG_BITS 0x50 -+#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -+#define PCIE20_PARF_Q2A_FLUSH 0x1AC -+#define PCIE20_PARF_LTSSM 0x1B0 -+ -+#define PCIE20_ELBI_VERSION 0x00 -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_STTS 0x08 -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+#define PCIE20_CAP_LINK_CAPABILITIES (PCIE20_CAP + 0xC) -+#define PCIE20_CAP_LINK_1 (PCIE20_CAP + 0x14) -+ -+#define PCIE20_COMMAND_STATUS 0x04 -+#define PCIE20_BUSNUMBERS 0x18 -+#define PCIE20_MEMORY_BASE_LIMIT 0x20 -+#define PCIE20_L1SUB_CONTROL1 0x158 -+#define PCIE20_EP_L1SUB_CTL1_OFFSET 0x30 -+#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98 -+ -+#define PCIE20_ACK_F_ASPM_CTRL_REG 0x70C -+#define PCIE20_ACK_N_FTS 0xff00 -+#define PCIE20_GEN2_CTRL_REG 0x80C -+#define PCIE20_MISC_CONTROL_1_REG 0x8BC -+ -+#define PCIE20_PLR_IATU_VIEWPORT 0x900 -+#define PCIE20_PLR_IATU_CTRL1 0x904 -+#define PCIE20_PLR_IATU_CTRL2 0x908 -+#define PCIE20_PLR_IATU_LBAR 0x90C -+#define PCIE20_PLR_IATU_UBAR 0x910 -+#define PCIE20_PLR_IATU_LAR 0x914 -+#define PCIE20_PLR_IATU_LTAR 0x918 -+#define PCIE20_PLR_IATU_UTAR 0x91c -+ -+#define RD 0 -+#define WR 1 -+ -+/* Timing Delays */ -+#define PERST_PROPAGATION_DELAY_US_MIN 1000 -+#define PERST_PROPAGATION_DELAY_US_MAX 1005 -+#define REFCLK_STABILIZATION_DELAY_US_MIN 1000 -+#define REFCLK_STABILIZATION_DELAY_US_MAX 1500 -+#define LINK_RETRY_TIMEOUT_US_MIN 20000 -+#define LINK_RETRY_TIMEOUT_US_MAX 25000 -+#define LINK_UP_TIMEOUT_US_MIN 5000 -+#define LINK_UP_TIMEOUT_US_MAX 5100 -+#define LINK_UP_CHECK_MAX_COUNT 20 -+#define PHY_STABILIZATION_DELAY_US_MIN 995 -+#define PHY_STABILIZATION_DELAY_US_MAX 1005 -+#define REQ_EXIT_L1_DELAY_US 1 -+ -+#define PHY_READY_TIMEOUT_COUNT 10 -+#define XMLH_LINK_UP 0x400 -+#define MAX_LINK_RETRIES 5 -+#define MAX_BUS_NUM 3 -+#define MAX_PROP_SIZE 32 -+#define MAX_RC_NAME_LEN 15 -+ -+#define CMD_BME_VAL 0x4 -+#define DBI_RO_WR_EN 1 -+#define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 -+#define LTSSM_EN (1 << 8) -+#define PCIE_CAP_ACTIVE_STATE_LINK_PM_SUPPORT_MASK 0xc00 -+#define PCIE_CAP_LINK1_VAL 0x2fd7f -+ -+/* Config Space Offsets */ -+#define BDF_OFFSET(bus, device, function) \ -+ ((bus << 24) | (device << 15) | (function << 8)) -+ -+/* debug mask sys interface */ -+static int msm_pcie_debug_mask; -+module_param_named(debug_mask, msm_pcie_debug_mask, -+ int, S_IRUGO | S_IWUSR | S_IWGRP); -+static atomic_t rc_removed; -+ -+/** -+ * PCIe driver state -+ */ -+struct pcie_drv_sta { -+ u32 rc_num; -+ u32 rc_expected; -+ u32 current_rc; -+ bool vreg_on; -+ struct mutex drv_lock; -+} pcie_drv; -+ -+/* msm pcie device data */ -+static struct msm_pcie_dev_t msm_pcie_dev[MAX_RC_NUM]; -+ -+/* regulators */ -+static struct msm_pcie_vreg_info_t msm_pcie_vreg_info[MSM_PCIE_MAX_VREG] = { -+ {NULL, "vreg-3.3", 0, 0, 0, false}, -+ {NULL, "vreg-1.8", 1800000, 1800000, 1000, true}, -+ {NULL, "vreg-0.9", 1000000, 1000000, 24000, true} -+}; -+ -+/* GPIOs */ -+static struct msm_pcie_gpio_info_t msm_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = { -+ {"perst-gpio", 0, 1, 0, 0}, -+ {"wake-gpio", 0, 0, 0, 0}, -+ {"clkreq-gpio", 0, 0, 0, 0} -+}; -+ -+/* clocks */ -+static struct msm_pcie_clk_info_t -+ msm_pcie_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_CLK] = { -+ { -+ {NULL, "pcie_0_cfg_ahb_clk", 0, true}, -+ {NULL, "pcie_0_mstr_axi_clk", 0, true}, -+ {NULL, "pcie_0_slv_axi_clk", 0, true}, -+ }, -+}; -+ -+/* RESETs */ -+static struct msm_pcie_rst_info_t msm_pcie_rst_info[MSM_PCIE_MAX_RESET] = { -+ {NULL, "pcie_rst_axi_m_ares"}, -+ {NULL, "pcie_rst_axi_s_ares"}, -+ {NULL, "pcie_rst_pipe_ares"}, -+ {NULL, "pcie_rst_axi_m_vmidmt_ares"}, -+ {NULL, "pcie_rst_axi_s_xpu_ares"}, -+ {NULL, "pcie_rst_parf_xpu_ares"}, -+ {NULL, "pcie_rst_phy_ares"}, -+ {NULL, "pcie_rst_axi_m_sticky_ares"}, -+ {NULL, "pcie_rst_pipe_sticky_ares"}, -+ {NULL, "pcie_rst_pwr_ares"}, -+ {NULL, "pcie_rst_ahb_res"}, -+ {NULL, "pcie_rst_phy_ahb_ares"} -+}; -+ -+/* resources */ -+static const struct msm_pcie_res_info_t msm_pcie_res_info[MSM_PCIE_MAX_RES] = { -+ {"parf", 0, 0}, -+ {"phy", 0, 0}, -+ {"dm_core", 0, 0}, -+ {"elbi", 0, 0}, -+ {"conf", 0, 0}, -+ {"io", 0, 0}, -+ {"bars", 0, 0} -+}; -+ -+/* irqs */ -+static const struct msm_pcie_irq_info_t msm_pcie_irq_info[MSM_PCIE_MAX_IRQ] = { -+ {"int_msi", 0}, -+ {"int_a", 0}, -+ {"int_b", 0}, -+ {"int_c", 0}, -+ {"int_d", 0}, -+ {"int_pls_pme", 0}, -+ {"int_pme_legacy", 0}, -+ {"int_pls_err", 0}, -+ {"int_aer_legacy", 0}, -+ {"int_pls_link_up", 0}, -+ {"int_pls_link_down", 0}, -+ {"int_bridge_flush_n", 0}, -+ {"int_wake", 0} -+}; -+ -+int msm_pcie_get_debug_mask(void) -+{ -+ return msm_pcie_debug_mask; -+} -+ -+bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev, -+ bool check_sw_stts, -+ bool check_ep) -+{ -+ u32 val; -+ -+ if (check_sw_stts && (dev->link_status != MSM_PCIE_LINK_ENABLED)) { -+ PCIE_DBG(dev, "PCIe: The link of RC %d is not enabled.\n", -+ dev->rc_idx); -+ return false; -+ } -+ -+ if (!(readl_relaxed(dev->dm_core + 0x80) & BIT(29))) { -+ PCIE_DBG(dev, "PCIe: The link of RC %d is not up.\n", -+ dev->rc_idx); -+ return false; -+ } -+ -+ val = readl_relaxed(dev->dm_core); -+ PCIE_DBG(dev, "PCIe: device ID and vender ID of RC %d are 0x%x.\n", -+ dev->rc_idx, val); -+ if (val == PCIE_LINK_DOWN) { -+ PCIE_ERR(dev, -+ "PCIe: The link of RC %d is not really up; device ID and vender ID of RC %d are 0x%x.\n", -+ dev->rc_idx, dev->rc_idx, val); -+ return false; -+ } -+ -+ if (check_ep) { -+ val = readl_relaxed(dev->conf); -+ PCIE_DBG(dev, -+ "PCIe: device ID and vender ID of EP of RC %d are 0x%x.\n", -+ dev->rc_idx, val); -+ if (val == PCIE_LINK_DOWN) { -+ PCIE_ERR(dev, -+ "PCIe: The link of RC %d is not really up; device ID and vender ID of EP of RC %d are 0x%x.\n", -+ dev->rc_idx, dev->rc_idx, val); -+ return false; -+ } -+ } -+ -+ return true; -+} -+ -+void msm_pcie_cfg_recover(struct msm_pcie_dev_t *dev, bool rc) -+{ -+ int i; -+ u32 val = 0; -+ u32 *shadow; -+ void *cfg; -+ -+ if (rc) { -+ shadow = dev->rc_shadow; -+ cfg = dev->dm_core; -+ } else { -+ shadow = dev->ep_shadow; -+ cfg = dev->conf; -+ } -+ -+ for (i = PCIE_CONF_SPACE_DW - 1; i >= 0; i--) { -+ val = shadow[i]; -+ if (val != PCIE_CLEAR) { -+ PCIE_DBG3(dev, "PCIe: before recovery:cfg 0x%x:0x%x\n", -+ i * 4, readl_relaxed(cfg + i * 4)); -+ PCIE_DBG3(dev, "PCIe: shadow_dw[%d]:cfg 0x%x:0x%x\n", -+ i, i * 4, val); -+ writel_relaxed(val, cfg + i * 4); -+ wmb(); -+ PCIE_DBG3(dev, "PCIe: after recovery:cfg 0x%x:0x%x\n\n", -+ i * 4, readl_relaxed(cfg + i * 4)); -+ } -+ } -+ -+ readl_relaxed(dev->elbi); -+} -+ -+static void msm_pcie_write_mask(void __iomem *addr, -+ uint32_t clear_mask, uint32_t set_mask) -+{ -+ uint32_t val; -+ -+ val = (readl_relaxed(addr) & ~clear_mask) | set_mask; -+ writel_relaxed(val, addr); -+ wmb(); /* ensure data is written to hardware register */ -+} -+ -+static int msm_pcie_is_link_up(struct msm_pcie_dev_t *dev) -+{ -+ return readl_relaxed(dev->dm_core + -+ PCIE20_CAP_LINKCTRLSTATUS) & BIT(29); -+} -+ -+static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper, -+ int where, int size, u32 *val) -+{ -+ uint32_t word_offset, byte_offset, mask; -+ uint32_t rd_val, wr_val; -+ struct msm_pcie_dev_t *dev; -+ void __iomem *config_base; -+ bool rc = false; -+ u32 rc_idx; -+ int rv = 0; -+ -+ dev = ((struct msm_pcie_dev_t *) -+ (((struct pci_sys_data *)bus->sysdata)->private_data)); -+ -+ if (!dev) { -+ pr_err("PCIe: No device found for this bus.\n"); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto out; -+ } -+ -+ /* Do the bus->number based access control since we don't support -+ ECAM mechanism */ -+ -+ switch (bus->number) { -+ case 0: -+ rc = true; -+ case 1: -+ rc_idx = dev->rc_idx; -+ break; -+ default: -+ PCIE_ERR(dev, "PCIe: unsupported bus number:%d\n", bus->number); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto out; -+ } -+ -+ if ((bus->number > MAX_BUS_NUM) || (devfn != 0)) { -+ PCIE_DBG3(dev, "RC%d invalid %s - bus %d devfn %d\n", rc_idx, -+ (oper == RD) ? "rd" : "wr", bus->number, devfn); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto out; -+ } -+ -+ spin_lock_irqsave(&dev->cfg_lock, dev->irqsave_flags); -+ -+ if (!dev->cfg_access) { -+ PCIE_DBG3(dev, -+ "Access denied for RC%d %d:0x%02x + 0x%04x[%d]\n", -+ rc_idx, bus->number, devfn, where, size); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto unlock; -+ } -+ -+ if (dev->link_status != MSM_PCIE_LINK_ENABLED) { -+ PCIE_DBG3(dev, -+ "Access to RC%d %d:0x%02x + 0x%04x[%d] is denied because link is down\n", -+ rc_idx, bus->number, devfn, where, size); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto unlock; -+ } -+ -+ /* check if the link is up for endpoint */ -+ if (!rc && !msm_pcie_is_link_up(dev)) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d %s fail, link down - bus %d devfn %d\n", -+ rc_idx, (oper == RD) ? "rd" : "wr", -+ bus->number, devfn); -+ *val = ~0; -+ rv = PCIBIOS_DEVICE_NOT_FOUND; -+ goto unlock; -+ } -+ -+ word_offset = where & ~0x3; -+ byte_offset = where & 0x3; -+ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset); -+ -+ config_base = rc ? dev->dm_core : dev->conf; -+ rd_val = readl_relaxed(config_base + word_offset); -+ -+ if (oper == RD) { -+ *val = ((rd_val & mask) >> (8 * byte_offset)); -+ PCIE_DBG3(dev, -+ "RC%d %d:0x%02x + 0x%04x[%d] -> 0x%08x; rd 0x%08x\n", -+ rc_idx, bus->number, devfn, where, size, *val, rd_val); -+ } else { -+ wr_val = (rd_val & ~mask) | -+ ((*val << (8 * byte_offset)) & mask); -+ writel_relaxed(wr_val, config_base + word_offset); -+ wmb(); /* ensure config data is written to hardware register */ -+ readl_relaxed(dev->elbi); -+ -+ if (rd_val == PCIE_LINK_DOWN) { -+ PCIE_ERR(dev, -+ "Read of RC%d %d:0x%02x + 0x%04x[%d] is all FFs\n", -+ rc_idx, bus->number, devfn, where, size); -+ } else if (dev->shadow_en) { -+ if (rc) -+ dev->rc_shadow[word_offset / 4] = wr_val; -+ else -+ dev->ep_shadow[word_offset / 4] = wr_val; -+ } -+ -+ PCIE_DBG3(dev, -+ "RC%d %d:0x%02x + 0x%04x[%d] <- 0x%08x; rd 0x%08x val 0x%08x\n", -+ rc_idx, bus->number, devfn, where, size, -+ wr_val, rd_val, *val); -+ } -+ -+unlock: -+ spin_unlock_irqrestore(&dev->cfg_lock, dev->irqsave_flags); -+out: -+ return rv; -+} -+ -+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, -+ int size, u32 *val) -+{ -+ int ret = msm_pcie_oper_conf(bus, devfn, RD, where, size, val); -+ -+ if ((bus->number == 0) && (where == PCI_CLASS_REVISION)) { -+ *val = (*val & 0xff) | (PCI_CLASS_BRIDGE_PCI << 16); -+ pr_debug("change class for RC:0x%x\n", *val); -+ } -+ -+ return ret; -+} -+ -+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, -+ int where, int size, u32 val) -+{ -+ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val); -+} -+ -+static struct pci_ops msm_pcie_ops = { -+ .read = msm_pcie_rd_conf, -+ .write = msm_pcie_wr_conf, -+}; -+ -+static int msm_pcie_gpio_init(struct msm_pcie_dev_t *dev) -+{ -+ int rc = 0, i; -+ struct msm_pcie_gpio_info_t *info; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = 0; i < dev->gpio_n; i++) { -+ info = &dev->gpio[i]; -+ -+ if (!info->num) -+ continue; -+ -+ rc = gpio_request(info->num, info->name); -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: RC%d can't get gpio %s; %d\n", -+ dev->rc_idx, info->name, rc); -+ break; -+ } -+ -+ if (info->out) -+ rc = gpio_direction_output(info->num, info->init); -+ else -+ rc = gpio_direction_input(info->num); -+ if (rc) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d can't set direction for GPIO %s:%d\n", -+ dev->rc_idx, info->name, rc); -+ gpio_free(info->num); -+ break; -+ } -+ } -+ -+ if (rc) -+ while (i--) -+ gpio_free(dev->gpio[i].num); -+ -+ return rc; -+} -+ -+static void msm_pcie_gpio_deinit(struct msm_pcie_dev_t *dev) -+{ -+ int i; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = 0; i < dev->gpio_n; i++) -+ gpio_free(dev->gpio[i].num); -+} -+ -+int msm_pcie_vreg_init(struct msm_pcie_dev_t *dev) -+{ -+ int i, rc = 0; -+ struct regulator *vreg; -+ struct msm_pcie_vreg_info_t *info; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = 0; i < MSM_PCIE_MAX_VREG; i++) { -+ info = &dev->vreg[i]; -+ vreg = info->hdl; -+ -+ if (!vreg) -+ continue; -+ -+ PCIE_DBG2(dev, "RC%d Vreg %s is being enabled\n", -+ dev->rc_idx, info->name); -+ if (info->max_v) { -+ rc = regulator_set_voltage(vreg, -+ info->min_v, info->max_v); -+ if (rc) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d can't set voltage for %s: %d\n", -+ dev->rc_idx, info->name, rc); -+ break; -+ } -+ } -+ -+ if (info->opt_mode) { -+ rc = regulator_set_mode(vreg, info->opt_mode); -+ if (rc < 0) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d can't set mode for %s: %d\n", -+ dev->rc_idx, info->name, rc); -+ break; -+ } -+ } -+ -+ rc = regulator_enable(vreg); -+ if (rc) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d can't enable regulator %s: %d\n", -+ dev->rc_idx, info->name, rc); -+ break; -+ } -+ } -+ -+ if (rc) -+ while (i--) { -+ struct regulator *hdl = dev->vreg[i].hdl; -+ if (hdl) -+ regulator_disable(hdl); -+ } -+ -+ return rc; -+} -+ -+static void msm_pcie_vreg_deinit(struct msm_pcie_dev_t *dev) -+{ -+ int i; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = MSM_PCIE_MAX_VREG - 1; i >= 0; i--) { -+ if (dev->vreg[i].hdl) { -+ PCIE_DBG(dev, "Vreg %s is being disabled\n", -+ dev->vreg[i].name); -+ regulator_disable(dev->vreg[i].hdl); -+ } -+ } -+} -+ -+static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev) -+{ -+ int i, rc = 0; -+ struct msm_pcie_clk_info_t *info; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ rc = regulator_enable(dev->gdsc); -+ -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: fail to enable GDSC for RC%d (%s)\n", -+ dev->rc_idx, dev->pdev->name); -+ return rc; -+ } -+ -+ if (dev->bus_client) { -+ rc = msm_bus_scale_client_update_request(dev->bus_client, 1); -+ if (rc) { -+ PCIE_ERR(dev, -+ "PCIe: fail to set bus bandwidth for RC%d:%d.\n", -+ dev->rc_idx, rc); -+ return rc; -+ } else { -+ PCIE_DBG2(dev, -+ "PCIe: set bus bandwidth for RC%d.\n", -+ dev->rc_idx); -+ } -+ } -+ -+ for (i = 0; i < MSM_PCIE_MAX_CLK; i++) { -+ info = &dev->clk[i]; -+ -+ if (!info->hdl) -+ continue; -+ -+ if (info->freq) { -+ rc = clk_set_rate(info->hdl, info->freq); -+ if (rc) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d can't set rate for clk %s: %d.\n", -+ dev->rc_idx, info->name, rc); -+ break; -+ } else { -+ PCIE_DBG2(dev, -+ "PCIe: RC%d set rate for clk %s.\n", -+ dev->rc_idx, info->name); -+ } -+ } -+ -+ rc = clk_prepare_enable(info->hdl); -+ -+ if (rc) -+ PCIE_ERR(dev, "PCIe: RC%d failed to enable clk %s\n", -+ dev->rc_idx, info->name); -+ else -+ PCIE_DBG2(dev, "enable clk %s for RC%d.\n", -+ info->name, dev->rc_idx); -+ } -+ -+ if (rc) { -+ PCIE_DBG(dev, "RC%d disable clocks for error handling.\n", -+ dev->rc_idx); -+ while (i--) { -+ struct clk *hdl = dev->clk[i].hdl; -+ if (hdl) -+ clk_disable_unprepare(hdl); -+ } -+ -+ regulator_disable(dev->gdsc); -+ } -+ -+ return rc; -+} -+ -+static void msm_pcie_clk_deinit(struct msm_pcie_dev_t *dev) -+{ -+ int i; -+ int rc; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = 0; i < MSM_PCIE_MAX_CLK; i++) -+ if (dev->clk[i].hdl) -+ clk_disable_unprepare(dev->clk[i].hdl); -+ -+ if (dev->bus_client) { -+ rc = msm_bus_scale_client_update_request(dev->bus_client, 0); -+ if (rc) -+ PCIE_ERR(dev, -+ "PCIe: fail to relinquish bus bandwidth for RC%d:%d.\n", -+ dev->rc_idx, rc); -+ else -+ PCIE_DBG(dev, -+ "PCIe: relinquish bus bandwidth for RC%d.\n", -+ dev->rc_idx); -+ } -+ -+ regulator_disable(dev->gdsc); -+} -+ -+static void msm_pcie_controller_reset(struct msm_pcie_dev_t *dev) -+{ -+ /* Assert pcie_pipe_ares */ -+ reset_control_assert(dev->rst[MSM_PCIE_AXI_M_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_AXI_S_ARES].hdl); -+ usleep_range(10000, 12000); /* wait 12ms */ -+ -+ reset_control_assert(dev->rst[MSM_PCIE_PIPE_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_PIPE_STICKY_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_PHY_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_PHY_AHB_ARES].hdl); -+ usleep_range(10000, 12000); /* wait 12ms */ -+ -+ reset_control_assert(dev->rst[MSM_PCIE_AXI_M_STICKY_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_PWR_ARES].hdl); -+ reset_control_assert(dev->rst[MSM_PCIE_AHB_ARES].hdl); -+ usleep_range(10000, 12000); /* wait 12ms */ -+ -+ reset_control_deassert(dev->rst[MSM_PCIE_PHY_AHB_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_PHY_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_PIPE_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_PIPE_STICKY_ARES].hdl); -+ usleep_range(10000, 12000); /* wait 12ms */ -+ -+ reset_control_deassert(dev->rst[MSM_PCIE_AXI_M_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_AXI_M_STICKY_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_AXI_S_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_PWR_ARES].hdl); -+ reset_control_deassert(dev->rst[MSM_PCIE_AHB_ARES].hdl); -+ usleep_range(10000, 12000); /* wait 12ms */ -+ wmb(); /* ensure data is written to hw register */ -+} -+ -+static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev) -+{ -+ struct resource *axi_conf = dev->res[MSM_PCIE_RES_CONF].resource; -+ u32 dev_conf, upper, lower, limit; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ if (IS_ENABLED(CONFIG_ARM_LPAE)) { -+ lower = PCIE_LOWER_ADDR(axi_conf->start); -+ upper = PCIE_UPPER_ADDR(axi_conf->start); -+ limit = PCIE_LOWER_ADDR(axi_conf->end); -+ } else { -+ lower = axi_conf->start; -+ upper = 0; -+ limit = axi_conf->end; -+ } -+ -+ dev_conf = BDF_OFFSET(1, 0, 0); -+ -+ if (dev->shadow_en) { -+ dev->rc_shadow[PCIE20_PLR_IATU_VIEWPORT / 4] = 0; -+ dev->rc_shadow[PCIE20_PLR_IATU_CTRL1 / 4] = 4; -+ dev->rc_shadow[PCIE20_PLR_IATU_LBAR / 4] = lower; -+ dev->rc_shadow[PCIE20_PLR_IATU_UBAR / 4] = upper; -+ dev->rc_shadow[PCIE20_PLR_IATU_LAR / 4] = limit; -+ dev->rc_shadow[PCIE20_PLR_IATU_LTAR / 4] = dev_conf; -+ dev->rc_shadow[PCIE20_PLR_IATU_UTAR / 4] = 0; -+ dev->rc_shadow[PCIE20_PLR_IATU_CTRL2 / 4] = BIT(31); -+ } -+ -+ /* -+ * program and enable address translation region 0 (device config -+ * address space); region type config; -+ * axi config address range to device config address range -+ */ -+ writel_relaxed(0, dev->dm_core + PCIE20_PLR_IATU_VIEWPORT); -+ /* ensure that hardware locks the region before programming it */ -+ wmb(); -+ -+ writel_relaxed(4, dev->dm_core + PCIE20_PLR_IATU_CTRL1); -+ writel_relaxed(lower, dev->dm_core + PCIE20_PLR_IATU_LBAR); -+ writel_relaxed(upper, dev->dm_core + PCIE20_PLR_IATU_UBAR); -+ writel_relaxed(limit, dev->dm_core + PCIE20_PLR_IATU_LAR); -+ writel_relaxed(dev_conf, dev->dm_core + PCIE20_PLR_IATU_LTAR); -+ writel_relaxed(0, dev->dm_core + PCIE20_PLR_IATU_UTAR); -+ writel_relaxed(BIT(31), dev->dm_core + PCIE20_PLR_IATU_CTRL2); -+ /* ensure that hardware registers the configuration */ -+ wmb(); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_LBAR:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_UBAR:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_LAR:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR)); -+ PCIE_DBG2(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2)); -+ -+ /* configure N_FTS */ -+ PCIE_DBG2(dev, "Original PCIE20_ACK_F_ASPM_CTRL_REG:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG)); -+ if (!dev->n_fts) -+ msm_pcie_write_mask(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG, -+ 0, BIT(15)); -+ else -+ msm_pcie_write_mask(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG, -+ PCIE20_ACK_N_FTS, -+ dev->n_fts << 8); -+ readl_relaxed(dev->elbi); -+ -+ if (dev->shadow_en) -+ dev->rc_shadow[PCIE20_ACK_F_ASPM_CTRL_REG / 4] = -+ readl_relaxed(dev->dm_core + -+ PCIE20_ACK_F_ASPM_CTRL_REG); -+ -+ PCIE_DBG2(dev, "Updated PCIE20_ACK_F_ASPM_CTRL_REG:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG)); -+} -+ -+static void msm_pcie_config_l1ss(struct msm_pcie_dev_t *dev) -+{ -+ u32 offset = 0; -+ -+ if (!dev->rc_idx) -+ offset = PCIE20_EP_L1SUB_CTL1_OFFSET; -+ -+ /* Enable the AUX Clock and the Core Clk to be synchronous for L1SS*/ -+ if (!dev->aux_clk_sync) -+ msm_pcie_write_mask(dev->parf + -+ PCIE20_PARF_SYS_CTRL, BIT(3), 0); -+ -+ /* Enable L1SS on RC */ -+ msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS, 0, -+ BIT(1)|BIT(0)); -+ msm_pcie_write_mask(dev->dm_core + PCIE20_L1SUB_CONTROL1, 0, -+ BIT(3)|BIT(2)|BIT(1)|BIT(0)); -+ msm_pcie_write_mask(dev->dm_core + PCIE20_DEVICE_CONTROL2_STATUS2, 0, -+ BIT(10)); -+ readl_relaxed(dev->elbi); -+ if (dev->shadow_en) { -+ dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] = -+ readl_relaxed(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS); -+ dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] = -+ readl_relaxed(dev->dm_core + PCIE20_L1SUB_CONTROL1); -+ dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] = -+ readl_relaxed(dev->dm_core + -+ PCIE20_DEVICE_CONTROL2_STATUS2); -+ } -+ PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS)); -+ PCIE_DBG2(dev, "RC's L1SUB_CONTROL1:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_L1SUB_CONTROL1)); -+ PCIE_DBG2(dev, "RC's DEVICE_CONTROL2_STATUS2:0x%x\n", -+ readl_relaxed(dev->dm_core + PCIE20_DEVICE_CONTROL2_STATUS2)); -+ -+ /* Enable L1SS on EP */ -+ msm_pcie_write_mask(dev->conf + PCIE20_CAP_LINKCTRLSTATUS, 0, -+ BIT(1)|BIT(0)); -+ msm_pcie_write_mask(dev->conf + PCIE20_L1SUB_CONTROL1 + -+ offset, 0, -+ BIT(3)|BIT(2)|BIT(1)|BIT(0)); -+ msm_pcie_write_mask(dev->conf + PCIE20_DEVICE_CONTROL2_STATUS2, 0, -+ BIT(10)); -+ readl_relaxed(dev->elbi); -+ if (dev->shadow_en) { -+ dev->ep_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] = -+ readl_relaxed(dev->conf + -+ PCIE20_CAP_LINKCTRLSTATUS); -+ dev->ep_shadow[PCIE20_L1SUB_CONTROL1 / 4 + offset / 4] = -+ readl_relaxed(dev->conf + -+ PCIE20_L1SUB_CONTROL1 + offset); -+ dev->ep_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] = -+ readl_relaxed(dev->conf + -+ PCIE20_DEVICE_CONTROL2_STATUS2); -+ } -+ PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n", -+ readl_relaxed(dev->conf + PCIE20_CAP_LINKCTRLSTATUS)); -+ PCIE_DBG2(dev, "EP's L1SUB_CONTROL1:0x%x\n", -+ readl_relaxed(dev->conf + PCIE20_L1SUB_CONTROL1 + -+ offset)); -+ PCIE_DBG2(dev, "EP's DEVICE_CONTROL2_STATUS2:0x%x\n", -+ readl_relaxed(dev->conf + PCIE20_DEVICE_CONTROL2_STATUS2)); -+} -+ -+static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev, -+ struct platform_device *pdev) -+{ -+ int i, len, cnt, ret = 0; -+ struct msm_pcie_vreg_info_t *vreg_info; -+ struct msm_pcie_gpio_info_t *gpio_info; -+ struct msm_pcie_clk_info_t *clk_info; -+ struct msm_pcie_rst_info_t *rst_info; -+ struct resource *res; -+ struct msm_pcie_res_info_t *res_info; -+ struct msm_pcie_irq_info_t *irq_info; -+ char prop_name[MAX_PROP_SIZE]; -+ const __be32 *prop; -+ u32 *clkfreq = NULL; -+ -+ cnt = of_property_count_strings((&pdev->dev)->of_node, -+ "clock-names"); -+ if (cnt > 0) { -+ clkfreq = kzalloc(cnt * sizeof(*clkfreq), -+ GFP_KERNEL); -+ if (!clkfreq) { -+ PCIE_ERR(dev, "PCIe: memory alloc failed for RC%d\n", -+ dev->rc_idx); -+ return -ENOMEM; -+ } -+ ret = of_property_read_u32_array( -+ (&pdev->dev)->of_node, -+ "max-clock-frequency-hz", clkfreq, cnt); -+ if (ret) { -+ PCIE_ERR(dev, -+ "PCIe: invalid max-clock-frequency-hz property for RC%d:%d\n", -+ dev->rc_idx, ret); -+ goto out; -+ } -+ } -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (i = 0; i < MSM_PCIE_MAX_VREG; i++) { -+ vreg_info = &dev->vreg[i]; -+ vreg_info->hdl = -+ devm_regulator_get(&pdev->dev, vreg_info->name); -+ -+ if (PTR_ERR(vreg_info->hdl) == -EPROBE_DEFER) { -+ PCIE_DBG(dev, "EPROBE_DEFER for VReg:%s\n", -+ vreg_info->name); -+ ret = PTR_ERR(vreg_info->hdl); -+ goto out; -+ } -+ -+ if (IS_ERR(vreg_info->hdl)) { -+ if (vreg_info->required) { -+ PCIE_DBG(dev, "Vreg %s doesn't exist\n", -+ vreg_info->name); -+ ret = PTR_ERR(vreg_info->hdl); -+ goto out; -+ } else { -+ PCIE_DBG(dev, -+ "Optional Vreg %s doesn't exist\n", -+ vreg_info->name); -+ vreg_info->hdl = NULL; -+ } -+ } else { -+ dev->vreg_n++; -+ snprintf(prop_name, MAX_PROP_SIZE, -+ "qcom,%s-voltage-level", vreg_info->name); -+ prop = of_get_property((&pdev->dev)->of_node, -+ prop_name, &len); -+ if (!prop || (len != (3 * sizeof(__be32)))) { -+ PCIE_DBG(dev, "%s %s property\n", -+ prop ? "invalid format" : -+ "no", prop_name); -+ vreg_info->hdl = NULL; -+ } else { -+ vreg_info->max_v = be32_to_cpup(&prop[0]); -+ vreg_info->min_v = be32_to_cpup(&prop[1]); -+ vreg_info->opt_mode = -+ be32_to_cpup(&prop[2]); -+ } -+ } -+ } -+ -+ dev->gdsc = devm_regulator_get(&pdev->dev, "gdsc-vdd"); -+ -+ if (IS_ERR(dev->gdsc)) { -+ PCIE_ERR(dev, "PCIe: RC%d Failed to get %s GDSC:%ld\n", -+ dev->rc_idx, dev->pdev->name, PTR_ERR(dev->gdsc)); -+ if (PTR_ERR(dev->gdsc) == -EPROBE_DEFER) -+ PCIE_DBG(dev, "PCIe: EPROBE_DEFER for %s GDSC\n", -+ dev->pdev->name); -+ ret = PTR_ERR(dev->gdsc); -+ goto out; -+ } -+ -+ dev->gpio_n = 0; -+ for (i = 0; i < MSM_PCIE_MAX_GPIO; i++) { -+ gpio_info = &dev->gpio[i]; -+ ret = of_get_named_gpio((&pdev->dev)->of_node, -+ gpio_info->name, 0); -+ if (ret >= 0) { -+ gpio_info->num = ret; -+ ret = 0; -+ dev->gpio_n++; -+ PCIE_DBG(dev, "GPIO num for %s is %d\n", -+ gpio_info->name, gpio_info->num); -+ } else { -+ goto out; -+ } -+ } -+ -+ for (i = 0; i < MSM_PCIE_MAX_CLK; i++) { -+ clk_info = &dev->clk[i]; -+ -+ clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name); -+ -+ if (IS_ERR(clk_info->hdl)) { -+ if (clk_info->required) { -+ PCIE_DBG(dev, "Clock %s isn't available:%ld\n", -+ clk_info->name, PTR_ERR(clk_info->hdl)); -+ ret = PTR_ERR(clk_info->hdl); -+ goto out; -+ } else { -+ PCIE_DBG(dev, "Ignoring Clock %s\n", -+ clk_info->name); -+ clk_info->hdl = NULL; -+ } -+ } else { -+ if (clkfreq != NULL) { -+ clk_info->freq = clkfreq[i + -+ MSM_PCIE_MAX_PIPE_CLK]; -+ PCIE_DBG(dev, "Freq of Clock %s is:%d\n", -+ clk_info->name, clk_info->freq); -+ } -+ } -+ } -+ -+ for (i = 0; i < MSM_PCIE_MAX_RESET; i++) { -+ rst_info = &dev->rst[i]; -+ -+ rst_info->hdl = devm_reset_control_get(&pdev->dev, rst_info->name); -+ -+ if (IS_ERR(rst_info->hdl)) { -+ PCIE_DBG(dev, "Reset %s isn't available:%ld\n", -+ rst_info->name, PTR_ERR(rst_info->hdl)); -+ ret = PTR_ERR(rst_info->hdl); -+ goto out; -+ } -+ } -+ -+ -+ -+ dev->bus_scale_table = msm_bus_cl_get_pdata(pdev); -+ if (!dev->bus_scale_table) { -+ PCIE_DBG(dev, "PCIe: No bus scale table for RC%d (%s)\n", -+ dev->rc_idx, dev->pdev->name); -+ dev->bus_client = 0; -+ } else { -+ dev->bus_client = -+ msm_bus_scale_register_client(dev->bus_scale_table); -+ if (!dev->bus_client) { -+ PCIE_ERR(dev, -+ "PCIe: Failed to register bus client for RC%d (%s)\n", -+ dev->rc_idx, dev->pdev->name); -+ msm_bus_cl_clear_pdata(dev->bus_scale_table); -+ ret = -ENODEV; -+ goto out; -+ } -+ } -+ -+ for (i = 0; i < MSM_PCIE_MAX_RES; i++) { -+ res_info = &dev->res[i]; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ res_info->name); -+ -+ if (!res) { -+ PCIE_ERR(dev, "PCIe: RC%d can't get %s resource.\n", -+ dev->rc_idx, res_info->name); -+ ret = -ENOMEM; -+ goto out; -+ } else -+ PCIE_DBG(dev, "start addr for %s is %pa.\n", -+ res_info->name, &res->start); -+ -+ res_info->base = devm_ioremap(&pdev->dev, -+ res->start, resource_size(res)); -+ if (!res_info->base) { -+ PCIE_ERR(dev, "PCIe: RC%d can't remap %s.\n", -+ dev->rc_idx, res_info->name); -+ ret = -ENOMEM; -+ goto out; -+ } -+ res_info->resource = res; -+ } -+ -+ for (i = 0; i < MSM_PCIE_MAX_IRQ; i++) { -+ irq_info = &dev->irq[i]; -+ -+ /* Skip if wakeirq is not present in device tree */ -+ if (!dev->ep_wakeirq && (i == MSM_PCIE_INT_WAKE)) -+ continue; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, -+ irq_info->name); -+ -+ if (!res) { -+ int j; -+ for (j = 0; j < MSM_PCIE_MAX_RES; j++) { -+ iounmap(dev->res[j].base); -+ dev->res[j].base = NULL; -+ } -+ PCIE_ERR(dev, "PCIe: RC%d can't find IRQ # for %s.\n", -+ dev->rc_idx, irq_info->name); -+ ret = -ENODEV; -+ goto out; -+ } else { -+ irq_info->num = res->start; -+ PCIE_DBG(dev, "IRQ # for %s is %d.\n", irq_info->name, -+ irq_info->num); -+ } -+ } -+ -+ /* All allocations succeeded */ -+ -+ dev->wake_n = dev->irq[MSM_PCIE_INT_WAKE].num; -+ -+ dev->parf = dev->res[MSM_PCIE_RES_PARF].base; -+ dev->phy = dev->res[MSM_PCIE_RES_PHY].base; -+ dev->elbi = dev->res[MSM_PCIE_RES_ELBI].base; -+ dev->dm_core = dev->res[MSM_PCIE_RES_DM_CORE].base; -+ dev->conf = dev->res[MSM_PCIE_RES_CONF].base; -+ dev->bars = dev->res[MSM_PCIE_RES_BARS].base; -+ dev->dev_mem_res = dev->res[MSM_PCIE_RES_BARS].resource; -+ dev->dev_io_res = dev->res[MSM_PCIE_RES_IO].resource; -+ dev->dev_io_res->flags = IORESOURCE_IO; -+ -+out: -+ kfree(clkfreq); -+ return ret; -+} -+ -+static void msm_pcie_release_resources(struct msm_pcie_dev_t *dev) -+{ -+ dev->parf = NULL; -+ dev->elbi = NULL; -+ dev->dm_core = NULL; -+ dev->conf = NULL; -+ dev->bars = NULL; -+ dev->dev_mem_res = NULL; -+ dev->dev_io_res = NULL; -+} -+ -+int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) -+{ -+ int ret = 0; -+ uint32_t val; -+ long int retries = 0; -+ int link_check_count = 0; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ mutex_lock(&dev->setup_lock); -+ -+ if (dev->link_status == MSM_PCIE_LINK_ENABLED) { -+ PCIE_ERR(dev, "PCIe: the link of RC%d is already enabled\n", -+ dev->rc_idx); -+ goto out; -+ } -+ -+ -+ /* assert PCIe reset link to keep EP in reset */ -+ -+ PCIE_INFO(dev, "PCIe: trigger the reset of endpoint of RC%d.\n", -+ dev->rc_idx); -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ usleep_range(PERST_PROPAGATION_DELAY_US_MIN, -+ PERST_PROPAGATION_DELAY_US_MAX); -+ -+ /* enable power */ -+ -+ if (options & PM_VREG) { -+ ret = msm_pcie_vreg_init(dev); -+ if (ret) -+ goto out; -+ } -+ -+ /* enable clocks */ -+ if (options & PM_CLK) { -+ ret = msm_pcie_clk_init(dev); -+ wmb(); -+ if (ret) -+ goto clk_fail; -+ } -+ -+ /* enable PCIe clocks and resets */ -+ msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* change DBI base address */ -+ writel_relaxed(0, dev->parf + PCIE20_PARF_DBI_BASE_ADDR); -+ -+ if (dev->rc_idx) -+ writel_relaxed(0x361c, dev->parf + PCIE20_PARF_SYS_CTRL); -+ else -+ writel_relaxed(0x3656, dev->parf + PCIE20_PARF_SYS_CTRL); -+ -+ writel_relaxed(0, dev->parf + PCIE20_PARF_Q2A_FLUSH); -+ -+ if (dev->use_msi) { -+ PCIE_DBG(dev, "RC%d: enable WR halt.\n", dev->rc_idx); -+ msm_pcie_write_mask(dev->parf + -+ PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, 0, BIT(31)); -+ } -+ -+ /* init PCIe PHY */ -+ if (dev->is_emulation) -+ pcie_phy_init(dev); -+ -+ writel_relaxed(CMD_BME_VAL, dev->dm_core + PCIE20_COMMAND_STATUS); -+ writel_relaxed(DBI_RO_WR_EN, -+ dev->dm_core + PCIE20_MISC_CONTROL_1_REG); -+ writel_relaxed(PCIE_CAP_LINK1_VAL, dev->dm_core + PCIE20_CAP_LINK_1); -+ msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINK_CAPABILITIES, -+ BIT(10) | BIT(11), 0); -+ writel_relaxed(PCIE_CAP_CPL_TIMEOUT_DISABLE, -+ dev->dm_core + PCIE20_DEVICE_CONTROL2_STATUS2); -+ writel_relaxed(LTSSM_EN, dev->parf + PCIE20_PARF_LTSSM); -+ -+ PCIE_DBG(dev, "RC%d: waiting for phy ready...\n", dev->rc_idx); -+ -+ do { -+ if (pcie_phy_is_ready(dev)) -+ break; -+ retries++; -+ usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN, -+ REFCLK_STABILIZATION_DELAY_US_MAX); -+ } while (retries < PHY_READY_TIMEOUT_COUNT); -+ -+ PCIE_DBG(dev, "RC%d: number of PHY retries:%ld.\n", -+ dev->rc_idx, retries); -+ -+ if (pcie_phy_is_ready(dev)) -+ PCIE_INFO(dev, "PCIe RC%d PHY is ready!\n", dev->rc_idx); -+ else { -+ PCIE_ERR(dev, "PCIe PHY RC%d failed to come up!\n", -+ dev->rc_idx); -+ ret = -ENODEV; -+ goto link_fail; -+ } -+ -+ if (dev->ep_latency) -+ msleep(dev->ep_latency); -+ -+ /* de-assert PCIe reset link to bring EP out of reset */ -+ -+ PCIE_INFO(dev, "PCIe: Release the reset of endpoint of RC%d.\n", -+ dev->rc_idx); -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ 1 - dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ usleep_range(PERST_PROPAGATION_DELAY_US_MIN, -+ PERST_PROPAGATION_DELAY_US_MAX); -+ -+ /* enable link training */ -+ msm_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); -+ -+ PCIE_DBG(dev, "%s", "check if link is up\n"); -+ -+ if (dev->rc_idx == 1) { -+ PCIE_DBG(dev, "optimized link training for RC%d\n", -+ dev->rc_idx); -+ /* Wait for up to 100ms for the link to come up */ -+ do { -+ usleep_range(LINK_UP_TIMEOUT_US_MIN, -+ LINK_UP_TIMEOUT_US_MAX); -+ val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); -+ } while ((!(val & XMLH_LINK_UP) || -+ !msm_pcie_confirm_linkup(dev, false, false)) -+ && (link_check_count++ < LINK_UP_CHECK_MAX_COUNT)); -+ -+ if ((val & XMLH_LINK_UP) && -+ msm_pcie_confirm_linkup(dev, false, false)) -+ PCIE_DBG(dev, "Link is up after %d checkings\n", -+ link_check_count); -+ else -+ PCIE_DBG(dev, "Initial link training failed for RC%d\n", -+ dev->rc_idx); -+ } else { -+ PCIE_DBG(dev, "non-optimized link training for RC%d\n", -+ dev->rc_idx); -+ usleep_range(LINK_RETRY_TIMEOUT_US_MIN * 5 , -+ LINK_RETRY_TIMEOUT_US_MAX * 5); -+ val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); -+ } -+ -+ retries = 0; -+ -+ while ((!(val & XMLH_LINK_UP) || -+ !msm_pcie_confirm_linkup(dev, false, false)) -+ && (retries < MAX_LINK_RETRIES)) { -+ PCIE_ERR(dev, "RC%d:No. %ld:LTSSM_STATE:0x%x\n", dev->rc_idx, -+ retries + 1, (val >> 0xC) & 0x1f); -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ usleep_range(PERST_PROPAGATION_DELAY_US_MIN, -+ PERST_PROPAGATION_DELAY_US_MAX); -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ 1 - dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ usleep_range(LINK_RETRY_TIMEOUT_US_MIN, -+ LINK_RETRY_TIMEOUT_US_MAX); -+ retries++; -+ val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); -+ } -+ -+ PCIE_DBG(dev, "number of link training retries: %ld\n", retries); -+ -+ if ((val & XMLH_LINK_UP) && -+ msm_pcie_confirm_linkup(dev, false, false)) { -+ PCIE_INFO(dev, "PCIe RC%d link initialized\n", dev->rc_idx); -+ } else { -+ PCIE_INFO(dev, "PCIe: trigger the reset of endpoint of RC%d.\n", -+ dev->rc_idx); -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ PCIE_ERR(dev, "PCIe RC%d link initialization failed\n", -+ dev->rc_idx); -+ ret = -1; -+ goto link_fail; -+ } -+ -+ msm_pcie_config_controller(dev); -+ -+ if (!dev->msi_gicm_addr) -+ msm_pcie_config_msi_controller(dev); -+ -+ if (dev->l1ss_supported) -+ msm_pcie_config_l1ss(dev); -+ -+ dev->link_status = MSM_PCIE_LINK_ENABLED; -+ dev->power_on = true; -+ dev->suspending = false; -+ goto out; -+ -+link_fail: -+ msm_pcie_clk_deinit(dev); -+clk_fail: -+ msm_pcie_vreg_deinit(dev); -+out: -+ mutex_unlock(&dev->setup_lock); -+ -+ return ret; -+} -+ -+ -+void msm_pcie_disable(struct msm_pcie_dev_t *dev, u32 options) -+{ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ mutex_lock(&dev->setup_lock); -+ -+ if (!dev->power_on) { -+ PCIE_DBG(dev, -+ "PCIe: the link of RC%d is already power down.\n", -+ dev->rc_idx); -+ mutex_unlock(&dev->setup_lock); -+ return; -+ } -+ -+ dev->link_status = MSM_PCIE_LINK_DISABLED; -+ dev->power_on = false; -+ -+ PCIE_INFO(dev, "PCIe: trigger the reset of endpoint of RC%d.\n", -+ dev->rc_idx); -+ -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ -+ if (options & PM_CLK) { -+ msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, 0, -+ BIT(0)); -+ msm_pcie_clk_deinit(dev); -+ } -+ -+ if (options & PM_VREG) -+ msm_pcie_vreg_deinit(dev); -+ -+ mutex_unlock(&dev->setup_lock); -+} -+ -+static int msm_pcie_setup(int nr, struct pci_sys_data *sys) -+{ -+ struct msm_pcie_dev_t *dev = -+ (struct msm_pcie_dev_t *)(sys->private_data); -+ -+ PCIE_DBG(dev, "bus %d\n", nr); -+ /* -+ * specify linux PCI framework to allocate device memory (BARs) -+ * from msm_pcie_dev.dev_mem_res resource. -+ */ -+ sys->mem_offset = 0; -+ sys->io_offset = 0; -+ -+ pci_add_resource(&sys->resources, dev->dev_io_res); -+ pci_add_resource(&sys->resources, dev->dev_mem_res); -+ return 1; -+} -+ -+static int msm_rc_remove(struct msm_pcie_dev_t *dev) -+{ -+ msm_pcie_disable(dev, PM_PIPE_CLK | PM_CLK | PM_VREG); -+ pci_stop_root_bus(dev->pci_bus); -+ pci_remove_root_bus(dev->pci_bus); -+ dev->pci_bus = NULL; -+ dev->enumerated = false; -+ return 0; -+} -+ -+void msm_pcie_remove_bus(void) -+{ -+ int i; -+ -+ if (atomic_read(&rc_removed)) -+ return; -+ -+ for (i = 0; i < pcie_drv.rc_num; i++) { -+ pr_notice("---> Removing %d", i); -+ msm_rc_remove(&msm_pcie_dev[i]); -+ pr_notice(" ... done<---\n"); -+ } -+ -+ atomic_set(&rc_removed, 1); -+} -+ -+static struct pci_bus *msm_pcie_scan_bus(int nr, -+ struct pci_sys_data *sys) -+{ -+ struct pci_bus *bus = NULL; -+ struct msm_pcie_dev_t *dev = -+ (struct msm_pcie_dev_t *)(sys->private_data); -+ -+ PCIE_DBG(dev, "bus %d\n", nr); -+ -+ bus = pci_scan_root_bus(NULL, sys->busnr, &msm_pcie_ops, sys, -+ &sys->resources); -+ dev->pci_bus = bus; -+ -+ return bus; -+} -+ -+static int msm_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -+{ -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ int ret = 0; -+ -+ PCIE_DBG(pcie_dev, "rc %s slot %d pin %d\n", pcie_dev->pdev->name, -+ slot, pin); -+ -+ switch (pin) { -+ case 1: -+ ret = pcie_dev->irq[MSM_PCIE_INT_A].num; -+ break; -+ case 2: -+ ret = pcie_dev->irq[MSM_PCIE_INT_B].num; -+ break; -+ case 3: -+ ret = pcie_dev->irq[MSM_PCIE_INT_C].num; -+ break; -+ case 4: -+ ret = pcie_dev->irq[MSM_PCIE_INT_D].num; -+ break; -+ default: -+ PCIE_ERR(pcie_dev, "PCIe: RC%d: unsupported pin number.\n", -+ pcie_dev->rc_idx); -+ } -+ -+ return ret; -+} -+ -+#if 0 -+static void msm_pcie_add_bus(struct pci_bus *bus) -+{ -+ struct pci_sys_data *sys = bus->sysdata; -+ struct msm_pcie_dev_t *dev = -+ (struct msm_pcie_dev_t *)(sys->private_data); -+ -+ bus->msi = dev->msi_chip; -+} -+#endif -+ -+static struct hw_pci msm_pci[MAX_RC_NUM] = { -+ { -+ //.domain = 0, -+ .nr_controllers = 1, -+ .swizzle = pci_common_swizzle, -+ .setup = msm_pcie_setup, -+ .scan = msm_pcie_scan_bus, -+ .map_irq = msm_pcie_map_irq, -+ //.add_bus = msm_pcie_add_bus, -+ }, -+}; -+ -+int msm_pcie_rescan(void) -+{ -+ int i; -+ -+ if (!atomic_read(&rc_removed)) -+ return 0; -+ -+ for (i = 0; i < pcie_drv.rc_num; i++) { -+ /* reset the RC and enumurate devices */ -+ msm_pcie_controller_reset(&msm_pcie_dev[i]); -+ msm_pcie_enumerate(i); -+ } -+ -+ atomic_set(&rc_removed, 0); -+ -+ return 0; -+} -+ -+int msm_pcie_enumerate(u32 rc_idx) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx]; -+ -+ PCIE_DBG(dev, "Enumerate RC%d\n", rc_idx); -+ -+ if (!dev->enumerated) { -+ ret = msm_pcie_enable(dev, PM_ALL); -+ -+ /* kick start ARM PCI configuration framework */ -+ if (!ret) { -+ struct pci_dev *pcidev = NULL; -+ bool found = false; -+ u32 ids = readl_relaxed(msm_pcie_dev[rc_idx].dm_core); -+ u32 vendor_id = ids & 0xffff; -+ u32 device_id = (ids & 0xffff0000) >> 16; -+ -+ PCIE_DBG(dev, "vendor-id:0x%x device_id:0x%x\n", -+ vendor_id, device_id); -+ -+ msm_pci[rc_idx].private_data = (void **)&dev; -+ pci_common_init(&msm_pci[rc_idx]); -+ /* This has to happen only once */ -+ dev->enumerated = true; -+ -+ do { -+ pcidev = pci_get_device(vendor_id, -+ device_id, pcidev); -+ if (pcidev && (&msm_pcie_dev[rc_idx] == -+ (struct msm_pcie_dev_t *) -+ PCIE_BUS_PRIV_DATA(pcidev))) { -+ msm_pcie_dev[rc_idx].dev = pcidev; -+ found = true; -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "PCI device is found for RC%d\n", -+ rc_idx); -+ } -+ } while (!found && pcidev); -+ -+ if (!pcidev) { -+ PCIE_ERR(dev, -+ "PCIe: Did not find PCI device for RC%d.\n", -+ dev->rc_idx); -+ return -ENODEV; -+ } -+ } else { -+ PCIE_ERR(dev, "PCIe: failed to enable RC%d.\n", -+ dev->rc_idx); -+ } -+ } else { -+ PCIE_ERR(dev, "PCIe: RC%d has already been enumerated.\n", -+ dev->rc_idx); -+ } -+ -+ return ret; -+} -+ -+static ssize_t msm_bus_rescan_store(struct bus_type *bus, const char *buf, -+ size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) { -+ pci_lock_rescan_remove(); -+ msm_pcie_rescan(); -+ pci_unlock_rescan_remove(); -+ } -+ return count; -+} -+static BUS_ATTR(rcrescan, (S_IWUSR|S_IWGRP), NULL, msm_bus_rescan_store); -+ -+static ssize_t msm_bus_remove_store(struct bus_type *bus, const char *buf, -+ size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val) < 0) -+ return -EINVAL; -+ -+ if (val) { -+ pci_lock_rescan_remove(); -+ msm_pcie_remove_bus(); -+ pci_unlock_rescan_remove(); -+ } -+ return count; -+} -+static BUS_ATTR(rcremove, (S_IWUSR|S_IWGRP), NULL, msm_bus_remove_store); -+ -+static int msm_pcie_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ int rc_idx = -1; -+ int i; -+ -+ pr_debug("%s\n", __func__); -+ -+ mutex_lock(&pcie_drv.drv_lock); -+ -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "qcom,ctrl-amt", &pcie_drv.rc_expected); -+ if (ret) { -+ pr_err("PCIe: does not find controller amount.\n"); -+ goto out; -+ } else { -+ if (pcie_drv.rc_expected > MAX_RC_NUM) { -+ pr_debug("Expected number of devices %d\n", -+ pcie_drv.rc_expected); -+ pr_debug("Exceeded max supported devices %d\n", -+ MAX_RC_NUM); -+ goto out; -+ } -+ pr_debug("Target has %d RC(s).\n", pcie_drv.rc_expected); -+ } -+ -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "cell-index", &rc_idx); -+ if (ret) { -+ pr_debug("Did not find RC index.\n"); -+ goto out; -+ } else { -+ if (rc_idx >= MAX_RC_NUM) { -+ pr_err( -+ "PCIe: Invalid RC Index %d (max supported = %d)\n", -+ rc_idx, MAX_RC_NUM); -+ goto out; -+ } -+ pcie_drv.rc_num++; -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "PCIe: RC index is %d.\n", -+ rc_idx); -+ } -+ -+ msm_pcie_dev[rc_idx].l1ss_supported = -+ of_property_read_bool((&pdev->dev)->of_node, -+ "qcom,l1ss-supported"); -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "L1ss is %s supported.\n", -+ msm_pcie_dev[rc_idx].l1ss_supported ? "" : "not"); -+ msm_pcie_dev[rc_idx].aux_clk_sync = -+ of_property_read_bool((&pdev->dev)->of_node, -+ "qcom,aux-clk-sync"); -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "AUX clock is %s synchronous to Core clock.\n", -+ msm_pcie_dev[rc_idx].aux_clk_sync ? "" : "not"); -+ -+ msm_pcie_dev[rc_idx].ep_wakeirq = -+ of_property_read_bool((&pdev->dev)->of_node, -+ "qcom,ep-wakeirq"); -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "PCIe: EP of RC%d does %s assert wake when it is up.\n", -+ rc_idx, msm_pcie_dev[rc_idx].ep_wakeirq ? "" : "not"); -+ -+ msm_pcie_dev[rc_idx].n_fts = 0; -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "qcom,n-fts", -+ &msm_pcie_dev[rc_idx].n_fts); -+ -+ if (ret) -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "n-fts does not exist. ret=%d\n", ret); -+ else -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "n-fts: 0x%x.\n", -+ msm_pcie_dev[rc_idx].n_fts); -+ -+ msm_pcie_dev[rc_idx].is_emulation = -+ of_property_read_bool((&pdev->dev)->of_node, -+ "qcom,is_emulation"); -+ -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "is_emulation: 0x%x.\n", -+ msm_pcie_dev[rc_idx].is_emulation); -+ -+ msm_pcie_dev[rc_idx].ext_ref_clk = -+ of_property_read_bool((&pdev->dev)->of_node, -+ "qcom,ext-ref-clk"); -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "ref clk is %s.\n", -+ msm_pcie_dev[rc_idx].ext_ref_clk ? "external" : "internal"); -+ -+ msm_pcie_dev[rc_idx].ep_latency = 0; -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "qcom,ep-latency", -+ &msm_pcie_dev[rc_idx].ep_latency); -+ if (ret) -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "RC%d: ep-latency does not exist.\n", -+ rc_idx); -+ else -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n", -+ rc_idx, msm_pcie_dev[rc_idx].ep_latency); -+ -+ msm_pcie_dev[rc_idx].msi_gicm_addr = 0; -+ msm_pcie_dev[rc_idx].msi_gicm_base = 0; -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "qcom,msi-gicm-addr", -+ &msm_pcie_dev[rc_idx].msi_gicm_addr); -+ -+ if (ret) { -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "%s", -+ "msi-gicm-addr does not exist.\n"); -+ } else { -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "msi-gicm-addr: 0x%x.\n", -+ msm_pcie_dev[rc_idx].msi_gicm_addr); -+ -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "qcom,msi-gicm-base", -+ &msm_pcie_dev[rc_idx].msi_gicm_base); -+ -+ if (ret) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: msi-gicm-base does not exist.\n", -+ rc_idx); -+ goto decrease_rc_num; -+ } else { -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "msi-gicm-base: 0x%x\n", -+ msm_pcie_dev[rc_idx].msi_gicm_base); -+ } -+ } -+ -+ msm_pcie_dev[rc_idx].rc_idx = rc_idx; -+ msm_pcie_dev[rc_idx].pdev = pdev; -+ msm_pcie_dev[rc_idx].vreg_n = 0; -+ msm_pcie_dev[rc_idx].gpio_n = 0; -+ msm_pcie_dev[rc_idx].parf_deemph = 0; -+ msm_pcie_dev[rc_idx].parf_swing = 0; -+ msm_pcie_dev[rc_idx].link_status = MSM_PCIE_LINK_DEINIT; -+ msm_pcie_dev[rc_idx].user_suspend = false; -+ msm_pcie_dev[rc_idx].saved_state = NULL; -+ msm_pcie_dev[rc_idx].enumerated = false; -+ msm_pcie_dev[rc_idx].linkdown_counter = 0; -+ msm_pcie_dev[rc_idx].suspending = false; -+ msm_pcie_dev[rc_idx].wake_counter = 0; -+ msm_pcie_dev[rc_idx].req_exit_l1_counter = 0; -+ msm_pcie_dev[rc_idx].power_on = false; -+ msm_pcie_dev[rc_idx].use_msi = false; -+ memcpy(msm_pcie_dev[rc_idx].vreg, msm_pcie_vreg_info, -+ sizeof(msm_pcie_vreg_info)); -+ memcpy(msm_pcie_dev[rc_idx].gpio, msm_pcie_gpio_info, -+ sizeof(msm_pcie_gpio_info)); -+ memcpy(msm_pcie_dev[rc_idx].clk, msm_pcie_clk_info[rc_idx], -+ sizeof(msm_pcie_clk_info)); -+ -+ memcpy(msm_pcie_dev[rc_idx].rst, msm_pcie_rst_info, -+ sizeof(msm_pcie_rst_info)); -+ memcpy(msm_pcie_dev[rc_idx].res, msm_pcie_res_info, -+ sizeof(msm_pcie_res_info)); -+ memcpy(msm_pcie_dev[rc_idx].irq, msm_pcie_irq_info, -+ sizeof(msm_pcie_irq_info)); -+ msm_pcie_dev[rc_idx].shadow_en = true; -+ for (i = 0; i < PCIE_CONF_SPACE_DW; i++) { -+ msm_pcie_dev[rc_idx].rc_shadow[i] = PCIE_CLEAR; -+ msm_pcie_dev[rc_idx].ep_shadow[i] = PCIE_CLEAR; -+ } -+ -+ ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx], -+ msm_pcie_dev[rc_idx].pdev); -+ -+ if (ret) -+ goto decrease_rc_num; -+ -+ ret = msm_pcie_gpio_init(&msm_pcie_dev[rc_idx]); -+ if (ret) { -+ msm_pcie_release_resources(&msm_pcie_dev[rc_idx]); -+ goto decrease_rc_num; -+ } -+ -+ /* s/w reset of pcie */ -+ msm_pcie_controller_reset(&msm_pcie_dev[rc_idx]); -+ -+ /* detect uni phy before accessing the pcie registers */ -+ if (msm_pcie_dev[rc_idx].is_emulation && !pcie_phy_detect(&msm_pcie_dev[rc_idx])) { -+ ret = -ENODEV; -+ msm_pcie_release_resources(&msm_pcie_dev[rc_idx]); -+ msm_pcie_gpio_deinit(&msm_pcie_dev[rc_idx]); -+ goto decrease_rc_num; -+ } -+ -+ ret = msm_pcie_irq_init(&msm_pcie_dev[rc_idx]); -+ if (ret) { -+ msm_pcie_release_resources(&msm_pcie_dev[rc_idx]); -+ msm_pcie_gpio_deinit(&msm_pcie_dev[rc_idx]); -+ goto decrease_rc_num; -+ } -+ -+ if (msm_pcie_dev[rc_idx].ep_wakeirq) { -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d will be enumerated upon WAKE signal from Endpoint.\n", -+ rc_idx); -+ mutex_unlock(&pcie_drv.drv_lock); -+ return 0; -+ } -+ -+ ret = msm_pcie_enumerate(rc_idx); -+ -+ if (ret) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d is not enabled during bootup; it will be enumerated upon WAKE signal.\n", -+ rc_idx); -+ goto decrease_rc_num; -+ } else { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], "RC%d is enabled in bootup\n", -+ rc_idx); -+ } -+ -+ PCIE_DBG(&msm_pcie_dev[rc_idx], "PCIE probed %s\n", -+ dev_name(&(pdev->dev))); -+ -+ /* create sysfs files to support power save mode */ -+ if (!rc_idx) { -+ ret = bus_create_file(&pci_bus_type, &bus_attr_rcrescan); -+ if (ret != 0) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "RC%d failed to create sysfs rcrescan file\n", -+ rc_idx); -+ } -+ -+ ret = bus_create_file(&pci_bus_type, &bus_attr_rcremove); -+ if (ret != 0) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "RC%d failed to create sysfs rcremove file\n", -+ rc_idx); -+ } -+ } -+ -+ mutex_unlock(&pcie_drv.drv_lock); -+ return 0; -+ -+decrease_rc_num: -+ pcie_drv.rc_num--; -+out: -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: Driver probe failed for RC%d:%d\n", -+ rc_idx, ret); -+ mutex_unlock(&pcie_drv.drv_lock); -+ -+ return ret; -+} -+ -+static int __exit msm_pcie_remove(struct platform_device *pdev) -+{ -+ int ret = 0; -+ int rc_idx; -+ -+ pr_debug("PCIe:%s.\n", __func__); -+ -+ mutex_lock(&pcie_drv.drv_lock); -+ -+ ret = of_property_read_u32((&pdev->dev)->of_node, -+ "cell-index", &rc_idx); -+ if (ret) { -+ pr_err("%s: Did not find RC index.\n", __func__); -+ goto out; -+ } else { -+ pcie_drv.rc_num--; -+ pr_debug("%s: RC index is 0x%x.", __func__, rc_idx); -+ } -+ -+ msm_pcie_irq_deinit(&msm_pcie_dev[rc_idx]); -+ msm_pcie_vreg_deinit(&msm_pcie_dev[rc_idx]); -+ msm_pcie_clk_deinit(&msm_pcie_dev[rc_idx]); -+ msm_pcie_gpio_deinit(&msm_pcie_dev[rc_idx]); -+ msm_pcie_release_resources(&msm_pcie_dev[rc_idx]); -+ -+out: -+ mutex_unlock(&pcie_drv.drv_lock); -+ -+ return ret; -+} -+ -+static struct of_device_id msm_pcie_match[] = { -+ { .compatible = "qcom,msm_pcie", -+ }, -+ {} -+}; -+ -+static struct platform_driver msm_pcie_driver = { -+ .probe = msm_pcie_probe, -+ .remove = msm_pcie_remove, -+ .driver = { -+ .name = "msm_pcie", -+ .owner = THIS_MODULE, -+ .of_match_table = msm_pcie_match, -+ }, -+}; -+ -+static int __init pcie_init(void) -+{ -+ int ret = 0, i; -+#ifdef CONFIG_IPC_LOGGING -+ char rc_name[MAX_RC_NAME_LEN]; -+#endif -+ -+ pr_debug("pcie:%s.\n", __func__); -+ -+ pcie_drv.rc_num = 0; -+ pcie_drv.rc_expected = 0; -+ mutex_init(&pcie_drv.drv_lock); -+ -+ for (i = 0; i < MAX_RC_NUM; i++) { -+#ifdef CONFIG_IPC_LOGGING -+ snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-short", i); -+ msm_pcie_dev[i].ipc_log = -+ ipc_log_context_create(PCIE_LOG_PAGES, rc_name, 0); -+ if (msm_pcie_dev[i].ipc_log == NULL) -+ pr_err("%s: unable to create IPC log context for %s\n", -+ __func__, rc_name); -+ else -+ PCIE_DBG(&msm_pcie_dev[i], -+ "PCIe IPC logging is enable for RC%d\n", -+ i); -+ snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-long", i); -+ msm_pcie_dev[i].ipc_log_long = -+ ipc_log_context_create(PCIE_LOG_PAGES, rc_name, 0); -+ if (msm_pcie_dev[i].ipc_log_long == NULL) -+ pr_err("%s: unable to create IPC log context for %s\n", -+ __func__, rc_name); -+ else -+ PCIE_DBG(&msm_pcie_dev[i], -+ "PCIe IPC logging %s is enable for RC%d\n", -+ rc_name, i); -+#endif -+ -+ spin_lock_init(&msm_pcie_dev[i].cfg_lock); -+ msm_pcie_dev[i].cfg_access = true; -+ mutex_init(&msm_pcie_dev[i].setup_lock); -+ mutex_init(&msm_pcie_dev[i].recovery_lock); -+ spin_lock_init(&msm_pcie_dev[i].linkdown_lock); -+ spin_lock_init(&msm_pcie_dev[i].wakeup_lock); -+ } -+ -+ ret = platform_driver_register(&msm_pcie_driver); -+ -+ return ret; -+} -+ -+static void __exit pcie_exit(void) -+{ -+ pr_debug("pcie:%s.\n", __func__); -+ -+ platform_driver_unregister(&msm_pcie_driver); -+} -+ -+subsys_initcall_sync(pcie_init); -+module_exit(pcie_exit); -+ -+ -+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */ -+static void msm_pcie_fixup_early(struct pci_dev *dev) -+{ -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ PCIE_DBG(pcie_dev, "hdr_type %d\n", dev->hdr_type); -+ if (dev->hdr_type == 1) -+ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8); -+} -+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, -+ msm_pcie_fixup_early); -+ -+/* Suspend the PCIe link */ -+static int msm_pcie_pm_suspend(struct pci_dev *dev, -+ void *user, void *data, u32 options) -+{ -+ int ret = 0; -+ u32 val = 0; -+ int ret_l23; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ pcie_dev->suspending = true; -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ if (!pcie_dev->power_on) { -+ PCIE_DBG(pcie_dev, -+ "PCIe: power of RC%d has been turned off.\n", -+ pcie_dev->rc_idx); -+ return ret; -+ } -+ -+ if (dev && !(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE) -+ && msm_pcie_confirm_linkup(pcie_dev, true, true)) { -+ ret = pci_save_state(dev); -+ pcie_dev->saved_state = pci_store_saved_state(dev); -+ } -+ if (ret) { -+ PCIE_ERR(pcie_dev, "PCIe: fail to save state of RC%d:%d.\n", -+ pcie_dev->rc_idx, ret); -+ pcie_dev->suspending = false; -+ return ret; -+ } -+ -+ spin_lock_irqsave(&pcie_dev->cfg_lock, -+ pcie_dev->irqsave_flags); -+ pcie_dev->cfg_access = false; -+ spin_unlock_irqrestore(&pcie_dev->cfg_lock, -+ pcie_dev->irqsave_flags); -+ -+ msm_pcie_write_mask(pcie_dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, -+ BIT(4)); -+ -+ PCIE_DBG(pcie_dev, "RC%d: PME_TURNOFF_MSG is sent out\n", -+ pcie_dev->rc_idx); -+ -+ ret_l23 = readl_poll_timeout((pcie_dev->parf -+ + PCIE20_PARF_PM_STTS), val, (val & BIT(6)), 10000, 100000); -+ -+ /* check L23_Ready */ -+ if (!ret_l23) -+ PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is received\n", -+ pcie_dev->rc_idx); -+ else -+ PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is NOT received\n", -+ pcie_dev->rc_idx); -+ -+ msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG); -+ -+ return ret; -+} -+ -+static void msm_pcie_fixup_suspend(struct pci_dev *dev) -+{ -+ int ret; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED) -+ return; -+ -+ mutex_lock(&pcie_dev->recovery_lock); -+ -+ ret = msm_pcie_pm_suspend(dev, NULL, NULL, 0); -+ if (ret) -+ PCIE_ERR(pcie_dev, "PCIe: RC%d got failure in suspend:%d.\n", -+ pcie_dev->rc_idx, ret); -+ -+ mutex_unlock(&pcie_dev->recovery_lock); -+} -+DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, -+ msm_pcie_fixup_suspend); -+ -+/* Resume the PCIe link */ -+static int msm_pcie_pm_resume(struct pci_dev *dev, -+ void *user, void *data, u32 options) -+{ -+ int ret; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ spin_lock_irqsave(&pcie_dev->cfg_lock, -+ pcie_dev->irqsave_flags); -+ pcie_dev->cfg_access = true; -+ spin_unlock_irqrestore(&pcie_dev->cfg_lock, -+ pcie_dev->irqsave_flags); -+ -+ ret = msm_pcie_enable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG); -+ if (ret) { -+ PCIE_ERR(pcie_dev, -+ "PCIe: RC%d fail to enable PCIe link in resume.\n", -+ pcie_dev->rc_idx); -+ return ret; -+ } else { -+ pcie_dev->suspending = false; -+ PCIE_DBG(pcie_dev, -+ "dev->bus->number = %d dev->bus->primary = %d\n", -+ dev->bus->number, dev->bus->primary); -+ -+ if (!(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)) { -+ pci_load_and_free_saved_state(dev, -+ &pcie_dev->saved_state); -+ pci_restore_state(dev); -+ } -+ } -+ -+ return ret; -+} -+ -+void msm_pcie_fixup_resume(struct pci_dev *dev) -+{ -+ int ret; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) || -+ pcie_dev->user_suspend) -+ return; -+ -+ mutex_lock(&pcie_dev->recovery_lock); -+ ret = msm_pcie_pm_resume(dev, NULL, NULL, 0); -+ if (ret) -+ PCIE_ERR(pcie_dev, -+ "PCIe: RC%d got failure in fixup resume:%d.\n", -+ pcie_dev->rc_idx, ret); -+ -+ mutex_unlock(&pcie_dev->recovery_lock); -+} -+DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, -+ msm_pcie_fixup_resume); -+ -+void msm_pcie_fixup_resume_early(struct pci_dev *dev) -+{ -+ int ret; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) || -+ pcie_dev->user_suspend) -+ return; -+ -+ mutex_lock(&pcie_dev->recovery_lock); -+ ret = msm_pcie_pm_resume(dev, NULL, NULL, 0); -+ if (ret) -+ PCIE_ERR(pcie_dev, "PCIe: RC%d got failure in resume:%d.\n", -+ pcie_dev->rc_idx, ret); -+ -+ mutex_unlock(&pcie_dev->recovery_lock); -+} -+DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, -+ msm_pcie_fixup_resume_early); -+ -+static void msm_pcie_fixup_final(struct pci_dev *dev) -+{ -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ pcie_drv.current_rc++; -+} -+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, msm_pcie_fixup_final); -+ -+int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, -+ void *data, u32 options) -+{ -+ int ret = 0; -+ struct pci_dev *dev; -+ u32 rc_idx = 0; -+ -+ pr_debug("PCIe: pm_opt:%d;busnr:%d;options:%d\n", -+ pm_opt, busnr, options); -+ -+ switch (busnr) { -+ case 1: -+ if (user) { -+ struct msm_pcie_dev_t *pcie_dev -+ = PCIE_BUS_PRIV_DATA(((struct pci_dev *)user)); -+ -+ if (pcie_dev) { -+ rc_idx = pcie_dev->rc_idx; -+ PCIE_DBG(pcie_dev, -+ "PCIe: RC%d: pm_opt:%d;busnr:%d;options:%d\n", -+ rc_idx, pm_opt, busnr, options); -+ } else { -+ pr_err( -+ "PCIe: did not find RC for pci endpoint device 0x%x.\n", -+ (u32)user); -+ ret = -ENODEV; -+ goto out; -+ } -+ } -+ break; -+ default: -+ pr_err("PCIe: unsupported bus number.\n"); -+ ret = PCIBIOS_DEVICE_NOT_FOUND; -+ goto out; -+ } -+ -+ dev = msm_pcie_dev[rc_idx].dev; -+ -+ switch (pm_opt) { -+ case MSM_PCIE_SUSPEND: -+ if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_ENABLED) -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: requested to suspend when link is not enabled:%d.\n", -+ rc_idx, msm_pcie_dev[rc_idx].link_status); -+ -+ if (!msm_pcie_dev[rc_idx].power_on) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: requested to suspend when link is powered down:%d.\n", -+ rc_idx, msm_pcie_dev[rc_idx].link_status); -+ break; -+ } -+ -+ msm_pcie_dev[rc_idx].user_suspend = true; -+ -+ mutex_lock(&msm_pcie_dev[rc_idx].recovery_lock); -+ -+ ret = msm_pcie_pm_suspend(dev, user, data, options); -+ if (ret) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: user failed to suspend the link.\n", -+ rc_idx); -+ msm_pcie_dev[rc_idx].user_suspend = false; -+ } -+ -+ mutex_unlock(&msm_pcie_dev[rc_idx].recovery_lock); -+ break; -+ case MSM_PCIE_RESUME: -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "User of RC%d requests to resume the link\n", rc_idx); -+ if (msm_pcie_dev[rc_idx].link_status != -+ MSM_PCIE_LINK_DISABLED) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: requested to resume when link is not disabled:%d.\n", -+ rc_idx, msm_pcie_dev[rc_idx].link_status); -+ break; -+ } -+ -+ mutex_lock(&msm_pcie_dev[rc_idx].recovery_lock); -+ ret = msm_pcie_pm_resume(dev, user, data, options); -+ if (ret) { -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: user failed to resume the link.\n", -+ rc_idx); -+ } else { -+ PCIE_DBG(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: user succeeded to resume the link.\n", -+ rc_idx); -+ -+ msm_pcie_dev[rc_idx].user_suspend = false; -+ } -+ -+ mutex_unlock(&msm_pcie_dev[rc_idx].recovery_lock); -+ break; -+ case MSM_PCIE_REQ_EXIT_L1: -+ msm_pcie_dev[rc_idx].req_exit_l1_counter++; -+ msm_pcie_write_mask(msm_pcie_dev[rc_idx].parf -+ + PCIE20_PARF_PM_CTRL, -+ 0, BIT(1)); -+ udelay(REQ_EXIT_L1_DELAY_US); -+ msm_pcie_write_mask(msm_pcie_dev[rc_idx].parf -+ + PCIE20_PARF_PM_CTRL, -+ BIT(1), 0); -+ break; -+ default: -+ PCIE_ERR(&msm_pcie_dev[rc_idx], -+ "PCIe: RC%d: unsupported pm operation:%d.\n", -+ rc_idx, pm_opt); -+ ret = -ENODEV; -+ goto out; -+ } -+ -+out: -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_pm_control); -+ -+int msm_pcie_register_event(struct msm_pcie_register_event *reg) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *pcie_dev; -+ -+ if (!reg) { -+ pr_err("PCIe: Event registration is NULL\n"); -+ return -ENODEV; -+ } -+ -+ if (!reg->user) { -+ pr_err("PCIe: User of event registration is NULL\n"); -+ return -ENODEV; -+ } -+ -+ pcie_dev = PCIE_BUS_PRIV_DATA(((struct pci_dev *)reg->user)); -+ -+ if (pcie_dev) { -+ pcie_dev->event_reg = reg; -+ PCIE_DBG(pcie_dev, -+ "Event 0x%x is registered for RC %d\n", reg->events, -+ pcie_dev->rc_idx); -+ } else { -+ PCIE_ERR(pcie_dev, -+ "PCIe: did not find RC for pci endpoint device 0x%x.\n", -+ (u32)reg->user); -+ ret = -ENODEV; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_register_event); -+ -+int msm_pcie_deregister_event(struct msm_pcie_register_event *reg) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *pcie_dev; -+ -+ if (!reg) { -+ pr_err("PCIe: Event deregistration is NULL\n"); -+ return -ENODEV; -+ } -+ -+ if (!reg->user) { -+ pr_err("PCIe: User of event deregistration is NULL\n"); -+ return -ENODEV; -+ } -+ -+ pcie_dev = PCIE_BUS_PRIV_DATA(((struct pci_dev *)reg->user)); -+ -+ if (pcie_dev) { -+ pcie_dev->event_reg = NULL; -+ PCIE_DBG(pcie_dev, "Event is deregistered for RC %d\n", -+ pcie_dev->rc_idx); -+ } else { -+ PCIE_ERR(pcie_dev, -+ "PCIe: did not find RC for pci endpoint device 0x%x.\n", -+ (u32)reg->user); -+ ret = -ENODEV; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_deregister_event); -+ -+int msm_pcie_recover_config(struct pci_dev *dev) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *pcie_dev; -+ -+ if (dev) { -+ pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ PCIE_DBG(pcie_dev, -+ "Recovery for the link of RC%d\n", pcie_dev->rc_idx); -+ } else { -+ pr_err("PCIe: the input pci dev is NULL.\n"); -+ return -ENODEV; -+ } -+ -+ if (msm_pcie_confirm_linkup(pcie_dev, true, true)) { -+ PCIE_DBG(pcie_dev, -+ "Recover config space of RC%d and its EP\n", -+ pcie_dev->rc_idx); -+ pcie_dev->shadow_en = false; -+ PCIE_DBG(pcie_dev, "Recover RC%d\n", pcie_dev->rc_idx); -+ msm_pcie_cfg_recover(pcie_dev, true); -+ PCIE_DBG(pcie_dev, "Recover EP of RC%d\n", pcie_dev->rc_idx); -+ msm_pcie_cfg_recover(pcie_dev, false); -+ PCIE_DBG(pcie_dev, -+ "Refreshing the saved config space in PCI framework for RC%d and its EP\n", -+ pcie_dev->rc_idx); -+ pci_save_state(pcie_dev->dev); -+ pci_save_state(dev); -+ pcie_dev->shadow_en = true; -+ PCIE_DBG(pcie_dev, "Turn on shadow for RC%d\n", -+ pcie_dev->rc_idx); -+ } else { -+ PCIE_ERR(pcie_dev, -+ "PCIe: the link of RC%d is not up yet; can't recover config space.\n", -+ pcie_dev->rc_idx); -+ ret = -ENODEV; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_recover_config); -+ -+int msm_pcie_shadow_control(struct pci_dev *dev, bool enable) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *pcie_dev; -+ -+ if (dev) { -+ pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ PCIE_DBG(pcie_dev, -+ "Recovery for the link of RC%d\n", pcie_dev->rc_idx); -+ } else { -+ pr_err("PCIe: the input pci dev is NULL.\n"); -+ return -ENODEV; -+ } -+ -+ PCIE_DBG(pcie_dev, -+ "The shadowing of RC%d is %s enabled currently.\n", -+ pcie_dev->rc_idx, pcie_dev->shadow_en ? "" : "not"); -+ -+ pcie_dev->shadow_en = enable; -+ -+ PCIE_DBG(pcie_dev, -+ "Shadowing of RC%d is turned %s upon user's request.\n", -+ pcie_dev->rc_idx, enable ? "on" : "off"); -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_shadow_control); -+ -+int msm_pcie_access_control(struct pci_dev *dev, bool allow_access) -+{ -+ int ret = 0; -+ struct msm_pcie_dev_t *pcie_dev; -+ -+ if (dev) { -+ pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ PCIE_DBG(pcie_dev, -+ "access control for the link of RC%d\n", -+ pcie_dev->rc_idx); -+ } else { -+ pr_err("PCIe: the input pci dev is NULL.\n"); -+ return -ENODEV; -+ } -+ -+ mutex_lock(&pcie_dev->recovery_lock); -+ -+ PCIE_DBG(pcie_dev, -+ "The config space of RC%d is %savailable currently.\n", -+ pcie_dev->rc_idx, pcie_dev->cfg_access ? "" : "un"); -+ -+ pcie_dev->cfg_access = allow_access; -+ -+ PCIE_DBG(pcie_dev, -+ "The config space of RC%d becomes %savailable upon user's request.\n", -+ pcie_dev->rc_idx, pcie_dev->cfg_access ? "" : "un"); -+ -+ mutex_unlock(&pcie_dev->recovery_lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_pcie_access_control); -diff --git a/arch/arm/mach-qcom/pcie.h b/arch/arm/mach-qcom/pcie.h -new file mode 100644 -index 0000000..94c0417 ---- /dev/null -+++ b/arch/arm/mach-qcom/pcie.h -@@ -0,0 +1,329 @@ -+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __ARCH_ARM_MACH_MSM_PCIE_H -+#define __ARCH_ARM_MACH_MSM_PCIE_H -+ -+#include -+#include -+#ifdef CONFIG_IPC_LOGGING -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#define MSM_PCIE_MAX_VREG 3 -+#define MSM_PCIE_MAX_CLK 3 -+#define MSM_PCIE_MAX_PIPE_CLK 1 -+ -+#define MAX_RC_NUM 1 -+ -+#ifdef CONFIG_ARM_LPAE -+#define PCIE_UPPER_ADDR(addr) ((u32)((addr) >> 32)) -+#else -+#define PCIE_UPPER_ADDR(addr) (0x0) -+#endif -+#define PCIE_LOWER_ADDR(addr) ((u32)((addr) & 0xffffffff)) -+ -+#define PCIE_MSI_NR_IRQS 256 -+ -+#define PCIE_LOG_PAGES (50) -+ -+#ifdef CONFIG_IPC_LOGGING -+ -+#define PCIE_DBG(dev, fmt, arg...) do { \ -+ if ((dev) && (dev)->ipc_log_long) \ -+ ipc_log_string((dev)->ipc_log_long, \ -+ "DBG1:%s: " fmt, __func__, arg); \ -+ if ((dev) && (dev)->ipc_log) \ -+ ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_DBG2(dev, fmt, arg...) do { \ -+ if ((dev) && (dev)->ipc_log) \ -+ ipc_log_string((dev)->ipc_log, "DBG2:%s: " fmt, __func__, arg);\ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_DBG3(dev, fmt, arg...) do { \ -+ if ((dev) && (dev)->ipc_log) \ -+ ipc_log_string((dev)->ipc_log, "DBG3:%s: " fmt, __func__, arg);\ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_INFO(dev, fmt, arg...) do { \ -+ if ((dev) && (dev)->ipc_log_long) \ -+ ipc_log_string((dev)->ipc_log_long, \ -+ "INFO:%s: " fmt, __func__, arg); \ -+ if ((dev) && (dev)->ipc_log) \ -+ ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \ -+ pr_info("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_ERR(dev, fmt, arg...) do { \ -+ if ((dev) && (dev)->ipc_log_long) \ -+ ipc_log_string((dev)->ipc_log_long, \ -+ "ERR:%s: " fmt, __func__, arg); \ -+ if ((dev) && (dev)->ipc_log) \ -+ ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \ -+ pr_err("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#else -+ -+#define PCIE_DBG(dev, fmt, arg...) do { \ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_DBG2(dev, fmt, arg...) do { \ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_DBG3(dev, fmt, arg...) do { \ -+ if (msm_pcie_get_debug_mask()) \ -+ pr_alert("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_INFO(dev, fmt, arg...) do { \ -+ pr_info("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#define PCIE_ERR(dev, fmt, arg...) do { \ -+ pr_err("%s: " fmt, __func__, arg); \ -+ } while (0) -+ -+#endif -+ -+#define PCIE_BUS_PRIV_DATA(pdev) \ -+ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data) -+ -+/* PM control options */ -+#define PM_IRQ 0x1 -+#define PM_CLK 0x2 -+#define PM_GPIO 0x4 -+#define PM_VREG 0x8 -+#define PM_PIPE_CLK 0x10 -+#define PM_ALL (PM_IRQ | PM_CLK | PM_GPIO | PM_VREG | PM_PIPE_CLK) -+ -+#define PCIE_CONF_SPACE_DW 1024 -+#define PCIE_CLEAR 0xDEADBEEF -+#define PCIE_LINK_DOWN 0xFFFFFFFF -+ -+enum msm_pcie_res { -+ MSM_PCIE_RES_PARF, -+ MSM_PCIE_RES_PHY, -+ MSM_PCIE_RES_DM_CORE, -+ MSM_PCIE_RES_ELBI, -+ MSM_PCIE_RES_CONF, -+ MSM_PCIE_RES_IO, -+ MSM_PCIE_RES_BARS, -+ MSM_PCIE_MAX_RES, -+}; -+ -+enum msm_pcie_rst { -+ MSM_PCIE_AXI_M_ARES, -+ MSM_PCIE_AXI_S_ARES, -+ MSM_PCIE_PIPE_ARES, -+ MSM_PCIE_AXI_M_VMIDMT_ARES, -+ MSM_PCIE_AXI_S_XPU_ARES, -+ MSM_PCIE_PARF_XPU_ARES, -+ MSM_PCIE_PHY_ARES, -+ MSM_PCIE_AXI_M_STICKY_ARES, -+ MSM_PCIE_PIPE_STICKY_ARES, -+ MSM_PCIE_PWR_ARES, -+ MSM_PCIE_AHB_ARES, -+ MSM_PCIE_PHY_AHB_ARES, -+ MSM_PCIE_MAX_RESET, -+}; -+ -+enum msm_pcie_irq { -+ MSM_PCIE_INT_MSI, -+ MSM_PCIE_INT_A, -+ MSM_PCIE_INT_B, -+ MSM_PCIE_INT_C, -+ MSM_PCIE_INT_D, -+ MSM_PCIE_INT_PLS_PME, -+ MSM_PCIE_INT_PME_LEGACY, -+ MSM_PCIE_INT_PLS_ERR, -+ MSM_PCIE_INT_AER_LEGACY, -+ MSM_PCIE_INT_LINK_UP, -+ MSM_PCIE_INT_LINK_DOWN, -+ MSM_PCIE_INT_BRIDGE_FLUSH_N, -+ MSM_PCIE_INT_WAKE, -+ MSM_PCIE_MAX_IRQ, -+}; -+ -+/* gpios */ -+enum msm_pcie_gpio { -+ MSM_PCIE_GPIO_PERST, -+ MSM_PCIE_GPIO_WAKE, -+ MSM_PCIE_GPIO_CLKREQ, -+ MSM_PCIE_MAX_GPIO -+}; -+ -+enum msm_pcie_link_status { -+ MSM_PCIE_LINK_DEINIT, -+ MSM_PCIE_LINK_ENABLED, -+ MSM_PCIE_LINK_DISABLED -+}; -+ -+/* gpio info structure */ -+struct msm_pcie_gpio_info_t { -+ char *name; -+ uint32_t num; -+ bool out; -+ uint32_t on; -+ uint32_t init; -+}; -+ -+/* voltage regulator info structrue */ -+struct msm_pcie_vreg_info_t { -+ struct regulator *hdl; -+ char *name; -+ uint32_t max_v; -+ uint32_t min_v; -+ uint32_t opt_mode; -+ bool required; -+}; -+ -+/* reset info structure */ -+struct msm_pcie_rst_info_t { -+ struct reset_control *hdl; -+ char *name; -+}; -+ -+/* clock info structure */ -+struct msm_pcie_clk_info_t { -+ struct clk *hdl; -+ char *name; -+ u32 freq; -+ bool required; -+}; -+ -+/* resource info structure */ -+struct msm_pcie_res_info_t { -+ char *name; -+ struct resource *resource; -+ void __iomem *base; -+}; -+ -+/* irq info structrue */ -+struct msm_pcie_irq_info_t { -+ char *name; -+ uint32_t num; -+}; -+ -+/* msm pcie device structure */ -+struct msm_pcie_dev_t { -+ struct platform_device *pdev; -+ struct pci_dev *dev; -+ struct msi_controller *msi_chip; -+ struct regulator *gdsc; -+ struct msm_pcie_vreg_info_t vreg[MSM_PCIE_MAX_VREG]; -+ struct msm_pcie_gpio_info_t gpio[MSM_PCIE_MAX_GPIO]; -+ struct msm_pcie_clk_info_t clk[MSM_PCIE_MAX_CLK]; -+ struct msm_pcie_res_info_t res[MSM_PCIE_MAX_RES]; -+ struct msm_pcie_irq_info_t irq[MSM_PCIE_MAX_IRQ]; -+ struct msm_pcie_rst_info_t rst[MSM_PCIE_MAX_RESET]; -+ -+ void __iomem *parf; -+ void __iomem *phy; -+ void __iomem *elbi; -+ void __iomem *dm_core; -+ void __iomem *conf; -+ void __iomem *bars; -+ -+ uint32_t axi_bar_start; -+ uint32_t axi_bar_end; -+ -+ struct resource *dev_mem_res; -+ struct resource *dev_io_res; -+ -+ uint32_t wake_n; -+ uint32_t vreg_n; -+ uint32_t gpio_n; -+ uint32_t parf_deemph; -+ uint32_t parf_swing; -+ -+ bool cfg_access; -+ spinlock_t cfg_lock; -+ unsigned long irqsave_flags; -+ struct mutex setup_lock; -+ -+ struct irq_domain *irq_domain; -+ DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_NR_IRQS); -+ uint32_t msi_gicm_addr; -+ uint32_t msi_gicm_base; -+ bool use_msi; -+ bool is_emulation; -+ -+ enum msm_pcie_link_status link_status; -+ bool user_suspend; -+ struct pci_saved_state *saved_state; -+ -+ struct wakeup_source ws; -+ struct msm_bus_scale_pdata *bus_scale_table; -+ uint32_t bus_client; -+ -+ bool l1ss_supported; -+ bool aux_clk_sync; -+ uint32_t n_fts; -+ bool ext_ref_clk; -+ uint32_t ep_latency; -+ bool ep_wakeirq; -+ -+ uint32_t rc_idx; -+ bool enumerated; -+ struct work_struct handle_wake_work; -+ struct mutex recovery_lock; -+ spinlock_t linkdown_lock; -+ spinlock_t wakeup_lock; -+ ulong linkdown_counter; -+ bool suspending; -+ ulong wake_counter; -+ ulong req_exit_l1_counter; -+ u32 ep_shadow[PCIE_CONF_SPACE_DW]; -+ u32 rc_shadow[PCIE_CONF_SPACE_DW]; -+ bool shadow_en; -+ struct msm_pcie_register_event *event_reg; -+ bool power_on; -+ void *ipc_log; -+ void *ipc_log_long; -+ struct pci_bus *pci_bus; -+}; -+ -+extern int msm_pcie_enumerate(u32 rc_idx); -+extern int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options); -+extern void msm_pcie_disable(struct msm_pcie_dev_t *dev, u32 options); -+extern void msm_pcie_cfg_recover(struct msm_pcie_dev_t *dev, bool rc); -+extern void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev); -+extern int32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev); -+extern void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev); -+extern int msm_pcie_get_debug_mask(void); -+extern bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev, -+ bool check_sw_stts, bool check_ep); -+ -+extern void pcie_phy_init(struct msm_pcie_dev_t *dev); -+extern bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev); -+extern void pcie20_uni_phy_init(struct msm_pcie_dev_t *dev); -+extern bool pcie_phy_detect(struct msm_pcie_dev_t *dev); -+ -+#endif -diff --git a/arch/arm/mach-qcom/pcie_irq.c b/arch/arm/mach-qcom/pcie_irq.c -new file mode 100644 -index 0000000..ae21d05 ---- /dev/null -+++ b/arch/arm/mach-qcom/pcie_irq.c -@@ -0,0 +1,598 @@ -+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+/* -+ * MSM PCIe controller IRQ driver. -+ */ -+ -+#define pr_fmt(fmt) "%s: " fmt, __func__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "pcie.h" -+ -+/* Any address will do here, as it won't be dereferenced */ -+#define MSM_PCIE_MSI_PHY 0xa0000000 -+ -+#define PCIE20_MSI_CTRL_ADDR (0x820) -+#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824) -+#define PCIE20_MSI_CTRL_INTR_EN (0x828) -+#define PCIE20_MSI_CTRL_INTR_MASK (0x82C) -+#define PCIE20_MSI_CTRL_INTR_STATUS (0x830) -+ -+#define PCIE20_MSI_CTRL_MAX 8 -+ -+#define LINKDOWN_INIT_WAITING_US_MIN 995 -+#define LINKDOWN_INIT_WAITING_US_MAX 1005 -+#define LINKDOWN_WAITING_US_MIN 4900 -+#define LINKDOWN_WAITING_US_MAX 5100 -+#define LINKDOWN_WAITING_COUNT 200 -+ -+static void msm_pcie_notify_client(struct msm_pcie_dev_t *dev, -+ enum msm_pcie_event event) -+{ -+ if (dev->event_reg && dev->event_reg->callback && -+ (dev->event_reg->events & event)) { -+ struct msm_pcie_notify *notify = &dev->event_reg->notify; -+ notify->event = event; -+ notify->user = dev->event_reg->user; -+ PCIE_DBG(dev, "PCIe: callback RC%d for event %d.\n", -+ dev->rc_idx, event); -+ dev->event_reg->callback(notify); -+ -+ if ((dev->event_reg->options & MSM_PCIE_CONFIG_NO_RECOVERY) && -+ (event == MSM_PCIE_EVENT_LINKDOWN)) { -+ dev->user_suspend = true; -+ PCIE_DBG(dev, -+ "PCIe: Client of RC%d will recover the link later.\n", -+ dev->rc_idx); -+ return; -+ } -+ } else { -+ PCIE_DBG2(dev, -+ "PCIe: Client of RC%d does not have registration for event %d.\n", -+ dev->rc_idx, event); -+ } -+} -+ -+static void handle_wake_func(struct work_struct *work) -+{ -+ int ret; -+ struct msm_pcie_dev_t *dev = container_of(work, struct msm_pcie_dev_t, -+ handle_wake_work); -+ -+ PCIE_DBG(dev, "PCIe: Wake work for RC%d\n", dev->rc_idx); -+ -+ mutex_lock(&dev->recovery_lock); -+ -+ if (!dev->enumerated) { -+ PCIE_DBG(dev, -+ "PCIe: Start enumeration for RC%d upon the wake from endpoint.\n", -+ dev->rc_idx); -+ -+ ret = msm_pcie_enumerate(dev->rc_idx); -+ -+ if (ret) { -+ PCIE_ERR(dev, -+ "PCIe: failed to enable RC%d upon wake request from the device.\n", -+ dev->rc_idx); -+ goto out; -+ } -+ -+ if ((dev->link_status == MSM_PCIE_LINK_ENABLED) && -+ dev->event_reg && dev->event_reg->callback && -+ (dev->event_reg->events & MSM_PCIE_EVENT_LINKUP)) { -+ struct msm_pcie_notify *notify = -+ &dev->event_reg->notify; -+ notify->event = MSM_PCIE_EVENT_LINKUP; -+ notify->user = dev->event_reg->user; -+ PCIE_DBG(dev, -+ "PCIe: Linkup callback for RC%d after enumeration is successful in wake IRQ handling\n", -+ dev->rc_idx); -+ dev->event_reg->callback(notify); -+ } else { -+ PCIE_DBG(dev, -+ "PCIe: Client of RC%d does not have registration for linkup event.\n", -+ dev->rc_idx); -+ } -+ goto out; -+ } else { -+ PCIE_ERR(dev, -+ "PCIe: The enumeration for RC%d has already been done.\n", -+ dev->rc_idx); -+ goto out; -+ } -+ -+out: -+ mutex_unlock(&dev->recovery_lock); -+} -+ -+static irqreturn_t handle_wake_irq(int irq, void *data) -+{ -+ struct msm_pcie_dev_t *dev = data; -+ unsigned long irqsave_flags; -+ -+ spin_lock_irqsave(&dev->wakeup_lock, irqsave_flags); -+ -+ dev->wake_counter++; -+ PCIE_DBG(dev, "PCIe: No. %ld wake IRQ for RC%d\n", -+ dev->wake_counter, dev->rc_idx); -+ -+ PCIE_DBG2(dev, "PCIe WAKE is asserted by Endpoint of RC%d\n", -+ dev->rc_idx); -+ -+ if (!dev->enumerated) { -+ PCIE_DBG(dev, "Start enumeating RC%d\n", dev->rc_idx); -+ schedule_work(&dev->handle_wake_work); -+ } else { -+ PCIE_DBG2(dev, "Wake up RC%d\n", dev->rc_idx); -+ __pm_stay_awake(&dev->ws); -+ __pm_relax(&dev->ws); -+ msm_pcie_notify_client(dev, MSM_PCIE_EVENT_WAKEUP); -+ } -+ -+ spin_unlock_irqrestore(&dev->wakeup_lock, irqsave_flags); -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t handle_linkdown_irq(int irq, void *data) -+{ -+ struct msm_pcie_dev_t *dev = data; -+ unsigned long irqsave_flags; -+ -+ spin_lock_irqsave(&dev->linkdown_lock, irqsave_flags); -+ -+ dev->linkdown_counter++; -+ PCIE_DBG(dev, -+ "PCIe: No. %ld linkdown IRQ for RC%d.\n", -+ dev->linkdown_counter, dev->rc_idx); -+ -+ if (!dev->enumerated || dev->link_status != MSM_PCIE_LINK_ENABLED) { -+ PCIE_DBG(dev, -+ "PCIe:Linkdown IRQ for RC%d when the link is not enabled\n", -+ dev->rc_idx); -+ } else if (dev->suspending) { -+ PCIE_DBG(dev, -+ "PCIe:the link of RC%d is suspending.\n", -+ dev->rc_idx); -+ } else { -+ dev->link_status = MSM_PCIE_LINK_DISABLED; -+ dev->shadow_en = false; -+ /* assert PERST */ -+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, -+ dev->gpio[MSM_PCIE_GPIO_PERST].on); -+ PCIE_ERR(dev, "PCIe link is down for RC%d\n", dev->rc_idx); -+ msm_pcie_notify_client(dev, MSM_PCIE_EVENT_LINKDOWN); -+ } -+ -+ spin_unlock_irqrestore(&dev->linkdown_lock, irqsave_flags); -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t handle_msi_irq(int irq, void *data) -+{ -+ int i, j; -+ unsigned long val; -+ struct msm_pcie_dev_t *dev = data; -+ void __iomem *ctrl_status; -+ -+ PCIE_DBG(dev, "irq=%d\n", irq); -+ -+ /* check for set bits, clear it by setting that bit -+ and trigger corresponding irq */ -+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) { -+ ctrl_status = dev->dm_core + -+ PCIE20_MSI_CTRL_INTR_STATUS + (i * 12); -+ -+ val = readl_relaxed(ctrl_status); -+ while (val) { -+ j = find_first_bit(&val, 32); -+ writel_relaxed(BIT(j), ctrl_status); -+ /* ensure that interrupt is cleared (acked) */ -+ wmb(); -+ generic_handle_irq( -+ irq_find_mapping(dev->irq_domain, (j + (32*i))) -+ ); -+ val = readl_relaxed(ctrl_status); -+ } -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev) -+{ -+ int i; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ /* program MSI controller and enable all interrupts */ -+ writel_relaxed(MSM_PCIE_MSI_PHY, dev->dm_core + PCIE20_MSI_CTRL_ADDR); -+ writel_relaxed(0, dev->dm_core + PCIE20_MSI_CTRL_UPPER_ADDR); -+ -+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) -+ writel_relaxed(~0, dev->dm_core + -+ PCIE20_MSI_CTRL_INTR_EN + (i * 12)); -+ -+ /* ensure that hardware is configured before proceeding */ -+ wmb(); -+} -+ -+void msm_pcie_destroy_irq(unsigned int irq, struct msm_pcie_dev_t *pcie_dev) -+{ -+ int pos; -+ struct msm_pcie_dev_t *dev; -+ -+ if (pcie_dev) { -+ dev = pcie_dev; -+ } else { -+ dev = irq_get_chip_data(irq); -+ if (!dev) -+ return; -+ } -+ -+ if (dev->msi_gicm_addr) { -+ PCIE_DBG(dev, "destroy QGIC based irq %d\n", irq); -+ pos = irq - dev->msi_gicm_base; -+ } else { -+ PCIE_DBG(dev, "destroy default MSI irq %d\n", irq); -+ pos = irq - irq_find_mapping(dev->irq_domain, 0); -+ } -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ if (!dev->msi_gicm_addr) -+ irq_dispose_mapping(irq); -+ -+ PCIE_DBG(dev, "Before clear_bit pos:%d msi_irq_in_use:%ld\n", -+ pos, *dev->msi_irq_in_use); -+ clear_bit(pos, dev->msi_irq_in_use); -+ PCIE_DBG(dev, "After clear_bit pos:%d msi_irq_in_use:%ld\n", -+ pos, *dev->msi_irq_in_use); -+} -+ -+/* hookup to linux pci msi framework */ -+void msm_pcie_teardown_msi_irq(struct msi_controller *chip, unsigned int irq) -+{ -+ pr_debug("irq %d deallocated\n", irq); -+ msm_pcie_destroy_irq(irq, NULL); -+} -+ -+#if 0 -+void msm_pcie_teardown_msi_irqs(struct pci_dev *dev) -+{ -+ struct msi_desc *entry; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC:%d EP: vendor_id:0x%x device_id:0x%x\n", -+ pcie_dev->rc_idx, dev->vendor, dev->device); -+ -+ pcie_dev->use_msi = false; -+ -+ list_for_each_entry(entry, &dev->msi_list, list) { -+ int i, nvec; -+ if (entry->irq == 0) -+ continue; -+ nvec = 1 << entry->msi_attrib.multiple; -+ for (i = 0; i < nvec; i++) -+ msm_pcie_destroy_irq(entry->irq + i, pcie_dev); -+ } -+} -+#endif -+ -+static void msm_pcie_msi_nop(struct irq_data *d) -+{ -+ return; -+} -+ -+static struct irq_chip pcie_msi_chip = { -+ .name = "msm-pcie-msi", -+ .irq_ack = msm_pcie_msi_nop, -+ .irq_enable = unmask_msi_irq, -+ .irq_disable = mask_msi_irq, -+ .irq_mask = mask_msi_irq, -+ .irq_unmask = unmask_msi_irq, -+}; -+ -+static int msm_pcie_create_irq(struct msm_pcie_dev_t *dev) -+{ -+ int irq, pos; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+again: -+ pos = find_first_zero_bit(dev->msi_irq_in_use, PCIE_MSI_NR_IRQS); -+ -+ if (pos >= PCIE_MSI_NR_IRQS) -+ return -ENOSPC; -+ -+ PCIE_DBG(dev, "pos:%d msi_irq_in_use:%ld\n", pos, *dev->msi_irq_in_use); -+ -+ if (test_and_set_bit(pos, dev->msi_irq_in_use)) -+ goto again; -+ else -+ PCIE_DBG(dev, "test_and_set_bit is successful pos=%d\n", pos); -+ -+ irq = irq_create_mapping(dev->irq_domain, pos); -+ if (!irq) -+ return -EINVAL; -+ -+ return irq; -+} -+ -+static int arch_setup_msi_irq_default(struct pci_dev *pdev, -+ struct msi_desc *desc, int nvec) -+{ -+ int irq; -+ struct msi_msg msg; -+ struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev); -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ irq = msm_pcie_create_irq(dev); -+ -+ PCIE_DBG(dev, "IRQ %d is allocated.\n", irq); -+ -+ if (irq < 0) -+ return irq; -+ -+ PCIE_DBG(dev, "irq %d allocated\n", irq); -+ -+ irq_set_msi_desc(irq, desc); -+ -+ /* write msi vector and data */ -+ msg.address_hi = 0; -+ msg.address_lo = MSM_PCIE_MSI_PHY; -+ msg.data = irq - irq_find_mapping(dev->irq_domain, 0); -+ write_msi_msg(irq, &msg); -+ -+ return 0; -+} -+ -+static int msm_pcie_create_irq_qgic(struct msm_pcie_dev_t *dev) -+{ -+ int irq, pos; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+again: -+ pos = find_first_zero_bit(dev->msi_irq_in_use, PCIE_MSI_NR_IRQS); -+ -+ if (pos >= PCIE_MSI_NR_IRQS) -+ return -ENOSPC; -+ -+ PCIE_DBG(dev, "pos:%d msi_irq_in_use:%ld\n", pos, *dev->msi_irq_in_use); -+ -+ if (test_and_set_bit(pos, dev->msi_irq_in_use)) -+ goto again; -+ else -+ PCIE_DBG(dev, "test_and_set_bit is successful pos=%d\n", pos); -+ -+ irq = dev->msi_gicm_base + pos; -+ if (!irq) { -+ PCIE_ERR(dev, "PCIe: RC%d failed to create QGIC MSI IRQ.\n", -+ dev->rc_idx); -+ return -EINVAL; -+ } -+ -+ return irq; -+} -+ -+static int arch_setup_msi_irq_qgic(struct pci_dev *pdev, -+ struct msi_desc *desc, int nvec) -+{ -+ int irq, index, firstirq = 0; -+ struct msi_msg msg; -+ struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev); -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ for (index = 0; index < nvec; index++) { -+ irq = msm_pcie_create_irq_qgic(dev); -+ PCIE_DBG(dev, "irq %d is allocated\n", irq); -+ -+ if (irq < 0) -+ return irq; -+ -+ if (index == 0) -+ firstirq = irq; -+ -+ irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); -+ } -+ -+ /* write msi vector and data */ -+ irq_set_msi_desc(firstirq, desc); -+ msg.address_hi = 0; -+ msg.address_lo = dev->msi_gicm_addr; -+ msg.data = firstirq; -+ write_msi_msg(firstirq, &msg); -+ -+ return 0; -+} -+ -+int msm_pcie_setup_msi_irq(struct msi_controller *chip, struct pci_dev *pdev, -+ struct msi_desc *desc) -+{ -+ struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev); -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ if (dev->msi_gicm_addr) -+ return arch_setup_msi_irq_qgic(pdev, desc, 1); -+ else -+ return arch_setup_msi_irq_default(pdev, desc, 1); -+} -+ -+#if 0 -+static int msm_pcie_get_msi_multiple(int nvec) -+{ -+ int msi_multiple = 0; -+ -+ while (nvec) { -+ nvec = nvec >> 1; -+ msi_multiple++; -+ } -+ pr_debug("log2 number of MSI multiple:%d\n", -+ msi_multiple - 1); -+ -+ return msi_multiple - 1; -+} -+ -+int msm_pcie_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -+{ -+ struct msi_desc *entry; -+ int ret; -+ struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); -+ -+ if (type != PCI_CAP_ID_MSI || nvec > 32) -+ return -ENOSPC; -+ -+ PCIE_DBG(pcie_dev, "nvec = %d\n", nvec); -+ -+ list_for_each_entry(entry, &dev->msi_list, list) { -+ entry->msi_attrib.multiple = -+ msm_pcie_get_msi_multiple(nvec); -+ -+ if (pcie_dev->msi_gicm_addr) -+ ret = arch_setup_msi_irq_qgic(dev, entry, nvec); -+ else -+ ret = arch_setup_msi_irq_default(dev, entry, nvec); -+ -+ PCIE_DBG(pcie_dev, "ret from msi_irq: %d\n", ret); -+ -+ if (ret < 0) -+ return ret; -+ if (ret > 0) -+ return -ENOSPC; -+ } -+ -+ pcie_dev->use_msi = true; -+ -+ return 0; -+} -+#endif -+ -+static int msm_pcie_msi_map(struct irq_domain *domain, unsigned int irq, -+ irq_hw_number_t hwirq) -+{ -+ irq_set_chip_and_handler (irq, &pcie_msi_chip, handle_simple_irq); -+ irq_set_chip_data(irq, domain->host_data); -+ //set_irq_flags(irq, IRQF_VALID); -+ return 0; -+} -+ -+static const struct irq_domain_ops msm_pcie_msi_ops = { -+ .map = msm_pcie_msi_map, -+}; -+ -+int32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev) -+{ -+ int rc; -+ int msi_start = 0; -+ struct device *pdev = &dev->pdev->dev; -+ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ dev->msi_chip = kzalloc(sizeof(struct msi_controller), GFP_KERNEL); -+ if (!dev->msi_chip) -+ return -ENOMEM; -+ -+ dev->msi_chip->setup_irq = msm_pcie_setup_msi_irq; -+ dev->msi_chip->teardown_irq = msm_pcie_teardown_msi_irq; -+ -+ if (dev->ep_wakeirq) -+ wakeup_source_init(&dev->ws, "pcie_wakeup_source"); -+ -+ /* register handler for linkdown interrupt */ -+ rc = devm_request_irq(pdev, -+ dev->irq[MSM_PCIE_INT_LINK_DOWN].num, handle_linkdown_irq, -+ IRQF_TRIGGER_RISING, dev->irq[MSM_PCIE_INT_LINK_DOWN].name, -+ dev); -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: Unable to request linkdown interrupt:%d\n", -+ dev->irq[MSM_PCIE_INT_LINK_DOWN].num); -+ return rc; -+ } -+ -+ /* register handler for physical MSI interrupt line */ -+ rc = devm_request_irq(pdev, -+ dev->irq[MSM_PCIE_INT_MSI].num, handle_msi_irq, -+ IRQF_TRIGGER_RISING, dev->irq[MSM_PCIE_INT_MSI].name, dev); -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: RC%d: Unable to request MSI interrupt\n", -+ dev->rc_idx); -+ return rc; -+ } -+ -+ if (dev->ep_wakeirq) { -+ /* register handler for PCIE_WAKE_N interrupt line */ -+ rc = devm_request_irq(pdev, -+ dev->wake_n, handle_wake_irq, IRQF_TRIGGER_FALLING, -+ "msm_pcie_wake", dev); -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: RC%d: Unable to request wake interrupt\n", -+ dev->rc_idx); -+ return rc; -+ } -+ -+ INIT_WORK(&dev->handle_wake_work, handle_wake_func); -+ -+ rc = enable_irq_wake(dev->wake_n); -+ if (rc) { -+ PCIE_ERR(dev, "PCIe: RC%d: Unable to enable wake interrupt\n", -+ dev->rc_idx); -+ return rc; -+ } -+ } -+ -+ /* Create a virtual domain of interrupts */ -+ if (!dev->msi_gicm_addr) { -+ dev->irq_domain = irq_domain_add_linear(dev->pdev->dev.of_node, -+ PCIE_MSI_NR_IRQS, &msm_pcie_msi_ops, dev); -+ -+ if (!dev->irq_domain) { -+ PCIE_ERR(dev, -+ "PCIe: RC%d: Unable to initialize irq domain\n", -+ dev->rc_idx); -+ disable_irq(dev->wake_n); -+ return PTR_ERR(dev->irq_domain); -+ } -+ -+ msi_start = irq_create_mapping(dev->irq_domain, 0); -+ } -+ -+ return 0; -+} -+ -+void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev) -+{ -+ PCIE_DBG(dev, "RC%d\n", dev->rc_idx); -+ -+ kfree(dev->msi_chip); -+ -+ if (dev->ep_wakeirq) { -+ wakeup_source_trash(&dev->ws); -+ disable_irq(dev->wake_n); -+ } -+} -diff --git a/arch/arm/mach-qcom/pcie_phy.c b/arch/arm/mach-qcom/pcie_phy.c -new file mode 100644 -index 0000000..32f3c79 ---- /dev/null -+++ b/arch/arm/mach-qcom/pcie_phy.c -@@ -0,0 +1,403 @@ -+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+/* -+ * MSM PCIe PHY driver. -+ */ -+ -+#include -+#include -+#include "pcie.h" -+#include "pcie_phy.h" -+ -+#define MDIO_CTRL_0_REG 0x40 -+#define MDIO_CTRL_1_REG 0x44 -+#define MDIO_CTRL_2_REG 0x48 -+#define MDIO_CTRL_3_REG 0x4C -+#define MDIO_CTRL_4_REG 0x50 -+ -+#define MDIO_PCIE_PHY_ID (0x5 << 13) -+#define MDC_MODE (0x1 << 12) -+#define MDIO_CLAUSE_22 (0x0 << 8) -+#define MDIO_CLAUSE_45 (0x1 << 8) -+#define MDIO_PCIE_CLK_DIV (0xF) -+#define MDIO_MMD_ID (0x1) -+ -+#define MDIO_ACCESS_BUSY (0x1 << 16) -+#define MDIO_ACCESS_START (0x1 << 8) -+#define MDIO_TIMEOUT_STATIC 1000 -+ -+#define MDIO_ACCESS_22_WRITE (0x1) -+#define MDIO_ACCESS_22_READ (0x0) -+#define MDIO_ACCESS_45_WRITE (0x2) -+#define MDIO_ACCESS_45_READ (0x1) -+#define MDIO_ACCESS_45_READ_ADDR (0x0) -+ -+static inline void write_phy(void *base, u32 offset, u32 value) -+{ -+ writel_relaxed(value, base + offset); -+ wmb(); -+} -+ -+#ifndef CONFIG_ARCH_MDM9630 -+static inline void pcie20_phy_init_default(struct msm_pcie_dev_t *dev) -+{ -+ -+ PCIE_DBG(dev, "RC%d: Initializing 28nm QMP phy - 19.2MHz\n", -+ dev->rc_idx); -+ -+ write_phy(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x03); -+ write_phy(dev->phy, QSERDES_COM_SYSCLK_EN_SEL, 0x08); -+ write_phy(dev->phy, QSERDES_COM_DEC_START1, 0x82); -+ write_phy(dev->phy, QSERDES_COM_DEC_START2, 0x03); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START1, 0xd5); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START2, 0xaa); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START3, 0x13); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP_EN, 0x01); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP1, 0x2b); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP2, 0x68); -+ write_phy(dev->phy, QSERDES_COM_PLL_CRCTRL, 0xff); -+ write_phy(dev->phy, QSERDES_COM_PLL_CP_SETI, 0x3f); -+ write_phy(dev->phy, QSERDES_COM_PLL_IP_SETP, 0x07); -+ write_phy(dev->phy, QSERDES_COM_PLL_CP_SETP, 0x03); -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL, 0xf3); -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL2, 0x6b); -+ write_phy(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x10); -+ write_phy(dev->phy, QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE, 0x87); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN12, 0x54); -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG1, 0xa3); -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG2, 0xcb); -+ write_phy(dev->phy, QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10); -+ write_phy(dev->phy, PCIE_PHY_ENDPOINT_REFCLK_DRIVE, 0x10); -+ write_phy(dev->phy, PCIE_PHY_SW_RESET, 0x00); -+ write_phy(dev->phy, PCIE_PHY_START, 0x03); -+} -+#endif -+ -+#ifdef CONFIG_ARCH_MDM9630 -+void pcie_phy_init(struct msm_pcie_dev_t *dev) -+{ -+ -+ PCIE_DBG(dev, "RC%d: Initializing 28nm QMP phy - 19.2MHz\n", -+ dev->rc_idx); -+ -+ write_phy(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x03); -+ -+ write_phy(dev->phy, QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08); -+ write_phy(dev->phy, QSERDES_COM_DEC_START1, 0x82); -+ write_phy(dev->phy, QSERDES_COM_DEC_START2, 0x03); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START1, 0xD5); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START2, 0xAA); -+ write_phy(dev->phy, QSERDES_COM_DIV_FRAC_START3, 0x4D); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP_EN, 0x01); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP1, 0x2B); -+ write_phy(dev->phy, QSERDES_COM_PLLLOCK_CMP2, 0x68); -+ write_phy(dev->phy, QSERDES_COM_PLL_CRCTRL, 0x7C); -+ write_phy(dev->phy, QSERDES_COM_PLL_CP_SETI, 0x02); -+ write_phy(dev->phy, QSERDES_COM_PLL_IP_SETP, 0x1F); -+ write_phy(dev->phy, QSERDES_COM_PLL_CP_SETP, 0x0F); -+ write_phy(dev->phy, QSERDES_COM_PLL_IP_SETI, 0x01); -+ write_phy(dev->phy, QSERDES_COM_IE_TRIM, 0x0F); -+ write_phy(dev->phy, QSERDES_COM_IP_TRIM, 0x0F); -+ write_phy(dev->phy, QSERDES_COM_PLL_CNTRL, 0x46); -+ -+ /* CDR Settings */ -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL1, 0xF3); -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL_HALF, 0x2B); -+ -+ write_phy(dev->phy, QSERDES_COM_PLL_VCOTAIL_EN, 0xE1); -+ -+ /* Calibration Settings */ -+ write_phy(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x90); -+ write_phy(dev->phy, QSERDES_COM_RESETSM_CNTRL2, 0x7); -+ -+ /* Additional writes */ -+ write_phy(dev->phy, QSERDES_COM_RES_CODE_START_SEG1, 0x20); -+ write_phy(dev->phy, QSERDES_COM_RES_CODE_CAL_CSR, 0x77); -+ write_phy(dev->phy, QSERDES_COM_RES_TRIM_CONTROL, 0x15); -+ write_phy(dev->phy, QSERDES_TX_RCV_DETECT_LVL, 0x03); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN1_MSB, 0x1F); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN2_MSB, 0x00); -+ write_phy(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1A); -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x80); -+ write_phy(dev->phy, QSERDES_RX_SIGDET_ENABLES, 0x40); -+ write_phy(dev->phy, QSERDES_RX_SIGDET_CNTRL, 0x70); -+ write_phy(dev->phy, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x06); -+ write_phy(dev->phy, QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10); -+ write_phy(dev->phy, PCIE_PHY_ENDPOINT_REFCLK_DRIVE, 0x10); -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG1, 0x23); -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG2, 0xCB); -+ write_phy(dev->phy, QSERDES_RX_RX_RCVR_IQ_EN, 0x31); -+ -+ write_phy(dev->phy, PCIE_PHY_SW_RESET, 0x00); -+ write_phy(dev->phy, PCIE_PHY_START, 0x03); -+} -+#elif defined(CONFIG_ARCH_FSM9900) -+void pcie_phy_init(struct msm_pcie_dev_t *dev) -+{ -+ if (dev->ext_ref_clk == false) { -+ pcie20_phy_init_default(dev); -+ return; -+ } -+ -+ PCIE_DBG(dev, "RC%d: Initializing 28nm ATE phy - 100MHz\n", -+ dev->rc_idx); -+ -+ /* 1 */ -+ write_phy(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x01); -+ /* 2 */ -+ write_phy(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x3e); -+ /* 3 */ -+ write_phy(dev->phy, QSERDES_COM_PLL_CP_SETI, 0x0f); -+ /* 4 */ -+ write_phy(dev->phy, QSERDES_COM_PLL_IP_SETP, 0x23); -+ /* 5 */ -+ write_phy(dev->phy, QSERDES_COM_PLL_IP_SETI, 0x3f); -+ /* 6 */ -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL, 0xf3); -+ /* 7 */ -+ write_phy(dev->phy, QSERDES_RX_CDR_CONTROL2, 0x6b); -+ /* 8 */ -+ write_phy(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x10); -+ /* 9 */ -+ write_phy(dev->phy, QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE, 0x87); -+ /* 10 */ -+ write_phy(dev->phy, QSERDES_RX_RX_EQ_GAIN12, 0x54); -+ /* 11 */ -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG1, 0xa3); -+ /* 12 */ -+ write_phy(dev->phy, PCIE_PHY_POWER_STATE_CONFIG2, 0x1b); -+ /* 13 */ -+ write_phy(dev->phy, PCIE_PHY_SW_RESET, 0x00); -+ /* 14 */ -+ write_phy(dev->phy, PCIE_PHY_START, 0x03); -+} -+#else -+void pcie_phy_init(struct msm_pcie_dev_t *dev) -+{ -+ if (dev->is_emulation) -+ pcie20_uni_phy_init(dev); -+ else -+ pcie20_phy_init_default(dev); -+} -+ -+#endif -+ -+bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev) -+{ -+ if (!dev->is_emulation && -+ readl_relaxed(dev->phy + PCIE_PHY_PCS_STATUS) & BIT(6)) -+ return false; -+ else -+ return true; -+} -+/** -+ * Write register -+ * -+ * @base - PHY base virtual address. -+ * @offset - register offset. -+ */ -+static u32 qca_uni_phy_read(void __iomem *base, u32 offset) -+{ -+ u32 value; -+ value = readl_relaxed(base + offset); -+ return value; -+} -+ -+/** -+ * Write register -+ * @base - PHY base virtual address. -+ * @offset - register offset. -+ * @val - value to write. -+ */ -+static void qca_uni_phy_write(void __iomem *base, u32 offset, u32 val) -+{ -+ writel(val, base + offset); -+ udelay(100); -+} -+ -+static int mdio_wait(void __iomem *base) -+{ -+ unsigned int mdio_access; -+ unsigned int timeout = MDIO_TIMEOUT_STATIC; -+ -+ do { -+ mdio_access = qca_uni_phy_read(base, MDIO_CTRL_4_REG); -+ if (!timeout--) -+ return -EFAULT; -+ } while (mdio_access & MDIO_ACCESS_BUSY); -+ -+ return 0; -+} -+ -+static int mdio_mii_read(void __iomem *base, unsigned char regAddr, -+ unsigned short *data) -+{ -+ unsigned short mdio_ctl_0 = (MDIO_PCIE_PHY_ID | MDC_MODE | -+ MDIO_CLAUSE_22 | MDIO_PCIE_CLK_DIV); -+ unsigned int regVal; -+ -+ qca_uni_phy_write(base, MDIO_CTRL_0_REG, mdio_ctl_0); -+ qca_uni_phy_write(base, MDIO_CTRL_1_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_22_READ); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_22_READ | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ regVal = qca_uni_phy_read(base, MDIO_CTRL_3_REG); -+ *data = (unsigned short)regVal; -+ return 0; -+} -+ -+static int mdio_mii_write(void __iomem *base, unsigned char regAddr, -+ unsigned short data) -+{ -+ unsigned short mdio_ctl_0 = (MDIO_PCIE_PHY_ID | MDC_MODE | -+ MDIO_CLAUSE_22 | MDIO_PCIE_CLK_DIV); -+ -+ qca_uni_phy_write(base, MDIO_CTRL_0_REG, mdio_ctl_0); -+ qca_uni_phy_write(base, MDIO_CTRL_1_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, data); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_22_WRITE); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_22_WRITE | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ return 0; -+} -+ -+static int mdio_mmd_read(void __iomem *base, unsigned short regAddr, -+ unsigned short *data) -+{ -+ unsigned short mdio_ctl_0 = (MDIO_PCIE_PHY_ID | MDC_MODE | -+ MDIO_CLAUSE_45 | MDIO_PCIE_CLK_DIV); -+ unsigned int regVal; -+ -+ qca_uni_phy_write(base, MDIO_CTRL_0_REG, mdio_ctl_0); -+ qca_uni_phy_write(base, MDIO_CTRL_1_REG, MDIO_MMD_ID); -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ_ADDR); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ_ADDR | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ qca_uni_phy_write(base, MDIO_CTRL_1_REG, MDIO_MMD_ID); -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_WRITE); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_WRITE | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ regVal = qca_uni_phy_read(base, MDIO_CTRL_3_REG); -+ *data = (unsigned short)regVal; -+ -+ return 0; -+} -+ -+static int mdio_mmd_write(void __iomem *base, unsigned short regAddr, -+ unsigned short data) -+{ -+ -+ unsigned short mdio_ctl_0 = (MDIO_PCIE_PHY_ID | MDC_MODE | -+ MDIO_CLAUSE_45 | MDIO_PCIE_CLK_DIV); -+ -+ qca_uni_phy_write(base, MDIO_CTRL_0_REG, mdio_ctl_0); -+ qca_uni_phy_write(base, MDIO_CTRL_1_REG, MDIO_MMD_ID); -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ_ADDR); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ_ADDR | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, data); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_READ | -+ MDIO_ACCESS_START); -+ -+ qca_uni_phy_write(base, MDIO_CTRL_2_REG, regAddr); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_WRITE); -+ qca_uni_phy_write(base, MDIO_CTRL_4_REG, MDIO_ACCESS_45_WRITE | -+ MDIO_ACCESS_START); -+ -+ /* wait for access busy to be cleared */ -+ if (mdio_wait(base)) { -+ pr_err("%s MDIO Access Busy Timeout %x\n", __func__, regAddr); -+ return -EFAULT; -+ } -+ -+ return 0; -+} -+ -+void pcie20_uni_phy_init(struct msm_pcie_dev_t *dev) -+{ -+ unsigned short data; -+ -+ mdio_mii_write(dev->phy, 0x1, 0x801c); -+ mdio_mii_write(dev->phy, 0xb, 0x300d); -+ -+ mdio_mmd_write(dev->phy, 0x2d, 0x681a); -+ mdio_mmd_write(dev->phy, 0x7d, 0x8); -+ mdio_mmd_write(dev->phy, 0x7f, 0x5ed5); -+ mdio_mmd_write(dev->phy, 0x87, 0xaa0a); -+ mdio_mmd_write(dev->phy, 0x4, 0x0802); -+ mdio_mmd_write(dev->phy, 0x8, 0x0280); -+ mdio_mmd_write(dev->phy, 0x9, 0x8854); -+ mdio_mmd_write(dev->phy, 0xa, 0x2815); -+ mdio_mmd_write(dev->phy, 0xb, 0x0120); -+ mdio_mmd_write(dev->phy, 0xc, 0x0480); -+ mdio_mmd_write(dev->phy, 0x13, 0x8000); -+ -+ mdio_mmd_read(dev->phy, 0x7e, &data); -+ -+ mdio_mii_read(dev->phy, 0x7, &data); -+} -+ -+bool pcie_phy_detect(struct msm_pcie_dev_t *dev) -+{ -+ unsigned short data; -+ -+ mdio_mii_read(dev->phy, 0x0, &data); -+ -+ if (data == 0x7f) { -+ pr_info("PCIe UNI PHY detected\n"); -+ return true; -+ } else { -+ return false; -+ } -+} -diff --git a/arch/arm/mach-qcom/pcie_phy.h b/arch/arm/mach-qcom/pcie_phy.h -new file mode 100644 -index 0000000..99297c3 ---- /dev/null -+++ b/arch/arm/mach-qcom/pcie_phy.h -@@ -0,0 +1,545 @@ -+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __ARCH_ARM_MACH_MSM_PCIE_PHY_H -+#define __ARCH_ARM_MACH_MSM_PCIE_PHY_H -+ -+#ifdef CONFIG_ARCH_MDM9630 -+#define QSERDES_COM_SYS_CLK_CTRL 0x000 -+#define QSERDES_COM_PLL_VCOTAIL_EN 0x004 -+#define QSERDES_COM_CMN_MODE 0x008 -+#define QSERDES_COM_IE_TRIM 0x00C -+#define QSERDES_COM_IP_TRIM 0x010 -+#define QSERDES_COM_PLL_CNTRL 0x014 -+#define QSERDES_COM_PLL_PHSEL_CONTROL 0x018 -+#define QSERDES_COM_IPTAT_TRIM_VCCA_TX_SEL 0x01C -+#define QSERDES_COM_PLL_PHSEL_DC 0x020 -+#define QSERDES_COM_PLL_IP_SETI 0x024 -+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x028 -+#define QSERDES_COM_PLL_BKG_KVCO_CAL_EN 0x02C -+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x030 -+#define QSERDES_COM_PLL_CP_SETI 0x034 -+#define QSERDES_COM_PLL_IP_SETP 0x038 -+#define QSERDES_COM_PLL_CP_SETP 0x03C -+#define QSERDES_COM_ATB_SEL1 0x040 -+#define QSERDES_COM_ATB_SEL2 0x044 -+#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND 0x048 -+#define QSERDES_COM_RESETSM_CNTRL 0x04C -+#define QSERDES_COM_RESETSM_CNTRL2 0x050 -+#define QSERDES_COM_RESETSM_CNTRL3 0x054 -+#define QSERDES_COM_DIV_REF1 0x058 -+#define QSERDES_COM_DIV_REF2 0x05C -+#define QSERDES_COM_KVCO_COUNT1 0x060 -+#define QSERDES_COM_KVCO_COUNT2 0x064 -+#define QSERDES_COM_KVCO_CAL_CNTRL 0x068 -+#define QSERDES_COM_KVCO_CODE 0x06C -+#define QSERDES_COM_VREF_CFG1 0x070 -+#define QSERDES_COM_VREF_CFG2 0x074 -+#define QSERDES_COM_VREF_CFG3 0x078 -+#define QSERDES_COM_VREF_CFG4 0x07C -+#define QSERDES_COM_VREF_CFG5 0x080 -+#define QSERDES_COM_VREF_CFG6 0x084 -+#define QSERDES_COM_PLLLOCK_CMP1 0x088 -+#define QSERDES_COM_PLLLOCK_CMP2 0x08C -+#define QSERDES_COM_PLLLOCK_CMP3 0x090 -+#define QSERDES_COM_PLLLOCK_CMP_EN 0x094 -+#define QSERDES_COM_BGTC 0x098 -+#define QSERDES_COM_PLL_TEST_UPDN 0x09C -+#define QSERDES_COM_PLL_VCO_TUNE 0x0A0 -+#define QSERDES_COM_DEC_START1 0x0A4 -+#define QSERDES_COM_PLL_AMP_OS 0x0A8 -+#define QSERDES_COM_SSC_EN_CENTER 0x0AC -+#define QSERDES_COM_SSC_ADJ_PER1 0x0B0 -+#define QSERDES_COM_SSC_ADJ_PER2 0x0B4 -+#define QSERDES_COM_SSC_PER1 0x0B8 -+#define QSERDES_COM_SSC_PER2 0x0BC -+#define QSERDES_COM_SSC_STEP_SIZE1 0x0C0 -+#define QSERDES_COM_SSC_STEP_SIZE2 0x0C4 -+#define QSERDES_COM_RES_CODE_UP 0x0C8 -+#define QSERDES_COM_RES_CODE_DN 0x0CC -+#define QSERDES_COM_RES_CODE_UP_OFFSET 0x0D0 -+#define QSERDES_COM_RES_CODE_DN_OFFSET 0x0D4 -+#define QSERDES_COM_RES_CODE_START_SEG1 0x0D8 -+#define QSERDES_COM_RES_CODE_START_SEG2 0x0DC -+#define QSERDES_COM_RES_CODE_CAL_CSR 0x0E0 -+#define QSERDES_COM_RES_CODE 0x0E4 -+#define QSERDES_COM_RES_TRIM_CONTROL 0x0E8 -+#define QSERDES_COM_RES_TRIM_CONTROL2 0x0EC -+#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE 0x0F0 -+#define QSERDES_COM_FAUX_EN 0x0F4 -+#define QSERDES_COM_DIV_FRAC_START1 0x0F8 -+#define QSERDES_COM_DIV_FRAC_START2 0x0FC -+#define QSERDES_COM_DIV_FRAC_START3 0x100 -+#define QSERDES_COM_DEC_START2 0x104 -+#define QSERDES_COM_PLL_RXTXEPCLK_EN 0x108 -+#define QSERDES_COM_PLL_CRCTRL 0x10C -+#define QSERDES_COM_PLL_CLKEPDIV 0x110 -+#define QSERDES_COM_PLL_FREQUPDATE 0x114 -+#define QSERDES_COM_PLL_BKGCAL_TRIM_UP 0x118 -+#define QSERDES_COM_PLL_BKGCAL_TRIM_DN 0x11C -+#define QSERDES_COM_PLL_BKGCAL_TRIM_MUX 0x120 -+#define QSERDES_COM_PLL_BKGCAL_VREF_CFG 0x124 -+#define QSERDES_COM_PLL_BKGCAL_DIV_REF1 0x128 -+#define QSERDES_COM_PLL_BKGCAL_DIV_REF2 0x12C -+#define QSERDES_COM_MUXADDR 0x130 -+#define QSERDES_COM_LOW_POWER_RO_CONTROL 0x134 -+#define QSERDES_COM_POST_DIVIDER_CONTROL 0x138 -+#define QSERDES_COM_HR_OCLK2_DIVIDER 0x13C -+#define QSERDES_COM_HR_OCLK3_DIVIDER 0x140 -+#define QSERDES_COM_PLL_VCO_HIGH 0x144 -+#define QSERDES_COM_RESET_SM 0x148 -+#define QSERDES_COM_MUXVAL 0x14C -+ -+#define QSERDES_RX_CDR_CONTROL1 0x400 -+#define QSERDES_RX_CDR_CONTROL2 0x404 -+#define QSERDES_RX_CDR_CONTROL_HALF 0x408 -+#define QSERDES_RX_CDR_CONTROL_QUARTER 0x40C -+#define QSERDES_RX_CDR_CONTROL_EIGHTH 0x410 -+#define QSERDES_RX_UCDR_FO_GAIN 0x414 -+#define QSERDES_RX_UCDR_SO_GAIN 0x418 -+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x41C -+#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x420 -+#define QSERDES_RX_AUX_CONTROL 0x424 -+#define QSERDES_RX_AUX_DATA_TCOARSE 0x428 -+#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x42C -+#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x430 -+#define QSERDES_RX_RCLK_AUXDATA_SEL 0x434 -+#define QSERDES_RX_AC_JTAG_ENABLE 0x438 -+#define QSERDES_RX_AC_JTAG_INITP 0x43C -+#define QSERDES_RX_AC_JTAG_INITN 0x440 -+#define QSERDES_RX_AC_JTAG_LVL 0x444 -+#define QSERDES_RX_AC_JTAG_MODE 0x448 -+#define QSERDES_RX_AC_JTAG_RESET 0x44C -+#define QSERDES_RX_RX_RCVR_IQ_EN 0x450 -+#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x454 -+#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x458 -+#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x45C -+#define QSERDES_RX_RX_IDAC_EN 0x460 -+#define QSERDES_RX_RX_IDAC_CTRL0 0x464 -+#define QSERDES_RX_RX_IDAC_CTRL1 0x468 -+#define QSERDES_RX_RX_EOM_EN 0x46C -+#define QSERDES_RX_RX_EOM_CTRL0 0x470 -+#define QSERDES_RX_RX_EOM_CTRL1 0x474 -+#define QSERDES_RX_RX_EOM_CTRL2 0x478 -+#define QSERDES_RX_RX_EOM_CTRL3 0x47C -+#define QSERDES_RX_RX_EOM_CTRL4 0x480 -+#define QSERDES_RX_RX_EOM_CTRL5 0x484 -+#define QSERDES_RX_RX_EOM_CTRL6 0x488 -+#define QSERDES_RX_RX_EOM_CTRL7 0x48C -+#define QSERDES_RX_RX_EOM_CTRL8 0x490 -+#define QSERDES_RX_RX_EOM_CTRL9 0x494 -+#define QSERDES_RX_RX_EOM_CTRL10 0x498 -+#define QSERDES_RX_RX_EOM_CTRL11 0x49C -+#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x4A0 -+#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x4A4 -+#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x4A8 -+#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x4AC -+#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x4B0 -+#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x4B4 -+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x4B8 -+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x4BC -+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x4C0 -+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x4C4 -+#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x4C8 -+#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION_2 0x4CC -+#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x4D0 -+#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x4D4 -+#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x4D8 -+#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x4DC -+#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x4E0 -+#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x4E4 -+#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x4E8 -+#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x4EC -+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x4F0 -+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x4F4 -+#define QSERDES_RX_SIGDET_ENABLES 0x4F8 -+#define QSERDES_RX_SIGDET_ENABLES_2 0x4FC -+#define QSERDES_RX_SIGDET_CNTRL 0x500 -+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x504 -+#define QSERDES_RX_SIGDET_TIMER_LIMIT 0x508 -+#define QSERDES_RX_RX_BAND 0x50C -+#define QSERDES_RX_CDR_FREEZE_UP_DN 0x510 -+#define QSERDES_RX_RX_INTERFACE_MODE 0x514 -+#define QSERDES_RX_JITTER_GEN_MODE 0x518 -+#define QSERDES_RX_BUJ_AMP 0x51C -+#define QSERDES_RX_SJ_AMP1 0x520 -+#define QSERDES_RX_SJ_AMP2 0x524 -+#define QSERDES_RX_SJ_PER1 0x528 -+#define QSERDES_RX_SJ_PER2 0x52C -+#define QSERDES_RX_BUJ_STEP_FREQ1 0x530 -+#define QSERDES_RX_BUJ_STEP_FREQ2 0x534 -+#define QSERDES_RX_PPM_OFFSET1 0x538 -+#define QSERDES_RX_PPM_OFFSET2 0x53C -+#define QSERDES_RX_SIGN_PPM_PERIOD1 0x540 -+#define QSERDES_RX_SIGN_PPM_PERIOD2 0x544 -+#define QSERDES_RX_SSC_CTRL 0x548 -+#define QSERDES_RX_SSC_COUNT1 0x54C -+#define QSERDES_RX_SSC_COUNT2 0x550 -+#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x554 -+#define QSERDES_RX_PI_CTRL1 0x558 -+#define QSERDES_RX_PI_CTRL2 0x55C -+#define QSERDES_RX_PI_QUAD 0x560 -+#define QSERDES_RX_IDATA1 0x564 -+#define QSERDES_RX_IDATA2 0x568 -+#define QSERDES_RX_AUX_DATA1 0x56C -+#define QSERDES_RX_AUX_DATA2 0x570 -+#define QSERDES_RX_AC_JTAG_OUTP 0x574 -+#define QSERDES_RX_AC_JTAG_OUTN 0x578 -+#define QSERDES_RX_RX_SIGDET 0x57C -+#define QSERDES_RX_RX_VDCOFF 0x580 -+#define QSERDES_RX_IDAC_CAL_ON 0x584 -+#define QSERDES_RX_IDAC_STATUS_I 0x588 -+#define QSERDES_RX_IDAC_STATUS_Q 0x58C -+#define QSERDES_RX_IDAC_STATUS_A 0x590 -+#define QSERDES_RX_CALST_STATUS_I 0x594 -+#define QSERDES_RX_CALST_STATUS_Q 0x598 -+#define QSERDES_RX_CALST_STATUS_A 0x59C -+#define QSERDES_RX_EOM_STATUS0 0x5A0 -+#define QSERDES_RX_EOM_STATUS1 0x5A4 -+#define QSERDES_RX_EOM_STATUS2 0x5A8 -+#define QSERDES_RX_EOM_STATUS3 0x5AC -+#define QSERDES_RX_EOM_STATUS4 0x5B0 -+#define QSERDES_RX_EOM_STATUS5 0x5B4 -+#define QSERDES_RX_EOM_STATUS6 0x5B8 -+#define QSERDES_RX_EOM_STATUS7 0x5BC -+#define QSERDES_RX_EOM_STATUS8 0x5C0 -+#define QSERDES_RX_EOM_STATUS9 0x5C4 -+#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x5C8 -+#define QSERDES_RX_READ_EQCODE 0x5CC -+#define QSERDES_RX_READ_OFFSETCODE 0x5D0 -+ -+#define QSERDES_TX_BIST_MODE_LANENO 0x200 -+#define QSERDES_TX_CLKBUF_ENABLE 0x204 -+#define QSERDES_TX_TX_EMP_POST1_LVL 0x208 -+#define QSERDES_TX_TX_DRV_LVL 0x20C -+#define QSERDES_TX_RESET_TSYNC_EN 0x210 -+#define QSERDES_TX_LPB_EN 0x214 -+#define QSERDES_TX_RES_CODE_UP 0x218 -+#define QSERDES_TX_RES_CODE_DN 0x21C -+#define QSERDES_TX_PERL_LENGTH1 0x220 -+#define QSERDES_TX_PERL_LENGTH2 0x224 -+#define QSERDES_TX_SERDES_BYP_EN_OUT 0x228 -+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x22C -+#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x230 -+#define QSERDES_TX_BIST_PATTERN1 0x234 -+#define QSERDES_TX_BIST_PATTERN2 0x238 -+#define QSERDES_TX_BIST_PATTERN3 0x23C -+#define QSERDES_TX_BIST_PATTERN4 0x240 -+#define QSERDES_TX_BIST_PATTERN5 0x244 -+#define QSERDES_TX_BIST_PATTERN6 0x248 -+#define QSERDES_TX_BIST_PATTERN7 0x24C -+#define QSERDES_TX_BIST_PATTERN8 0x250 -+#define QSERDES_TX_LANE_MODE 0x254 -+#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x258 -+#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x25C -+#define QSERDES_TX_ATB_SEL1 0x260 -+#define QSERDES_TX_ATB_SEL2 0x264 -+#define QSERDES_TX_RCV_DETECT_LVL 0x268 -+#define QSERDES_TX_PRBS_SEED1 0x26C -+#define QSERDES_TX_PRBS_SEED2 0x270 -+#define QSERDES_TX_PRBS_SEED3 0x274 -+#define QSERDES_TX_PRBS_SEED4 0x278 -+#define QSERDES_TX_RESET_GEN 0x27C -+#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x280 -+#define QSERDES_TX_TX_INTERFACE_MODE 0x284 -+#define QSERDES_TX_PWM_CTRL 0x288 -+#define QSERDES_TX_PWM_DATA 0x28C -+#define QSERDES_TX_PWM_ENC_DIV_CTRL 0x290 -+#define QSERDES_TX_VMODE_CTRL1 0x294 -+#define QSERDES_TX_VMODE_CTRL2 0x298 -+#define QSERDES_TX_VMODE_CTRL3 0x29C -+#define QSERDES_TX_VMODE_CTRL4 0x2A0 -+#define QSERDES_TX_VMODE_CTRL5 0x2A4 -+#define QSERDES_TX_VMODE_CTRL6 0x2A8 -+#define QSERDES_TX_VMODE_CTRL7 0x2AC -+#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x2B0 -+#define QSERDES_TX_BIST_STATUS 0x2B4 -+#define QSERDES_TX_BIST_ERROR_COUNT1 0x2B8 -+#define QSERDES_TX_BIST_ERROR_COUNT2 0x2BC -+#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x2C0 -+#define QSERDES_TX_PWM_DEC_STATUS 0x2C4 -+ -+#define PCIE_PHY_SW_RESET 0x600 -+#define PCIE_PHY_POWER_DOWN_CONTROL 0x604 -+#define PCIE_PHY_START 0x608 -+#define PCIE_PHY_TXMGN_V1_V0 0x60C -+#define PCIE_PHY_TXMGN_V3_V2 0x610 -+#define PCIE_PHY_TXMGN_LS_V4 0x614 -+#define PCIE_PHY_TXDEEMPH_M6DB_V0 0x618 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V0 0x61C -+#define PCIE_PHY_TXDEEMPH_M6DB_V1 0x620 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V1 0x624 -+#define PCIE_PHY_TXDEEMPH_M6DB_V2 0x628 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V2 0x62C -+#define PCIE_PHY_TXDEEMPH_M6DB_V3 0x630 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V3 0x634 -+#define PCIE_PHY_TXDEEMPH_M6DB_V4 0x638 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V4 0x63C -+#define PCIE_PHY_TXDEEMPH_M6DB_LS 0x640 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_LS 0x644 -+#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE 0x648 -+#define PCIE_PHY_RX_IDLE_DTCT_CNTRL 0x64C -+#define PCIE_PHY_POWER_STATE_CONFIG1 0x650 -+#define PCIE_PHY_POWER_STATE_CONFIG2 0x654 -+#define PCIE_PHY_POWER_STATE_CONFIG3 0x658 -+#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_L 0x65C -+#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_H 0x660 -+#define PCIE_PHY_RCVR_DTCT_DLY_U3_L 0x664 -+#define PCIE_PHY_RCVR_DTCT_DLY_U3_H 0x668 -+#define PCIE_PHY_LOCK_DETECT_CONFIG1 0x66C -+#define PCIE_PHY_LOCK_DETECT_CONFIG2 0x670 -+#define PCIE_PHY_LOCK_DETECT_CONFIG3 0x674 -+#define PCIE_PHY_TSYNC_RSYNC_TIME 0x678 -+#define PCIE_PHY_SIGDET_LOW_2_IDLE_TIME 0x67C -+#define PCIE_PHY_BEACON_2_IDLE_TIME_L 0x680 -+#define PCIE_PHY_BEACON_2_IDLE_TIME_H 0x684 -+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK 0x688 -+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK 0x68C -+#define PCIE_PHY_LFPS_DET_HIGH_COUNT_VAL 0x690 -+#define PCIE_PHY_LFPS_TX_ECSTART_EQTLOCK 0x694 -+#define PCIE_PHY_LFPS_TX_END_CNT_P2U3_START 0x698 -+#define PCIE_PHY_RXEQTRAINING_WAIT_TIME 0x69C -+#define PCIE_PHY_RXEQTRAINING_RUN_TIME 0x6A0 -+#define PCIE_PHY_TXONESZEROS_RUN_LENGTH 0x6A4 -+#define PCIE_PHY_FLL_CNTRL1 0x6A8 -+#define PCIE_PHY_FLL_CNTRL2 0x6AC -+#define PCIE_PHY_FLL_CNT_VAL_L 0x6B0 -+#define PCIE_PHY_FLL_CNT_VAL_H_TOL 0x6B4 -+#define PCIE_PHY_FLL_MAN_CODE 0x6B8 -+#define PCIE_PHY_AUTONOMOUS_MODE_CTRL 0x6BC -+#define PCIE_PHY_LFPS_RXTERM_IRQ_CLEAR 0x6C0 -+#define PCIE_PHY_ARCVR_DTCT_EN_PERIOD 0x6C4 -+#define PCIE_PHY_ARCVR_DTCT_CM_DLY 0x6C8 -+#define PCIE_PHY_ALFPS_DEGLITCH_VAL 0x6CC -+#define PCIE_PHY_INSIG_SW_CTRL1 0x6D0 -+#define PCIE_PHY_INSIG_SW_CTRL2 0x6D4 -+#define PCIE_PHY_INSIG_SW_CTRL3 0x6D8 -+#define PCIE_PHY_INSIG_MX_CTRL1 0x6DC -+#define PCIE_PHY_INSIG_MX_CTRL2 0x6E0 -+#define PCIE_PHY_INSIG_MX_CTRL3 0x6E4 -+#define PCIE_PHY_TEST_CONTROL 0x6E8 -+#define PCIE_PHY_BIST_CTRL 0x6EC -+#define PCIE_PHY_PRBS_POLY0 0x6F0 -+#define PCIE_PHY_PRBS_POLY1 0x6F4 -+#define PCIE_PHY_PRBS_SEED0 0x6F8 -+#define PCIE_PHY_PRBS_SEED1 0x6FC -+#define PCIE_PHY_FIXED_PAT_CTRL 0x700 -+#define PCIE_PHY_FIXED_PAT0 0x704 -+#define PCIE_PHY_FIXED_PAT1 0x708 -+#define PCIE_PHY_FIXED_PAT2 0x70C -+#define PCIE_PHY_FIXED_PAT3 0x710 -+#define PCIE_PHY_SPARE1 0x714 -+#define PCIE_PHY_BIST_CHK_ERR_CNT_L 0x718 -+#define PCIE_PHY_BIST_CHK_ERR_CNT_H 0x71C -+#define PCIE_PHY_BIST_CHK_STATUS 0x720 -+#define PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE 0x724 -+#define PCIE_PHY_PCS_STATUS 0x728 -+#define PCIE_PHY_PCS_STATUS2 0x72C -+#define PCIE_PHY_REVISION_ID0 0x730 -+#define PCIE_PHY_REVISION_ID1 0x734 -+#define PCIE_PHY_REVISION_ID2 0x738 -+#define PCIE_PHY_REVISION_ID3 0x73C -+#define PCIE_PHY_DEBUG_BUS_0_STATUS 0x740 -+#define PCIE_PHY_DEBUG_BUS_1_STATUS 0x744 -+#define PCIE_PHY_DEBUG_BUS_2_STATUS 0x748 -+#define PCIE_PHY_DEBUG_BUS_3_STATUS 0x74C -+ -+#else -+ -+#define QSERDES_COM_SYS_CLK_CTRL 0x000 -+#define QSERDES_COM_PLL_VCOTAIL_EN 0x004 -+#define QSERDES_COM_CMN_MODE 0x008 -+#define QSERDES_COM_IE_TRIM 0x00c -+#define QSERDES_COM_IP_TRIM 0x010 -+#define QSERDES_COM_PLL_CNTRL 0x014 -+#define QSERDES_COM_PLL_IP_SETI 0x018 -+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x01c -+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x020 -+#define QSERDES_COM_PLL_CP_SETI 0x024 -+#define QSERDES_COM_PLL_IP_SETP 0x028 -+#define QSERDES_COM_PLL_CP_SETP 0x02c -+#define QSERDES_COM_ATB_SEL1 0x030 -+#define QSERDES_COM_ATB_SEL2 0x034 -+#define QSERDES_COM_SYSCLK_EN_SEL 0x038 -+#define QSERDES_COM_RES_CODE_TXBAND 0x03c -+#define QSERDES_COM_RESETSM_CNTRL 0x040 -+#define QSERDES_COM_PLLLOCK_CMP1 0x044 -+#define QSERDES_COM_PLLLOCK_CMP2 0x048 -+#define QSERDES_COM_PLLLOCK_CMP3 0x04c -+#define QSERDES_COM_PLLLOCK_CMP_EN 0x050 -+#define QSERDES_COM_RES_TRIM_OFFSET 0x054 -+#define QSERDES_COM_BGTC 0x058 -+#define QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP 0x05c -+#define QSERDES_COM_PLL_VCO_TUNE 0x060 -+#define QSERDES_COM_DEC_START1 0x064 -+#define QSERDES_COM_PLL_AMP_OS 0x068 -+#define QSERDES_COM_SSC_EN_CENTER 0x06c -+#define QSERDES_COM_SSC_ADJ_PER1 0x070 -+#define QSERDES_COM_SSC_ADJ_PER2 0x074 -+#define QSERDES_COM_SSC_PER1 0x078 -+#define QSERDES_COM_SSC_PER2 0x07c -+#define QSERDES_COM_SSC_STEP_SIZE1 0x080 -+#define QSERDES_COM_SSC_STEP_SIZE2 0x084 -+#define QSERDES_COM_RES_TRIM_SEARCH 0x088 -+#define QSERDES_COM_RES_TRIM_FREEZE 0x08c -+#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE 0x090 -+#define QSERDES_COM_FAUX_EN 0x094 -+#define QSERDES_COM_DIV_FRAC_START1 0x098 -+#define QSERDES_COM_DIV_FRAC_START2 0x09c -+#define QSERDES_COM_DIV_FRAC_START3 0x0a0 -+#define QSERDES_COM_DEC_START2 0x0a4 -+#define QSERDES_COM_PLL_RXTXEPCLK_EN 0x0a8 -+#define QSERDES_COM_PLL_CRCTRL 0x0ac -+#define QSERDES_COM_PLL_CLKEPDIV 0x0b0 -+#define QSERDES_COM_PLL_FREQUPDATE 0x0b4 -+#define QSERDES_COM_PLL_VCO_HIGH 0x0b8 -+#define QSERDES_COM_RESET_SM 0x0bc -+#define QSERDES_TX_BIST_MODE_LANENO 0x200 -+#define QSERDES_TX_CLKBUF_ENABLE 0x204 -+#define QSERDES_TX_TX_EMP_POST1_LVL 0x208 -+#define QSERDES_TX_TX_DRV_LVL 0x20c -+#define QSERDES_TX_RESET_TSYNC_EN 0x210 -+#define QSERDES_TX_LPB_EN 0x214 -+#define QSERDES_TX_RES_CODE 0x218 -+#define QSERDES_TX_PERL_LENGTH1 0x21c -+#define QSERDES_TX_PERL_LENGTH2 0x220 -+#define QSERDES_TX_SERDES_BYP_EN_OUT 0x224 -+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN 0x228 -+#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x22c -+#define QSERDES_TX_BIST_PATTERN1 0x230 -+#define QSERDES_TX_BIST_PATTERN2 0x234 -+#define QSERDES_TX_BIST_PATTERN3 0x238 -+#define QSERDES_TX_BIST_PATTERN4 0x23c -+#define QSERDES_TX_BIST_PATTERN5 0x240 -+#define QSERDES_TX_BIST_PATTERN6 0x244 -+#define QSERDES_TX_BIST_PATTERN7 0x248 -+#define QSERDES_TX_BIST_PATTERN8 0x24c -+#define QSERDES_TX_LANE_MODE 0x250 -+#define QSERDES_TX_ATB_SEL 0x254 -+#define QSERDES_TX_REC_DETECT_LVL 0x258 -+#define QSERDES_TX_PRBS_SEED1 0x25c -+#define QSERDES_TX_PRBS_SEED2 0x260 -+#define QSERDES_TX_PRBS_SEED3 0x264 -+#define QSERDES_TX_PRBS_SEED4 0x268 -+#define QSERDES_TX_RESET_GEN 0x26c -+#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x270 -+#define QSERDES_TX_TX_INTERFACE_MODE 0x274 -+#define QSERDES_TX_BIST_STATUS 0x278 -+#define QSERDES_TX_BIST_ERROR_COUNT1 0x27c -+#define QSERDES_TX_BIST_ERROR_COUNT2 0x280 -+#define QSERDES_RX_CDR_CONTROL 0x400 -+#define QSERDES_RX_AUX_CONTROL 0x404 -+#define QSERDES_RX_AUX_DATA_TCODE 0x408 -+#define QSERDES_RX_RCLK_AUXDATA_SEL 0x40c -+#define QSERDES_RX_CDR_CONTROL2 0x410 -+#define QSERDES_RX_AC_JTAG_INITP 0x414 -+#define QSERDES_RX_AC_JTAG_INITN 0x418 -+#define QSERDES_RX_AC_JTAG_LVL_EN 0x41c -+#define QSERDES_RX_AC_JTAG_MODE 0x420 -+#define QSERDES_RX_AC_JTAG_RESET 0x424 -+#define QSERDES_RX_RX_IQ_RXDET_EN 0x428 -+#define QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE 0x42c -+#define QSERDES_RX_RX_EQ_GAIN12 0x430 -+#define QSERDES_RX_SIGDET_CNTRL 0x434 -+#define QSERDES_RX_RX_BAND 0x438 -+#define QSERDES_RX_CDR_FREEZE_UP_DN 0x43c -+#define QSERDES_RX_RX_INTERFACE_MODE 0x440 -+#define QSERDES_RX_JITTER_GEN_MODE 0x444 -+#define QSERDES_RX_BUJ_AMP 0x448 -+#define QSERDES_RX_SJ_AMP1 0x44c -+#define QSERDES_RX_SJ_AMP2 0x450 -+#define QSERDES_RX_SJ_PER1 0x454 -+#define QSERDES_RX_SJ_PER2 0x458 -+#define QSERDES_RX_BUJ_STEP_FREQ1 0x45c -+#define QSERDES_RX_BUJ_STEP_FREQ2 0x460 -+#define QSERDES_RX_PPM_OFFSET1 0x464 -+#define QSERDES_RX_PPM_OFFSET2 0x468 -+#define QSERDES_RX_SIGN_PPM_PERIOD1 0x46c -+#define QSERDES_RX_SIGN_PPM_PERIOD2 0x470 -+#define QSERDES_RX_SSC_CTRL 0x474 -+#define QSERDES_RX_SSC_COUNT1 0x478 -+#define QSERDES_RX_SSC_COUNT2 0x47c -+#define QSERDES_RX_PWM_CNTRL1 0x480 -+#define QSERDES_RX_PWM_CNTRL2 0x484 -+#define QSERDES_RX_PWM_NDIV 0x488 -+#define QSERDES_RX_PI_CTRL1 0x48c -+#define QSERDES_RX_PI_CTRL2 0x490 -+#define QSERDES_RX_PI_QUAD 0x494 -+#define QSERDES_RX_IDATA1 0x498 -+#define QSERDES_RX_IDATA2 0x49c -+#define QSERDES_RX_AUX_DATA1 0x4a0 -+#define QSERDES_RX_AUX_DATA2 0x4a4 -+#define QSERDES_RX_AC_JTAG_OUTP 0x4a8 -+#define QSERDES_RX_AC_JTAG_OUTN 0x4ac -+#define QSERDES_RX_RX_SIGDET_PWMDECSTATUS 0x4b0 -+#define PCIE_PHY_SW_RESET 0x600 -+#define PCIE_PHY_POWER_DOWN_CONTROL 0x604 -+#define PCIE_PHY_START 0x608 -+#define PCIE_PHY_TXMGN_V1_V0 0x60c -+#define PCIE_PHY_TXMGN_V3_V2 0x610 -+#define PCIE_PHY_TXMGN_LS_V4 0x614 -+#define PCIE_PHY_TXDEEMPH_M6DB_V0 0x618 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V0 0x61c -+#define PCIE_PHY_TXDEEMPH_M6DB_V1 0x620 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V1 0x624 -+#define PCIE_PHY_TXDEEMPH_M6DB_V2 0x628 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V2 0x62c -+#define PCIE_PHY_TXDEEMPH_M6DB_V3 0x630 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V3 0x634 -+#define PCIE_PHY_TXDEEMPH_M6DB_V4 0x638 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_V4 0x63c -+#define PCIE_PHY_TXDEEMPH_M6DB_LS 0x640 -+#define PCIE_PHY_TXDEEMPH_M3P5DB_LS 0x644 -+#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE 0x648 -+#define PCIE_PHY_RX_IDLE_DTCT_CNTRL 0x64c -+#define PCIE_PHY_POWER_STATE_CONFIG1 0x650 -+#define PCIE_PHY_POWER_STATE_CONFIG2 0x654 -+#define PCIE_PHY_RCVR_DTCT_DLY_L 0x658 -+#define PCIE_PHY_RCVR_DTCT_DLY_H 0x65c -+#define PCIE_PHY_LOCK_DETECT_CONFIG1 0x660 -+#define PCIE_PHY_LOCK_DETECT_CONFIG2 0x664 -+#define PCIE_PHY_TSYNC_RSYNC_TIME 0x668 -+#define PCIE_PHY_SIGDET_LOW_2_IDLE_TIME 0x66c -+#define PCIE_PHY_BEACON_2_IDLE_TIME_L 0x670 -+#define PCIE_PHY_BEACON_2_IDLE_TIME_H 0x674 -+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK 0x678 -+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK 0x67c -+#define PCIE_PHY_INSIG_SW_CTRL1 0x680 -+#define PCIE_PHY_INSIG_SW_CTRL2 0x684 -+#define PCIE_PHY_INSIG_MX_CTRL1 0x688 -+#define PCIE_PHY_INSIG_MX_CTRL2 0x68c -+#define PCIE_PHY_TEST_CONTROL 0x690 -+#define PCIE_PHY_BIST_CTRL 0x694 -+#define PCIE_PHY_PRBS_POLY0 0x698 -+#define PCIE_PHY_PRBS_POLY1 0x69c -+#define PCIE_PHY_PRBS_SEED0 0x6a0 -+#define PCIE_PHY_PRBS_SEED1 0x6a4 -+#define PCIE_PHY_FIXED_PAT_CTRL 0x6a8 -+#define PCIE_PHY_FIXED_PAT0 0x6ac -+#define PCIE_PHY_FIXED_PAT1 0x6b0 -+#define PCIE_PHY_FIXED_PAT2 0x6b4 -+#define PCIE_PHY_FIXED_PAT3 0x6b8 -+#define PCIE_PHY_BIST_CHK_ERR_CNT_L 0x6bc -+#define PCIE_PHY_BIST_CHK_ERR_CNT_H 0x6c0 -+#define PCIE_PHY_BIST_CHK_STATUS 0x6c4 -+#define PCIE_PHY_PCS_STATUS 0x6c8 -+#define PCIE_PHY_REVISION_ID0 0x6cc -+#define PCIE_PHY_REVISION_ID1 0x6d0 -+#define PCIE_PHY_REVISION_ID2 0x6d4 -+#define PCIE_PHY_REVISION_ID3 0x6d8 -+#define PCIE_PHY_DEBUG_BUS_0_STATUS 0x6dc -+#define PCIE_PHY_DEBUG_BUS_1_STATUS 0x6e0 -+#define PCIE_PHY_DEBUG_BUS_2_STATUS 0x6e4 -+#define PCIE_PHY_DEBUG_BUS_3_STATUS 0x6e8 -+#endif -+ -+#endif --- -2.7.4 -