diff --git a/device/community/linux-pine64-pinephonepro/0001-arm64-dts-rk3399-pinephone-pro-Keep-modem-regulators.patch b/device/community/linux-pine64-pinephonepro/0001-arm64-dts-rk3399-pinephone-pro-Keep-modem-regulators.patch new file mode 100644 index 000000000..7e4801921 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/0001-arm64-dts-rk3399-pinephone-pro-Keep-modem-regulators.patch @@ -0,0 +1,33 @@ +From: Dummy +Subject: [PATCH] arm64: dts: rk3399-pinephone-pro: Keep modem regulators + always-on + +The vcc_4g and vcc_4g_5v regulators power the EG25 modem. Mark them +always-on so the modem is powered at boot when using eg25-manager. +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 7a3fa599f541..9afef750bb9b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -430,6 +430,7 @@ vcc_4g_5v: regulator-vcc-4g-5v { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_sys>; ++ regulator-always-on; + }; + + vcc_4g: regulator-vcc-4g { +@@ -442,6 +443,7 @@ vcc_4g: regulator-vcc-4g { + regulator-min-microvolt = <3800000>; + regulator-max-microvolt = <3800000>; + vin-supply = <&vcc_sys>; ++ regulator-always-on; + }; + + vcc1v8_codec: regulator-vcc1v8-codec { +-- +2.39.5 + diff --git a/device/community/linux-pine64-pinephonepro/APKBUILD b/device/community/linux-pine64-pinephonepro/APKBUILD index 9fe772a4a..3ec50ea31 100644 --- a/device/community/linux-pine64-pinephonepro/APKBUILD +++ b/device/community/linux-pine64-pinephonepro/APKBUILD @@ -1,10 +1,8 @@ maintainer="Rudraksha Gupta " pkgname=linux-pine64-pinephonepro -pkgver=7.0 -_pkgver=7.0 -_tagdate=20251104-2007 -pkgrel=3 -pkgdesc="Mainline kernel for the pinephone pro" +pkgver=7.1 +pkgrel=0 +pkgdesc="Mainline kernel for the Pinephone Pro" arch="aarch64" _flavor="${pkgname#linux-}" url="https://kernel.org" @@ -45,10 +43,16 @@ esac # Currently, the megi kernel is the most up-to-date pinephone pro kernel. As postmarketOS doesn't want to use the Megi kernel directly, we can strip the Megi kernel down to it's essentials by doing the following: # -# git reset --hard v7.0 -# git merge megi/fusb302-7.0 megi/imx258-7.0 megi/rk818-7.0 megi/rt5640-7.0 megi/rk3399-typec-7.0 megi/typec-extcon-7.0 megi/tcpm-7.0 megi/arasan-7.0 megi/ppp-7.0 -# git cherry-pick ebc624f4 # `arm64: dts: rockchip: rk3399-s: Add DMC table ` -# git rebase v7.0 +# git clone https://codeberg.org/megi/linux.git --branch orange-pi-7.1 +# git switch -c ppp-7.1 +# git reset --hard 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 # v7.1 +# +# cherry pick and make sure to fix/skip conflicts... +# +# git cherry-pick $(git log --reverse --format='%H%x09%s' HEAD..orange-pi-7.1 | awk -F'\t' '{n=split($2,a,":"); s=a[n-1]; gsub(/^ +| +$/,"",s); if(s ~/^(arasan|cdn-dp|displayport|dwc3|fusb302|gigadevice|imx258|phy-rockchip-inno-usb2|rk3399|rk3399-pinephone-pro|rk3399-s|rk818|rk818-battery|rk818-charger|rockchip|rockchip-inno-usb2|rt5640|supply|tcpm|typec-extcon|rk8xx|rk818_battery|rockchip-typec)$/) print $1}') +# +# keep modem always on... +# # git am --ignore-whitespace <<'PATCH' # From: Dummy # Subject: [PATCH] arm64: dts: rk3399-pinephone-pro: Keep modem regulators always-on @@ -85,10 +89,96 @@ esac # Source _config="config-$_flavor.$CARCH" source=" - https://codeberg.org/LogicalErzor/linux/archive/ppp-megi-7.0-stripped-modem-fix.tar.gz + ppp-v7.1.tar.gz::https://github.com/torvalds/linux/archive/refs/tags/v7.1.tar.gz config-$_flavor.aarch64 + megi_patches/0001-ASoC-codecs-rt5640-Fix-output-mixer-input-channel-li.patch + megi_patches/0002-ASoC-codecs-rt5640-Fix-hpout-restore-when-lout-is-en.patch + megi_patches/0003-ASoC-codecs-rt5640-Add-input-mixer-input-volume-cont.patch + megi_patches/0004-ASoC-codecs-rt5640-Allow-to-control-single-ended-dif.patch + megi_patches/0005-ASoC-codecs-rt5640-Keep-the-codec-enabled-when-idle.patch + megi_patches/0006-ASoC-codecs-rt5640-Add-support-for-power-supplies.patch + megi_patches/0007-media-imx258-Add-i2c-supply.patch + megi_patches/0008-media-imx258-Add-reset-gpio.patch + megi_patches/0009-media-imx258-Drop-interface-speed-to-1224-mbps.patch + megi_patches/0010-DEBUG-media-imx258-Add-debug-register-access.patch + megi_patches/0011-ASoC-rockchip-Fix-doubling-of-playback-speed-after-s.patch + megi_patches/0012-Revert-usb-dwc3-Abort-suspend-on-soft-disconnect-fai.patch + megi_patches/0013-thermal-rockchip-Add-support-for-RV1106-SoC.patch + megi_patches/0014-soc-rockchip-Add-support-for-power-monitoring-driver.patch + megi_patches/0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch + megi_patches/0016-thermal-rockchip-Add-support-for-RK3506.patch + megi_patches/0017-mtd-spi-nor-gigadevice-Add-support-for-gd25lq128e.patch + megi_patches/0018-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch + megi_patches/0019-clk-rockchip-rk3399-Don-t-allow-to-reparent-dclk_vop.patch + megi_patches/0020-power-supply-Add-support-for-USB_BC_ENABLED-and-USB_.patch + megi_patches/0021-mfd-rk8xx-Enable-rk808-clkout2-function.patch + megi_patches/0022-power-supply-rk818_battery-Add-battery-charger-drive.patch + megi_patches/0023-power-supply-rk818_battery-Add-code-docs-for-the-HW-.patch + megi_patches/0024-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch + megi_patches/0025-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch + megi_patches/0026-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch + megi_patches/0027-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch + megi_patches/0028-usb-typec-fusb302-Slightly-increase-wait-time-for-BC.patch + megi_patches/0029-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch + megi_patches/0030-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch + megi_patches/0031-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch + megi_patches/0032-usb-typec-fusb302-Add-OF-extcon-support.patch + megi_patches/0033-usb-typec-fusb302-Fix-register-definitions.patch + megi_patches/0034-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch + megi_patches/0035-usb-typec-fusb302-Turn-off-VBUS-and-VCONN-on-shutdow.patch + megi_patches/0036-phy-rockchip-inno-usb2-Add-support-for-RV1106-RV1103.patch + megi_patches/0037-phy-rockchip-inno-usb2-Add-support-for-RK3506.patch + megi_patches/0038-phy-rockchip-inno-usb2-Add-PHY-tuning-for-rk3566-rk3.patch + megi_patches/0039-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch + megi_patches/0040-phy-rockchip-inno-usb2-Decrease-delay-between-port-i.patch + megi_patches/0041-phy-rockchip-inno-usb2-More-robust-charger-detection.patch + megi_patches/0042-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch + megi_patches/0043-usb-dwc3-Always-disable-SUSPHY-when-entering-a-role.patch + megi_patches/0044-usb-dwc3-Track-cable-connection-state-separately-fro.patch + megi_patches/0045-usb-dwc3-Power-cycle-USB3-PHY-on-role-connection-cha.patch + megi_patches/0046-usb-dwc3-Register-the-role-even-when-no-cable-is-con.patch + megi_patches/0047-phy-rockchip-inno-usb2-Set-up-charger-detection-mode.patch + megi_patches/0048-usb-dwc3-Do-not-re-register-the-gadget-on-cable-even.patch + megi_patches/0049-phy-rockchip-typec-Support-a-Type-C-orientation-swit.patch + megi_patches/0050-phy-rockchip-typec-Support-a-Type-C-mode-mux.patch + megi_patches/0051-phy-rockchip-typec-Publish-the-DP-lane-count.patch + megi_patches/0052-drm-rockchip-cdn-dp-Support-ports-driven-by-a-Type-C.patch + megi_patches/0053-phy-rockchip-inno-usb2-Learn-the-host-role-from-phy_.patch + megi_patches/0054-usb-dwc3-Tell-the-UDC-core-when-there-is-no-cable.patch + megi_patches/0055-phy-rockchip-inno-usb2-Do-not-run-charger-detection-.patch + megi_patches/0056-usb-dwc3-Set-the-PHY-mode-before-starting-the-host.patch + megi_patches/0057-usb-dwc3-Do-not-connect-the-gadget-while-no-cable-is.patch + megi_patches/0058-phy-rockchip-inno-usb2-Power-the-PHY-up-before-the-h.patch + megi_patches/0059-usb-dwc3-Require-MPS-aligned-OUT-buffers.patch + megi_patches/0060-usb-typec-displayport-Update-the-mux-before-signalli.patch + megi_patches/0061-arm64-dts-rockchip-rk3399-Disable-debug-nodes.patch + megi_patches/0062-arm64-dts-rockchip-rk3399-Add-reboot-mode-driver.patch + megi_patches/0063-arm64-dts-rockchip-rk3399-Power-cycle-the-USB3-PHY-o.patch + megi_patches/0064-arm64-dts-rockchip-rk3399-Add-dmc_opp_table.patch + megi_patches/0065-arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch + megi_patches/0066-arm64-dts-rockchip-rk3399-Number-the-CDN-DP-ports.patch + megi_patches/0067-arm64-dts-rockchip-rk3399-pinephone-pro-Add-Type-C-p.patch + megi_patches/0068-arm64-dts-rockchip-rk3399-pinephone-pro-Add-internal.patch + megi_patches/0069-arm64-dts-rockchip-rk3399-pinephone-pro-Add-battery-.patch + megi_patches/0070-arm64-dts-rockchip-rk3399-pinephone-pro-Add-sound-su.patch + megi_patches/0071-arm64-dts-rockchip-rk3399-pinephone-pro-Add-modem-su.patch + megi_patches/0072-arm64-dts-rockchip-rk3399-pinephone-pro-Change-modem.patch + megi_patches/0073-arm64-dts-rockchip-rk3399-pinephone-pro-Add-light-pr.patch + megi_patches/0074-arm64-dts-rockchip-rk3399-pinephone-pro-Add-I2C-supp.patch + megi_patches/0075-arm64-dts-rockchip-rk3399-pinephone-pro-Add-magnetom.patch + megi_patches/0076-arm64-dts-rockchip-rk3399-pinephone-pro-Add-mount-ma.patch + megi_patches/0077-arm64-dts-rockchip-rk3399-pinephone-pro-Enable-POGO-.patch + megi_patches/0078-arm64-dts-rockchip-rk3399-pinephone-pro-Add-pinephon.patch + megi_patches/0079-arm64-dts-rockchip-rk3399-pinephone-pro-Switch-LED-b.patch + megi_patches/0080-arm64-dts-rockchip-rk3399-pinephone-pro-Pre-configur.patch + megi_patches/0081-arm64-dts-rockchip-rk3399-pinephone-pro-Improve-SPI-.patch + megi_patches/0082-arm64-dts-rockchip-rk3399-pinephone-pro-Assign-power.patch + megi_patches/0083-arm64-dts-rockchip-rk3399-pinephone-pro-Add-camera-f.patch + megi_patches/0084-arm64-dts-rockchip-rk3399-pinephone-pro-Disable-inte.patch + 0001-arm64-dts-rk3399-pinephone-pro-Keep-modem-regulators.patch + BTF-seg-fault.patch " -builddir="$srcdir/linux" +builddir="$srcdir/linux-$pkgver" prepare() { default_prepare @@ -111,7 +201,7 @@ package() { INSTALL_MOD_STRIP=1 \ INSTALL_PATH="$pkgdir"/boot \ INSTALL_DTBS_PATH="$pkgdir"/boot/dtbs-$_flavor - rm -f "$pkgdir"/lib/modules/*/build "$pkgdir"/lib/modules/*/source + rm -f "$pkgdir"/usr/lib/modules/*/build "$pkgdir"/usr/lib/modules/*/source install -D "$builddir"/include/config/kernel.release \ "$pkgdir"/usr/share/kernel/$_flavor/kernel.release @@ -119,6 +209,92 @@ package() { sha512sums=" -8a2e76553f0635d5891ae5e11219506da73893d78ce1cccf3421c0bbaf87c6caf48d927f5be7c8e2a8119e05cb9035e7b3c15bea786ff2eaeafd693b9219a428 ppp-megi-7.0-stripped-modem-fix.tar.gz -510a18c5ef416be1eab1c93d9b62b4c3530fb666e8166d5ab3fd94998773f9d405aac4deec3be03390195ad0f1a1c5efadf19b19356332f49a6c426e95106733 config-pine64-pinephonepro.aarch64 +10c0cfd70a7a5731963cb4c7538396f6ed637d7db532a1c04b00628af47f4b81ffb3d92200e64ba1ce04047e4b2e39e6845c4067762e85ad9df13f2740c10397 ppp-v7.1.tar.gz +f489be70242738c722dd588b9cadc94dbab329a76fed396473318f94abcc9d724ed54cd06f5d101e9b95e5a9098e46a5c8c95ddeb6d30f3b87f822b83080ac43 config-pine64-pinephonepro.aarch64 +cafe0f07dab850059475c8f84df0b859d65bedda455517a5996f75d4e5f28c7fe66ac5a2b2e4ee6b46ed173819b899cc37ac08d1106bf635beab018ad636ee1e 0001-ASoC-codecs-rt5640-Fix-output-mixer-input-channel-li.patch +5db306c6728666c3e64ca28a3cc330d7452c87c9119aa8cd57a4549cee7aee1c1137dedece8c50bb58d1040bd861d7722c6341112b24699919fb5a3bd816afdb 0002-ASoC-codecs-rt5640-Fix-hpout-restore-when-lout-is-en.patch +a734d758f0e84fbd7dd3c8e7daa24ca8778af2d25e8d1df3c7e27978f616428fa57e507dede65f59dab6ce23597380b69d83fde35b549b3c0d8b55a38f58a57b 0003-ASoC-codecs-rt5640-Add-input-mixer-input-volume-cont.patch +b7b94b6b2f144932009fa94ad0e5505b5749cea446221ef94e51490117b9a747080a876d7a004a0b0829fba1e3ecbb714f867a3725e40cf51fb90ff49a7c0da9 0004-ASoC-codecs-rt5640-Allow-to-control-single-ended-dif.patch +5c8e63d2e3fe0b4805f74ad0da75159495dd82592e701160d07c4729f00e9c9de7d23dee6250fb62fbbba05c3a0ca42f6495e2f1e51fd1d330f62394003ac222 0005-ASoC-codecs-rt5640-Keep-the-codec-enabled-when-idle.patch +89c5ecda11e85c795aaf5e718b7c636ed149ca17ca71068cc329abd598edb568584db3b559f9cc4074c8eef4e4e9f749181726e416747ca9b853e6bdd30b4dd1 0006-ASoC-codecs-rt5640-Add-support-for-power-supplies.patch +0a865555b611922507021b262d5ba6f7991ddc0354a440543aa18c2ddc9c47a6807905fd0fcd23e33dfa1f93a337f016114230c472c9ee5f4d56fa45866e828f 0007-media-imx258-Add-i2c-supply.patch +a9feac6a2eaf059699584dab91665a3bcf4afce09dba8c69739c77a1fc3639d01de1dc8fd68d3593afeeac8413a1c1e37d6bf59f272e9f8e504fa67b26d07f76 0008-media-imx258-Add-reset-gpio.patch +50983f8bf418b0b602aa4fedaba273316d13eebff0c4a03f8f680851797a1f28fcf2da0ca98d1fa61cb0e3eb30a36a23b936262f426bfa38a4f43ad64116e307 0009-media-imx258-Drop-interface-speed-to-1224-mbps.patch +a7c6cde4aa77e5df3e464f7eef00859d1ea71f4874f1d447cb78dbc722691380f60aabfef39ce1e740448593a5017b50cb38d01b68ea7c279c808f5043acfce6 0010-DEBUG-media-imx258-Add-debug-register-access.patch +001b8f0c086b24acc5b53f0ce13a10d754903314b21f8fc22c45b5bd6ea719c09c2c2b51a839710e1d2fcff70f7dceeac7ac44a24fd42acb8c99cb1bb3c2023c 0011-ASoC-rockchip-Fix-doubling-of-playback-speed-after-s.patch +f0b43b03a918c66f96d6d2987762eefdd4c5a11f4252a0b977532a17a0d7a24498b10511a059dcb07f7e572cb3864599a31c1fc3ca7505727cf1e08b64a9d614 0012-Revert-usb-dwc3-Abort-suspend-on-soft-disconnect-fai.patch +1bf5e2cef1ecf4d470b6ef0159fce095eee239f4c296c6bbbc14edf510fb9a20b0048ac537876c91e2cc4c0318572c2eaa727ac5ddacf3dbddea2bf0202043e0 0013-thermal-rockchip-Add-support-for-RV1106-SoC.patch +7b0e3cb86ebe501fcf82433c2cc046720f311cdeb423d3191fb472f54501e99eafecaf5da338698453e0feb6c00ed29257ecf1a5765290911d47d77467875f43 0014-soc-rockchip-Add-support-for-power-monitoring-driver.patch +67c5119ae325a9e328e5d9e92690f575d2cfd5c46304c65a4c7966330800fadd7a314fe57ba76c29970ed9afbfebbdf676e95b1a7f3a8b4328ba2dba87d7f2e3 0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch +a9a8aa2da2f503d86681e4b61606db6bfac4622030f05ea081f4d75b05edfcf4794a3a7575de4c1c9a5025aa3f25be6fa348418f97d06c0bed88843fc2aca9c5 0016-thermal-rockchip-Add-support-for-RK3506.patch +96a56ce7aa57c8e1e04892dcf12c043143f6e0dc0affddeb4c565865358d8b51d84fb2e25ef58c9a07a684a8e43058f64dc206c1371b3b07d0e115444885469f 0017-mtd-spi-nor-gigadevice-Add-support-for-gd25lq128e.patch +c7dfed2e10fc2902647134ce09351c2567d04be3d190ecacd09f08130587311553d12831d1b0652b41eb949f0172308efaa65cda0ea0b53aeff92c007b805619 0018-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch +1639b72d3552817018fb2811388d30c0c5f958ef0e14dd395962db0394bc8cdd8369d70ca494668cbad0a36e61df60cf508686ac06794639038dbc0d15382477 0019-clk-rockchip-rk3399-Don-t-allow-to-reparent-dclk_vop.patch +247a291979eafbccdfe85c99a569235f7fff860f037e2d47fd76312bb20277c0f0fa3e523be969f977657b602a88b846f2ddc058c243b64828bcca0315fe7386 0020-power-supply-Add-support-for-USB_BC_ENABLED-and-USB_.patch +888dbcd2af60be116391e9464b0915a2468fcb2661803f2df54e9df9b1d38c718eabaf704c0d9c6cbf96f73b115ed14bac0d2bbbb87617cf10181b357a2c719e 0021-mfd-rk8xx-Enable-rk808-clkout2-function.patch +73e2cadbfacb5c8e9547ca410efb6c11a9c87dc8239fd7096f2373d56a7697f19114c08811b8a01b1de5e199cbd20f9a78cc5c4e0db6830b79842a8eede9d079 0022-power-supply-rk818_battery-Add-battery-charger-drive.patch +6d7027d970a389213ef8a233c564df25e854f8635db2dbf4c14f7d29de51ad6d3d9ee61523513be71b382c2ee1d730bd32160b1809ed0dcbfec2c7b24c6547ad 0023-power-supply-rk818_battery-Add-code-docs-for-the-HW-.patch +b443aaa45d2bcb98b2b6c43577722fb718198f06baf126e2c1ea2d636a8874787a972c45d0bf83bdceb783920280a7cfeeb7e8d18b2b2634f685e974cc362e97 0024-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch +db34fb8ee7c4ba3baba480ccbe7df866fe7f5277a80739f7eeeadde80c1a4fa103df4f5995ba9660de725ece6aabeaefc302db39d50b6fa8c7fe3fdb212c6e26 0025-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch +98229ad878e1158c887c63c1cfe72b7ca7084bf6b42f092005ba1e327307c20e7c77246cb9258ad2f0813722a91e6023f45553230bff7d248c1923c1452a94c4 0026-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch +89edc9f8935f6905e37e18a554787cf5262624f4c5b9e4dfaa984022bdfb1e0293e95bf731f27ca7640f352167676924b720d9a6a09bded813c07fd152878f63 0027-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch +196080cfeee98682195153f0918edbd08b302e5bad5afc9860747b0b6b90159a133813e0b6940ffabf9a88be36088a680f647cd5b611083c9139ea123989783f 0028-usb-typec-fusb302-Slightly-increase-wait-time-for-BC.patch +dc4d378cd114949ad51f47a8da7deb9cda63a75432f6f386086d41fa0ebe126657da270af89df84495c1777be7e5041274a780877b2ce6ca387e35aaaca7a46d 0029-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch +67049fa20660f5924cf06001ac9e55363f337e4cda084f79b828a9863627893c5a3f4d5cc809d613f60d792540eee579097b652bf49dfee8c9840892485beaad 0030-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch +3bf2d0ac9c737eb761461ab5eb5597c05ef8b368a69b9b0c48ae2b68e58da2d432cd9060fea80c2541b7f453c6fc93657ef94aa073a893b48a7407632d46820d 0031-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch +d9b11a9acbbb64668332e202c786457f370c7f9d2bdedf6144b434d992d067ada00ab027067da0a597277c726ccb7ae68d49580848a95ea88775f0a6837586f7 0032-usb-typec-fusb302-Add-OF-extcon-support.patch +4946338c55952347050ac2c3cb814ce778b8c9933a1974ed093daf82e8234b0a2d8ccb808c9beb29451d41c80a6cc03917a9d1a2a72a374a47374d20d3ca460f 0033-usb-typec-fusb302-Fix-register-definitions.patch +f72b87321bb0ff45e58e729587d6edf97604f1cbf3b5c82e9b26289096f752b0725e800a22525ab662f378eca2637463e3ceb7e213b86f6f5293705dd509d119 0034-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch +4ae24e87a3b514a8fbb41cf47d5d00292bc399ed8fd1299ef4dc4990c31143c5c6d3fbc6c6480089c492b3988f743a86b30420c91a1d551968a3758ea9538b03 0035-usb-typec-fusb302-Turn-off-VBUS-and-VCONN-on-shutdow.patch +065427abc79c435762e25a40cc13b35a14feeca318f767d0384970cf57e7c785c796923bd891746edd0497079941313b0b7839c7988f63e243c8166a0c8f3579 0036-phy-rockchip-inno-usb2-Add-support-for-RV1106-RV1103.patch +9115fe459431dac6558d60f9e6ebab1fef726413c8a3bdb88b21e402f2b1ccd0c485ce65aa9feaca948b2ef88bff6ea38973c761bac0f7dccb7b019938c37ae5 0037-phy-rockchip-inno-usb2-Add-support-for-RK3506.patch +2abd784fd1a6abe3ca5da3919f7a9b7617379d06340cc9549dbf3dee2dcf095fb5aa3735d08e93f4363247667ba14d9f6c96e414afa965b65e12fd00af08bd48 0038-phy-rockchip-inno-usb2-Add-PHY-tuning-for-rk3566-rk3.patch +60656a8bdf90e92661d5d2b69aad78489955e5a82edbee67b385a3285bbb59d59c615e47ad83f993ed3c5b4ee6f39958e5199492d41cf652e333473d27136855 0039-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch +a941594dd09c6468c6b89ba56f8ba127aedc15228904667f521cfbf9d25dafac8dc8e8ecddea3c86af2a1305d2fb40035f85bf3b559a5574bad3e0362649f290 0040-phy-rockchip-inno-usb2-Decrease-delay-between-port-i.patch +b0093aec9b85ef54400585c23b9a1cc2b31598c847cb998ae03f277eaa81a0cdf8c979ac7b8d581b4dff7bd70c601d812eb6ef7bff543fbc00f5ba9436cea748 0041-phy-rockchip-inno-usb2-More-robust-charger-detection.patch +32e6b984e14996387c232ac0ac708d7eaf75d1fc6de3ec5df53a110368be270177cd02253523c66163f6d0c227b0b6a18d171411d76c2757bbe4dd13facb8920 0042-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch +7b4ce3f693b3d1731693c79fdf114de0c67b5a74406b3b46b1bd133babb6e5a20c3e2466feb33239dfc9dd08f9db68fc018c8a80a1115dfd2fd8812b7da017fa 0043-usb-dwc3-Always-disable-SUSPHY-when-entering-a-role.patch +302dad47110479739cb5348c938ad9c9c3b41d0ce46c45ee49211cf71578ca2342edaea664b52d3855fd5a7e8ae7fd850d80bd080a43423c349254e47ff79b11 0044-usb-dwc3-Track-cable-connection-state-separately-fro.patch +e08557f8efe8f730d358f247771d32d3001ea3337db950f3a785b632be8098044933276f2b1a5b376270b88bb21345c056ccb818b74e59b93fb291d062edc85c 0045-usb-dwc3-Power-cycle-USB3-PHY-on-role-connection-cha.patch +107bdb8b4d289306b8f4be1f7088a44a239e8e66cc286d9108f14281d7bb64a5defb54909ec9a356b7bf60aa83152e5c9ac9c5aa8f872a69cf201a5b9c8cff69 0046-usb-dwc3-Register-the-role-even-when-no-cable-is-con.patch +46a8452081223bdf5864c86f50e070a796b7d495b77ad861804e862d2659df29f9c17130bc9ff438b5c9a8cb8df4c3c55191eecb339c5691ad6d9c341c1d6293 0047-phy-rockchip-inno-usb2-Set-up-charger-detection-mode.patch +ec262e3094fe7edac553d8b29662182ca9b617bba266c1e959b04fea1c358dc26be123cb0e5e286e08aac5210f8ba26172230bc9508b8685a293d0349276dc6a 0048-usb-dwc3-Do-not-re-register-the-gadget-on-cable-even.patch +3561f087e06bb61eb1f3c10250120bf747e879aa7aebe039f148d20a8960c9b545f90a882960bc91a4eda593092dcf36de34e81074ed2ccbadb37a9cc2c04731 0049-phy-rockchip-typec-Support-a-Type-C-orientation-swit.patch +cf24ee439036f001d66a6c9c8662f8cafcb0e3b068269782de00b8a1fb14a58b39b66a17078bc0d9c22c8e76093633aeae553c3c05aec803e08b96a40134a3f6 0050-phy-rockchip-typec-Support-a-Type-C-mode-mux.patch +5d659877cb2fd5f099eac102ef2f6da7975215bfbf52b8790999cb11a8be06f37c7c87d26e6fa1c38d6c803e6988913bfb91b41948585fca1d3513d2471d0c77 0051-phy-rockchip-typec-Publish-the-DP-lane-count.patch +76d1b5efc343564f7dde1dbcb8f46a564879395aa12b3b6aa21aaf1ea018ee14c5c2a0dea6cb13d5609a716b623bf008aa11e29021363a57dc33fdf762f6fd43 0052-drm-rockchip-cdn-dp-Support-ports-driven-by-a-Type-C.patch +b6f71b575b69b5a44892bfd51253fff8e422d701d95e1b4cf318b1263ed3227a7e861f181458f37af933e26d3756e985646e2b9f9cec654ce0ac419f822d8a04 0053-phy-rockchip-inno-usb2-Learn-the-host-role-from-phy_.patch +cfedc21450d5ac15411f0703179f312dcf1a7d179d8983a269b69e06b2c07d1292abbb9a741caf7e18d091bac47d87bd0be5382a1b0077136ee67e4fa8e25b0e 0054-usb-dwc3-Tell-the-UDC-core-when-there-is-no-cable.patch +c79dc3ff0ad97514df04bb295f1a33d022c3200b02c21162a2cd9c1da453c935be3d49486904741968dfffda18ff7c273b783eac3ccab875ca99964c07b4eacc 0055-phy-rockchip-inno-usb2-Do-not-run-charger-detection-.patch +56f43f4f6448506d7ddce07b5cb0bcfa8a82fd799da17c7be0c9cec0d7194035455b9850a9637ad1226f80fa01b6a6834c33e3bc50da855e5121160eda806a44 0056-usb-dwc3-Set-the-PHY-mode-before-starting-the-host.patch +86a7b1e9f59e1532c985d3a4a1edd196e4ab47b3047627306e9e7fe122bf3f7fa6a2049f9467d30a221c3d65277600e91d71e9fa295c388117b379456adc2970 0057-usb-dwc3-Do-not-connect-the-gadget-while-no-cable-is.patch +9e5ad8a5f08e6419f6bd25162fe4e542b2cc33890ef761e35fac4fb324f10d5df96fe0f5be3458ee636dd57f05f90ced31d01c084a3580beed7c1c71573f7b23 0058-phy-rockchip-inno-usb2-Power-the-PHY-up-before-the-h.patch +2c6d05ddb458189aedb70f8ba6036fadc942f1b2634003c2f2325625a8a1a4afa933c927d893b4fdac406b593cbeb321320be6f835ca7cb0268448738f850ffc 0059-usb-dwc3-Require-MPS-aligned-OUT-buffers.patch +d40ca07d6379fedd78858097c4e57fea2a52a24254be0a534874c641d1e9ed5e1ee96755420fd45bea1ec538b0340f326712230d5fb1377a07521c8c6c93debf 0060-usb-typec-displayport-Update-the-mux-before-signalli.patch +36a5a4ca60c45161e7913edde968d87884d6c552d6f0bc119a9b6ff3c0d0d3e750410df4cd891c0233f56c3a5b80ef7a7aadb34ec4936dce1f399301fa993001 0061-arm64-dts-rockchip-rk3399-Disable-debug-nodes.patch +15998054b6521020e4895c2024ce2ad9db3650220ee0d3eb693fa44c72086520eefa592c824b015d514b593ca7ded1d3c5b6e1f7146b1133598cae13b4994c62 0062-arm64-dts-rockchip-rk3399-Add-reboot-mode-driver.patch +60904d0dd44dd3740991dcdfb752d7d78470a9814414d407e7ca2de0d4061716deb1c70e08ae1937684e05f5ef6ba4fe57fbcdea69374053906ee3753d11fc63 0063-arm64-dts-rockchip-rk3399-Power-cycle-the-USB3-PHY-o.patch +dff55b92a62499fe5c46d68debeac92213038058b8fd19652b4ae7cc5980205bdb560e568cba4c8109ae298fceeb79c7815f0f405501c8fbccdb759d828bd5f5 0064-arm64-dts-rockchip-rk3399-Add-dmc_opp_table.patch +9f8b94efc2d5c0a6602122731e70a29f8439d2b4861211da189cdb845f707530bd7c73956ad85c94aeb94ec8b62918f8f6b17687b84470087580b3a15c4a5a55 0065-arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch +9ea12b3c4ccc408cb07116da003186ac2060cece7c4847f55489c6fd0cae6035a2555eebf0bbbb54d674d44fc5db0e1bbe1a2ded48dc5ec2a6a3df4c2e2ca185 0066-arm64-dts-rockchip-rk3399-Number-the-CDN-DP-ports.patch +1ecce40405e69b9fec96d1535622ca71ccb5fc76753b459c9e321c7903178623ddc31f5527ca1949b071d47ce1f73dbce74dea8059bc1b683d253f83d9130c80 0067-arm64-dts-rockchip-rk3399-pinephone-pro-Add-Type-C-p.patch +2f1dcbe2f0538cdc4c01aa2992afc1f26d1599e1505780b286c1e645a81be5ed1f44288bf5024c2e3178e2f438f38b7220c76f1fce842548cdcec37728d18b9f 0068-arm64-dts-rockchip-rk3399-pinephone-pro-Add-internal.patch +479cfb8e65ccd95d656ff0fe212aa31cfdd7f3e298d612da27063dcff7e62324210b627e7ab210a273142d3f15384b5f2cadfb1983f3864145355421f99f0957 0069-arm64-dts-rockchip-rk3399-pinephone-pro-Add-battery-.patch +bdf826beedcfe769acd7475f7a30c3adc8225fa103dd1e37c5a31d750fea3044b077f4903986c0ee3d3102e79948f4375bed7a674498e4c21b34b3ba44e54ba1 0070-arm64-dts-rockchip-rk3399-pinephone-pro-Add-sound-su.patch +443688c9b00de95b9bd23fbc08741ef488c849211151f7f8bae136149e0f0aa562d2353875120333e2e7f3e187abfd0bebdc1b2b006d725d33d638e0e20496f6 0071-arm64-dts-rockchip-rk3399-pinephone-pro-Add-modem-su.patch +eb66518e6876badf3ef3bc766328ff621031194dc067ecea5a0ed629702c08cd5f964d3c25c2a1aae9fc5b13615f212c7150ea0c2afe33b0427a1554a6bd4029 0072-arm64-dts-rockchip-rk3399-pinephone-pro-Change-modem.patch +70f83b6210e991b546c70c594e8d6f28ebc557c301acf1ef9f0d1df625dd709f3744ae1738865626ae7ca5b6ce9682bfbc052766bb6989d67b261abd6606ce85 0073-arm64-dts-rockchip-rk3399-pinephone-pro-Add-light-pr.patch +077deb29bf5182a0f1689bc7e6da41f553cc7a2c8a2ef96a2b111f9d5e9e2ba8f26d3228af20f79d15f45c0a69aa8f60733a422fad1895700a299a8aad7d1e51 0074-arm64-dts-rockchip-rk3399-pinephone-pro-Add-I2C-supp.patch +fbb3e8fa505f8a97d512834dd75b849cad5aed4d357dbbe2904d9b435bb8f19c3863ef7417c4ef4afeefc224bd4ad6eab74f4bc00ac66b69aaccb001c29fba71 0075-arm64-dts-rockchip-rk3399-pinephone-pro-Add-magnetom.patch +2d79639df7928f464217e71e4219a40fa082e36bb410b3d1fa12bb1ab6246199c4976fba84fa32e621939c0c37b08929d2be7380756aec40b8bcaff91298e81d 0076-arm64-dts-rockchip-rk3399-pinephone-pro-Add-mount-ma.patch +e1e1fc09232f21c69b8a2613d3da6063d669f8f97a340954702e7227214f1a6c4adc866420618cd365753767a0da93e52ac40c3575e5beac9dc778c93895b622 0077-arm64-dts-rockchip-rk3399-pinephone-pro-Enable-POGO-.patch +ac1e93b71deb4cf8a070c3380061b46e1e1cea094346d02d5f8ed326d3f0e5316ca4ebdae7677ceea3d4456a580542f0075d29df50d1a37d8817392e59e5d380 0078-arm64-dts-rockchip-rk3399-pinephone-pro-Add-pinephon.patch +8a160ffa6e95afbf58608f030c3ca3a7d7de298f7a20ce8e298eba54576a7f99ffff6389e36a4dc5d8bb1110e112c72e0718e4d8e88fe4049019119ba0c1a8fa 0079-arm64-dts-rockchip-rk3399-pinephone-pro-Switch-LED-b.patch +5308f80bd8fdaa3d76f26bd278f2d027689b5e75a8d0e6a00e13b6c056bdd1b30875105e2afbe4daa47f282ab3312df3747e74368601f745b394f2f5874d018a 0080-arm64-dts-rockchip-rk3399-pinephone-pro-Pre-configur.patch +9f61f0602d232c583ec1e447037e8f69eb89fae674e8e1cf65a187d224bc3eeb5da7ce209045c2a48508d9202b355a3ad001ed9ed365b0afdf5cf7e6b98822d3 0081-arm64-dts-rockchip-rk3399-pinephone-pro-Improve-SPI-.patch +04c6e3f63e897d044945cf05f7a15c3d99ec0889f3518691738e84ffa115532b70215695e9e7c1b6b6c3483b872ed676cefd9cebdcf18f0e83db3d5207045813 0082-arm64-dts-rockchip-rk3399-pinephone-pro-Assign-power.patch +2baa9dc87a0f7d9f60e0f0429c83943dab6edc25e963fe70d5bf322e713217ce8285691ebbc7b6f73fd3d48a0fd36c8053f819af904753898b27cf12e8e1df54 0083-arm64-dts-rockchip-rk3399-pinephone-pro-Add-camera-f.patch +e820d9169b6854832d1a29d0c1925732155afe08e72aa865a6804ee67359137cdcbd967acee41913ef36ff1acae02346c4a5f8b07337ab4f9d56ae45b75b56f3 0084-arm64-dts-rockchip-rk3399-pinephone-pro-Disable-inte.patch +79ac34ea9e3f4e74809c5f30bc90c511a3bf02ed9c00475db2adb0b009c5609b00f14dedccafb0f9f2dea136332df8416b78920807df100faa2ce937484871d3 0001-arm64-dts-rk3399-pinephone-pro-Keep-modem-regulators.patch +e2555390d48cbbdc32a7d3f0c27599ff88f2fe3e786d1d0086caaa5b092cc3edcb6e95e2fb07034edb3793f6dc954da0662c723202eb7e2c4956c99b50919b12 BTF-seg-fault.patch " diff --git a/device/community/linux-pine64-pinephonepro/BTF-seg-fault.patch b/device/community/linux-pine64-pinephonepro/BTF-seg-fault.patch new file mode 100644 index 000000000..1fc18c672 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/BTF-seg-fault.patch @@ -0,0 +1,161 @@ +From b23705e6afb6ac4ae6d220dcb35975698667dd76 Mon Sep 17 00:00:00 2001 +From: Carlos Llamas +Date: Sat, 23 May 2026 16:27:21 +0000 +Subject: [PATCH] libbpf: Fix UAF in strset__add_str() + +strset_add_str_mem() might reallocate the strset data buffer in order to +accommodate the provided string 's'. However, if 's' points to a string +already present in the buffer, it becomes dangling after the realloc. +This leads to a use-after-free when attempting to memcpy() the string +into the new buffer. + +One scenario that triggers this problematic path is when resolve_btfids +attempts to patch kfunc prototypes using existing BTF parameter names: + + | resolve_btfids: function bpf_list_push_back_impl already exists in BTF + | Segmentation fault (core dumped) + +Compiling resolve_btfids with fsanitize=address generates a detailed +report of the UAF: + + | ================================================================= + | ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 + | ==1507892==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 at pc 0x55d25155a2a8 bp 0x7ffcef879060 sp 0x7ffcef878818 + | READ of size 5 at 0x7f4c4a500bd4 thread T0 + | #0 0x55d25155a2a7 in memcpy (tools/bpf/resolve_btfids/resolve_btfids+0xcf2a7) + | #1 0x55d2515d708e in strset__add_str tools/lib/bpf/strset.c:162:2 + | #2 0x55d2515c730b in btf__add_str tools/lib/bpf/btf.c:2109:8 + | #3 0x55d2515c9020 in btf__add_func_param tools/lib/bpf/btf.c:3108:14 + | #4 0x55d25159f0b5 in process_kfunc_with_implicit_args tools/bpf/resolve_btfids/main.c:1196:9 + | #5 0x55d25159e004 in btf2btf tools/bpf/resolve_btfids/main.c:1229:9 + | #6 0x55d25159cee7 in main tools/bpf/resolve_btfids/main.c:1535:6 + | #7 0x7f4c78e29f76 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 + | #8 0x7f4c78e2a026 in __libc_start_main csu/../csu/libc-start.c:360:3 + | #9 0x55d2514bb860 in _start (tools/bpf/resolve_btfids/resolve_btfids+0x30860) + | + | 0x7f4c4a500bd4 is located 13268 bytes inside of 2829000-byte region [0x7f4c4a4fd800,0x7f4c4a7b02c8) + | freed by thread T0 here: + | #0 0x55d25155b700 in realloc (tools/bpf/resolve_btfids/resolve_btfids+0xd0700) + | #1 0x55d2515c426c in libbpf_reallocarray tools/lib/bpf/./libbpf_internal.h:220:9 + | #2 0x55d2515c426c in libbpf_add_mem tools/lib/bpf/btf.c:224:13 + | + | previously allocated by thread T0 here: + | #0 0x55d25155b2e3 in malloc (tools/bpf/resolve_btfids/resolve_btfids+0xd02e3) + | #1 0x55d2515d6e7d in strset__new tools/lib/bpf/strset.c:58:20 + +While resolve_btfids could be refactored to avoid this call path, let's +instead fix this issue at the source in strset__add_str() and avoid +similar scenarios. + +Let's check if set->strs_data was reallocated and whether 's' points to +an internal string within the old strset buffer. In such case, 's' is +reconstructed to point to the new buffer. + +While already here, also fix strset__find_str() which suffers from the +same problem by factoring out the common operations into a new helper +function strset_str_append(). + +Fixes: 90d76d3ececc ("libbpf: Extract internal set-of-strings datastructure APIs") +Suggested-by: Andrii Nakryiko +Suggested-by: Mykyta Yatsenko +Signed-off-by: Carlos Llamas +Signed-off-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20260523162722.2718940-1-cmllamas@google.com +--- + tools/lib/bpf/strset.c | 62 ++++++++++++++++++++++++++++-------------- + 1 file changed, 41 insertions(+), 21 deletions(-) + +diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c +index 2464bcbd04e037..ace73c6b3d62b4 100644 +--- a/tools/lib/bpf/strset.c ++++ b/tools/lib/bpf/strset.c +@@ -107,6 +107,41 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) + set->strs_data_len, set->strs_data_max_len, add_sz); + } + ++static long strset_str_append(struct strset *set, const char *s) ++{ ++ uintptr_t old_data = (uintptr_t)set->strs_data; ++ size_t old_data_len = set->strs_data_len; ++ uintptr_t old_s = (uintptr_t)s; ++ long len = strlen(s) + 1; ++ void *p; ++ ++ /* ++ * Hashmap keys are always offsets within set->strs_data, so to even ++ * look up some string from the "outside", we need to first append it ++ * at the end, so that it can be addressed with an offset. Luckily, ++ * until set->strs_data_len is incremented, that string is just a piece ++ * of garbage for the rest of the code, so no harm, no foul. On the ++ * other hand, if the string is unique, it's already appended and ++ * ready to be used, only a simple set->strs_data_len increment away. ++ */ ++ p = strset_add_str_mem(set, len); ++ if (!p) ++ return -ENOMEM; ++ ++ /* ++ * The set->strs_data might have reallocated and if 's' pointed ++ * to an internal string within the old buffer, then it became ++ * dangling and needs to be reconstructed before the copy. ++ */ ++ if (old_data && old_data != (uintptr_t)set->strs_data && ++ old_s >= old_data && old_s < old_data + old_data_len) ++ s = set->strs_data + (old_s - old_data); ++ ++ memcpy(p, s, len); ++ ++ return len; ++} ++ + /* Find string offset that corresponds to a given string *s*. + * Returns: + * - >0 offset into string data, if string is found; +@@ -116,16 +151,12 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) + int strset__find_str(struct strset *set, const char *s) + { + long old_off, new_off, len; +- void *p; + +- /* see strset__add_str() for why we do this */ +- len = strlen(s) + 1; +- p = strset_add_str_mem(set, len); +- if (!p) +- return -ENOMEM; ++ len = strset_str_append(set, s); ++ if (len < 0) ++ return len; + + new_off = set->strs_data_len; +- memcpy(p, s, len); + + if (hashmap__find(set->strs_hash, new_off, &old_off)) + return old_off; +@@ -142,24 +173,13 @@ int strset__find_str(struct strset *set, const char *s) + int strset__add_str(struct strset *set, const char *s) + { + long old_off, new_off, len; +- void *p; + int err; + +- /* Hashmap keys are always offsets within set->strs_data, so to even +- * look up some string from the "outside", we need to first append it +- * at the end, so that it can be addressed with an offset. Luckily, +- * until set->strs_data_len is incremented, that string is just a piece +- * of garbage for the rest of the code, so no harm, no foul. On the +- * other hand, if the string is unique, it's already appended and +- * ready to be used, only a simple set->strs_data_len increment away. +- */ +- len = strlen(s) + 1; +- p = strset_add_str_mem(set, len); +- if (!p) +- return -ENOMEM; ++ len = strset_str_append(set, s); ++ if (len < 0) ++ return len; + + new_off = set->strs_data_len; +- memcpy(p, s, len); + + /* Now attempt to add the string, but only if the string with the same + * contents doesn't exist already (HASHMAP_ADD strategy). If such diff --git a/device/community/linux-pine64-pinephonepro/config-pine64-pinephonepro.aarch64 b/device/community/linux-pine64-pinephonepro/config-pine64-pinephonepro.aarch64 index 3480231e2..99778186d 100644 --- a/device/community/linux-pine64-pinephonepro/config-pine64-pinephonepro.aarch64 +++ b/device/community/linux-pine64-pinephonepro/config-pine64-pinephonepro.aarch64 @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 7.0.0 Kernel Configuration +# Linux/arm64 7.1.0 Kernel Configuration # CONFIG_CC_VERSION_TEXT="Alpine clang version 22.1.8" CONFIG_GCC_VERSION=0 @@ -13,6 +13,7 @@ CONFIG_LD_IS_LLD=y CONFIG_LLD_VERSION=220108 CONFIG_RUSTC_VERSION=0 CONFIG_RUSTC_LLVM_VERSION=0 +CONFIG_RUSTC_LLVM_MAJOR_VERSION=0 CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y CONFIG_TOOLS_SUPPORT_RELR=y @@ -186,6 +187,7 @@ CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y CONFIG_CC_HAS_INT128=y CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough" +CONFIG_CC_MS_EXTENSIONS="-fms-extensions" CONFIG_GCC10_NO_ARRAY_BOUNDS=y CONFIG_GCC_NO_STRINGOP_OVERFLOW=y CONFIG_ARCH_SUPPORTS_INT128=y @@ -224,6 +226,7 @@ CONFIG_SOCK_CGROUP_DATA=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y CONFIG_TIME_NS=y +CONFIG_TIME_NS_VDSO=y CONFIG_IPC_NS=y CONFIG_USER_NS=y CONFIG_PID_NS=y @@ -312,6 +315,7 @@ CONFIG_VMCORE_INFO=y # end of General setup CONFIG_ARM64=y +CONFIG_RUSTC_SUPPORTS_ARM64=y CONFIG_CLANG_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y CONFIG_64BIT=y CONFIG_MMU=y @@ -360,14 +364,7 @@ CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y # CONFIG_ARCH_KEEMBAY is not set # CONFIG_ARCH_MEDIATEK is not set # CONFIG_ARCH_MESON is not set - -# -# Microchip SoC support -# -# CONFIG_ARCH_LAN969X is not set -# CONFIG_ARCH_SPARX5 is not set -# end of Microchip SoC support - +# CONFIG_ARCH_MICROCHIP is not set # CONFIG_ARCH_MMP is not set # CONFIG_ARCH_MVEBU is not set # CONFIG_ARCH_NXP is not set @@ -437,6 +434,7 @@ CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD=y CONFIG_ARM64_ERRATUM_3117295=y CONFIG_ARM64_ERRATUM_3194386=y CONFIG_ARM64_ERRATUM_4311569=y +CONFIG_ARM64_ERRATUM_4193714=y # CONFIG_CAVIUM_ERRATUM_22375 is not set # CONFIG_CAVIUM_ERRATUM_23144 is not set # CONFIG_CAVIUM_ERRATUM_23154 is not set @@ -567,6 +565,14 @@ CONFIG_ARCH_PKEY_BITS=3 CONFIG_ARM64_GCS=y # end of ARMv9.4 architectural features +CONFIG_AS_HAS_LSUI=y + +# +# ARMv9.6 architectural features +# +CONFIG_ARM64_LSUI=y +# end of ARMv9.6 architectural features + CONFIG_ARM64_SVE=y CONFIG_ARM64_SME=y # CONFIG_ARM64_PSEUDO_NMI is not set @@ -721,8 +727,8 @@ CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y CONFIG_KVM_GUEST_MEMFD=y CONFIG_VIRTUALIZATION=y CONFIG_KVM=y -# CONFIG_NVHE_EL2_DEBUG is not set # CONFIG_PTDUMP_STAGE2_DEBUGFS is not set +# CONFIG_NVHE_EL2_DEBUG is not set CONFIG_HAVE_LIVEPATCH=y # CONFIG_LIVEPATCH is not set CONFIG_CPU_MITIGATIONS=y @@ -767,6 +773,7 @@ CONFIG_ARCH_WANTS_NO_INSTR=y CONFIG_HAVE_ASM_MODVERSIONS=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_RUST=y CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y CONFIG_HAVE_HW_BREAKPOINT=y CONFIG_HAVE_PERF_REGS=y @@ -801,6 +808,7 @@ CONFIG_ARCH_SUPPORTS_CFI=y CONFIG_CFI=y CONFIG_CFI_ICALL_NORMALIZE_INTEGERS=y CONFIG_HAVE_CFI_ICALL_NORMALIZE_INTEGERS=y +CONFIG_HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC=y # CONFIG_CFI_PERMISSIVE is not set CONFIG_HAVE_CONTEXT_TRACKING_USER=y CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y @@ -873,9 +881,11 @@ CONFIG_HAVE_GCC_PLUGINS=y CONFIG_FUNCTION_ALIGNMENT_4B=y CONFIG_FUNCTION_ALIGNMENT=4 CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT=y +CONFIG_HAVE_ARCH_GET_SECUREBOOT=y # end of General architecture-dependent options CONFIG_RT_MUTEXES=y +CONFIG_MODULE_SIG_FORMAT=y CONFIG_MODULES=y # CONFIG_MODULE_DEBUG is not set CONFIG_MODULE_FORCE_LOAD=y @@ -883,7 +893,16 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODULE_UNLOAD_TAINT_TRACKING is not set # CONFIG_MODULE_SRCVERSION_ALL is not set -# CONFIG_MODULE_SIG is not set +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_MODULE_SIG_ALL=y +# CONFIG_MODULE_SIG_SHA256 is not set +# CONFIG_MODULE_SIG_SHA384 is not set +CONFIG_MODULE_SIG_SHA512=y +# CONFIG_MODULE_SIG_SHA3_256 is not set +# CONFIG_MODULE_SIG_SHA3_384 is not set +# CONFIG_MODULE_SIG_SHA3_512 is not set +CONFIG_MODULE_SIG_HASH="sha512" CONFIG_MODULE_COMPRESS=y # CONFIG_MODULE_COMPRESS_GZIP is not set # CONFIG_MODULE_COMPRESS_XZ is not set @@ -1038,7 +1057,6 @@ CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_NUMA_KEEP_MEMINFO=y CONFIG_MEMORY_ISOLATION=y CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MHP_DEFAULT_ONLINE_TYPE_OFFLINE=y # CONFIG_MHP_DEFAULT_ONLINE_TYPE_ONLINE_AUTO is not set @@ -1053,6 +1071,7 @@ CONFIG_SPLIT_PMD_PTLOCKS=y CONFIG_COMPACTION=y CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 # CONFIG_PAGE_REPORTING is not set +CONFIG_NUMA_MIGRATION=y CONFIG_MIGRATION=y CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y CONFIG_ARCH_ENABLE_THP_MIGRATION=y @@ -1309,7 +1328,6 @@ CONFIG_NF_CONNTRACK_PROCFS=y # CONFIG_NF_CONNTRACK_TIMESTAMP is not set # CONFIG_NF_CONNTRACK_LABELS is not set CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y # CONFIG_NF_CONNTRACK_AMANDA is not set # CONFIG_NF_CONNTRACK_FTP is not set # CONFIG_NF_CONNTRACK_H323 is not set @@ -1734,7 +1752,6 @@ CONFIG_NET_DROP_MONITOR=m # end of Network testing # end of Networking options -# CONFIG_HAMRADIO is not set # CONFIG_CAN is not set CONFIG_BT=m CONFIG_BT_BREDR=y @@ -1828,7 +1845,6 @@ CONFIG_RFKILL_LEDS=y CONFIG_RFKILL_INPUT=y CONFIG_RFKILL_GPIO=m # CONFIG_NET_9P is not set -# CONFIG_CAIF is not set # CONFIG_CEPH_LIB is not set # CONFIG_NFC is not set CONFIG_PSAMPLE=m @@ -1864,6 +1880,7 @@ CONFIG_UEVENT_HELPER_PATH="" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y # CONFIG_DEVTMPFS_SAFE is not set +CONFIG_DRIVER_DEFERRED_PROBE_TIMEOUT=10 CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y @@ -1911,8 +1928,6 @@ CONFIG_GENERIC_ARCH_NUMA=y CONFIG_ARM_CCI=y CONFIG_ARM_CCI400_COMMON=y # CONFIG_ARM_INTEGRATOR_LM is not set -# CONFIG_BT1_APB is not set -# CONFIG_BT1_AXI is not set # CONFIG_MOXTET is not set # CONFIG_HISILICON_LPC is not set # CONFIG_IMX_WEIM is not set @@ -2060,6 +2075,7 @@ CONFIG_MTD_BLOCK=y # CONFIG_MTD_OOPS is not set # CONFIG_MTD_SWAP is not set CONFIG_MTD_PARTITIONED_MASTER=y +# CONFIG_MTD_VIRT_CONCAT is not set # # RAM/ROM/Flash chip drivers @@ -2425,7 +2441,6 @@ CONFIG_NET_VENDOR_FREESCALE=y # CONFIG_FSL_PQ_MDIO is not set # CONFIG_FSL_XGMAC_MDIO is not set # CONFIG_GIANFAR is not set -# CONFIG_FSL_DPAA2_SWITCH is not set # CONFIG_FSL_ENETC_IERB is not set # CONFIG_NXP_NETC_BLK_CTRL is not set # CONFIG_NET_VENDOR_FUNGIBLE is not set @@ -2473,7 +2488,6 @@ CONFIG_NET_VENDOR_VERTEXCOM=y # CONFIG_NET_VENDOR_WANGXUN is not set # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set -CONFIG_MDIO_BUS=y CONFIG_PHYLINK=m CONFIG_PHYLIB=y CONFIG_SWPHY=y @@ -2561,6 +2575,7 @@ CONFIG_OF_MDIO=y # CONFIG_MDIO_MSCC_MIIM is not set # CONFIG_MDIO_MOXART is not set # CONFIG_MDIO_OCTEON is not set +# CONFIG_MDIO_PIC64HPSC is not set # CONFIG_MDIO_IPQ4019 is not set # CONFIG_MDIO_IPQ8064 is not set # CONFIG_MDIO_REALTEK_RTL9300 is not set @@ -2682,7 +2697,6 @@ CONFIG_MAC80211_HWSIM=m # CONFIG_NETDEVSIM is not set CONFIG_NET_FAILOVER=m -# CONFIG_ISDN is not set # # Input device support @@ -2722,6 +2736,7 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_KEYBOARD_GPIO_POLLED=m # CONFIG_KEYBOARD_TCA8418 is not set CONFIG_KEYBOARD_MATRIX=m +# CONFIG_KEYBOARD_CHARLIEPLEX is not set # CONFIG_KEYBOARD_LM8323 is not set # CONFIG_KEYBOARD_LM8333 is not set # CONFIG_KEYBOARD_LPC32XX is not set @@ -3040,6 +3055,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y # CONFIG_SERIAL_HS_LPC32XX is not set CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_PIC32 is not set # CONFIG_SERIAL_MSM is not set # CONFIG_SERIAL_VT8500 is not set # CONFIG_SERIAL_OMAP is not set @@ -3068,8 +3084,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_LITEUART is not set # CONFIG_SERIAL_SUNPLUS is not set # CONFIG_SERIAL_NUVOTON_MA35D1 is not set -# CONFIG_SERIAL_ESP32 is not set -# CONFIG_SERIAL_ESP32_ACM is not set # end of Serial drivers CONFIG_SERIAL_MCTRL_GPIO=y @@ -3289,6 +3303,7 @@ CONFIG_SPI_MEM=y # CONFIG_SPI_AMLOGIC_SPISG is not set # CONFIG_SPI_APPLE is not set # CONFIG_SPI_AR934X is not set +# CONFIG_SPI_ATCSPI200 is not set # CONFIG_SPI_ATH79 is not set # CONFIG_SPI_ARMADA_3700 is not set # CONFIG_SPI_ASPEED_SMC is not set @@ -3475,6 +3490,7 @@ CONFIG_GENERIC_PINCONF=y # CONFIG_PINCTRL_MCP23S08 is not set # CONFIG_PINCTRL_MICROCHIP_SGPIO is not set # CONFIG_PINCTRL_OCELOT is not set +# CONFIG_PINCTRL_PIC32 is not set # CONFIG_PINCTRL_PISTACHIO is not set # CONFIG_PINCTRL_RK805 is not set CONFIG_PINCTRL_ROCKCHIP=y @@ -3721,6 +3737,7 @@ CONFIG_GPIO_GENERIC=y # CONFIG_GPIO_BCM_XGS_IPROC is not set # CONFIG_GPIO_BLZP1600 is not set # CONFIG_GPIO_BRCMSTB is not set +# CONFIG_GPIO_BY_PINCTRL is not set # CONFIG_GPIO_CADENCE is not set # CONFIG_GPIO_CLPS711X is not set # CONFIG_GPIO_DAVINCI is not set @@ -3870,6 +3887,7 @@ CONFIG_REBOOT_MODE=m CONFIG_SYSCON_REBOOT_MODE=m # CONFIG_POWER_RESET_SC27XX is not set CONFIG_NVMEM_REBOOT_MODE=m +# CONFIG_POWER_RESET_QEMU_VIRT_CTRL is not set # CONFIG_POWER_SEQUENCING is not set CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set @@ -3886,6 +3904,7 @@ CONFIG_IP5XXX_POWER=y # CONFIG_BATTERY_DS2782 is not set # CONFIG_BATTERY_LEGO_EV3 is not set # CONFIG_BATTERY_SAMSUNG_SDI is not set +# CONFIG_BATTERY_S2MU005 is not set # CONFIG_BATTERY_SBS is not set # CONFIG_CHARGER_SBS is not set # CONFIG_MANAGER_SBS is not set @@ -3966,7 +3985,6 @@ CONFIG_SENSORS_ARM_SCPI=m # CONFIG_SENSORS_ASPEED is not set # CONFIG_SENSORS_ASPEED_G6 is not set # CONFIG_SENSORS_ATXP1 is not set -# CONFIG_SENSORS_BT1_PVT is not set # CONFIG_SENSORS_CHIPCAP2 is not set # CONFIG_SENSORS_CORSAIR_CPRO is not set # CONFIG_SENSORS_CORSAIR_PSU is not set @@ -4027,6 +4045,7 @@ CONFIG_SENSORS_JC42=m # CONFIG_SENSORS_MAX31790 is not set # CONFIG_SENSORS_MC34VR500 is not set # CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_MCP9982 is not set # CONFIG_SENSORS_TC654 is not set # CONFIG_SENSORS_TPS23861 is not set # CONFIG_SENSORS_MR75203 is not set @@ -4986,6 +5005,7 @@ CONFIG_VIDEO_IMX258=m # CONFIG_VIDEO_OV2659 is not set # CONFIG_VIDEO_OV2680 is not set # CONFIG_VIDEO_OV2685 is not set +# CONFIG_VIDEO_OV2732 is not set # CONFIG_VIDEO_OV2735 is not set # CONFIG_VIDEO_OV2740 is not set # CONFIG_VIDEO_OV4689 is not set @@ -5019,6 +5039,7 @@ CONFIG_VIDEO_OV8858=m # CONFIG_VIDEO_S5K5BAF is not set # CONFIG_VIDEO_S5K6A3 is not set # CONFIG_VIDEO_S5KJN1 is not set +# CONFIG_VIDEO_T4KA3 is not set # CONFIG_VIDEO_VD55G1 is not set # CONFIG_VIDEO_VD56G3 is not set # CONFIG_VIDEO_VGXY61 is not set @@ -5188,6 +5209,7 @@ CONFIG_DRM=y CONFIG_DRM_MIPI_DSI=y CONFIG_DRM_KMS_HELPER=y # CONFIG_DRM_PANIC is not set +# CONFIG_DRM_RAS is not set # CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set # CONFIG_DRM_DEBUG_MODESET_LOCK is not set CONFIG_DRM_CLIENT=y @@ -5235,6 +5257,7 @@ CONFIG_DRM_SCHED=y # CONFIG_DRM_ATMEL_HLCDC is not set CONFIG_DRM_BRIDGE=y CONFIG_DRM_PANEL_BRIDGE=y +CONFIG_DRM_AUX_HPD_BRIDGE=y # # Display Interface Bridges @@ -5252,6 +5275,7 @@ CONFIG_DRM_INNO_HDMI=y # CONFIG_DRM_LONTIUM_LT9211 is not set # CONFIG_DRM_LONTIUM_LT9611 is not set # CONFIG_DRM_LONTIUM_LT9611UXC is not set +# CONFIG_DRM_LONTIUM_LT8713SX is not set # CONFIG_DRM_ITE_IT66121 is not set # CONFIG_DRM_LVDS_CODEC is not set # CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set @@ -5266,6 +5290,7 @@ CONFIG_DRM_INNO_HDMI=y CONFIG_DRM_SIMPLE_BRIDGE=y # CONFIG_DRM_SOLOMON_SSD2825 is not set # CONFIG_DRM_THINE_THC63LVD1024 is not set +# CONFIG_DRM_THEAD_TH1520_DW_HDMI is not set # CONFIG_DRM_TOSHIBA_TC358762 is not set # CONFIG_DRM_TOSHIBA_TC358764 is not set # CONFIG_DRM_TOSHIBA_TC358767 is not set @@ -5345,12 +5370,14 @@ CONFIG_DRM_PANEL=y # CONFIG_DRM_PANEL_HIMAX_HX83102 is not set # CONFIG_DRM_PANEL_HIMAX_HX83112A is not set # CONFIG_DRM_PANEL_HIMAX_HX83112B is not set +# CONFIG_DRM_PANEL_HIMAX_HX83121A is not set CONFIG_DRM_PANEL_HIMAX_HX8394=y # CONFIG_DRM_PANEL_HYDIS_HV101HD1 is not set # CONFIG_DRM_PANEL_ILITEK_IL9322 is not set # CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set # CONFIG_DRM_PANEL_ILITEK_ILI9805 is not set -# CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9806E_SPI is not set # CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set # CONFIG_DRM_PANEL_ILITEK_ILI9882T is not set # CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set @@ -5368,8 +5395,10 @@ CONFIG_DRM_PANEL_HIMAX_HX8394=y # CONFIG_DRM_PANEL_LG_LD070WX3 is not set # CONFIG_DRM_PANEL_LG_LG4573 is not set # CONFIG_DRM_PANEL_LG_SW43408 is not set +# CONFIG_DRM_PANEL_LXD_M9189A is not set # CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966 is not set # CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set +# CONFIG_DRM_PANEL_MOTOROLA_MOT is not set # CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set # CONFIG_DRM_PANEL_NEWVISION_NV3051D is not set # CONFIG_DRM_PANEL_NEWVISION_NV3052C is not set @@ -5379,6 +5408,7 @@ CONFIG_DRM_PANEL_HIMAX_HX8394=y # CONFIG_DRM_PANEL_NOVATEK_NT36523 is not set # CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set # CONFIG_DRM_PANEL_NOVATEK_NT36672E is not set +# CONFIG_DRM_PANEL_NOVATEK_NT37700F is not set # CONFIG_DRM_PANEL_NOVATEK_NT37801 is not set # CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set # CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set @@ -5414,6 +5444,7 @@ CONFIG_DRM_PANEL_HIMAX_HX8394=y # CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E8FC0 is not set # CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set # CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set # CONFIG_DRM_PANEL_SHARP_LQ079L1SX01 is not set @@ -5508,6 +5539,7 @@ CONFIG_DRM_SIMPLEDRM=y CONFIG_DRM_UDL=m # CONFIG_DRM_V3D is not set # CONFIG_DRM_VC4 is not set +# CONFIG_DRM_VERISILICON_DC is not set CONFIG_DRM_VGEM=m # CONFIG_DRM_VKMS is not set CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y @@ -6174,6 +6206,7 @@ CONFIG_SND_SOC_SPDIF=m # CONFIG_SND_SOC_TSCS454 is not set # CONFIG_SND_SOC_UDA1334 is not set # CONFIG_SND_SOC_UDA1342 is not set +# CONFIG_SND_SOC_UDA1380 is not set # CONFIG_SND_SOC_WM8510 is not set # CONFIG_SND_SOC_WM8523 is not set # CONFIG_SND_SOC_WM8524 is not set @@ -6303,6 +6336,8 @@ CONFIG_HID_KENSINGTON=m CONFIG_HID_LCPOWER=m CONFIG_HID_LED=m # CONFIG_HID_LENOVO is not set +# CONFIG_HID_LENOVO_GO is not set +# CONFIG_HID_LENOVO_GO_S is not set CONFIG_HID_LETSKETCH=m CONFIG_HID_LOGITECH=m CONFIG_HID_LOGITECH_DJ=m @@ -6381,6 +6416,7 @@ CONFIG_HID_SENSOR_CUSTOM_SENSOR=m CONFIG_HID_ALPS=m # CONFIG_HID_MCP2200 is not set CONFIG_HID_MCP2221=m +# CONFIG_HID_HUAWEI is not set # end of Special HID drivers # @@ -6554,6 +6590,7 @@ CONFIG_USB_DWC3_OF_SIMPLE=y CONFIG_USB_DWC3_ST=y CONFIG_USB_DWC3_QCOM=y CONFIG_USB_DWC3_IMX8MP=y +CONFIG_USB_DWC3_IMX=y CONFIG_USB_DWC3_XILINX=y CONFIG_USB_DWC3_AM62=y CONFIG_USB_DWC3_OCTEON=y @@ -6784,7 +6821,6 @@ CONFIG_TYPEC_UCSI=y # CONFIG_TYPEC_HD3SS3220 is not set # CONFIG_TYPEC_STUSB160X is not set # CONFIG_TYPEC_WUSB3801 is not set -CONFIG_TYPEC_EXTCON=y # # USB Type-C Multiplexer/DeMultiplexer Switch support @@ -6844,6 +6880,7 @@ CONFIG_MMC_SDHCI_OF_ARASAN=y # CONFIG_MMC_SDHCI_PXAV2 is not set # CONFIG_MMC_SDHCI_SPEAR is not set # CONFIG_MMC_SDHCI_BCM_KONA is not set +# CONFIG_MMC_SDHCI_BST is not set # CONFIG_MMC_SDHCI_F_SDH30 is not set # CONFIG_MMC_SDHCI_MILBEAUT is not set # CONFIG_MMC_SDHCI_IPROC is not set @@ -6878,6 +6915,7 @@ CONFIG_MMC_CQHCI=y # CONFIG_MMC_HSQ is not set # CONFIG_MMC_BCM2835 is not set # CONFIG_MMC_MTK is not set +# CONFIG_MMC_SDHCI_MICROCHIP_PIC32 is not set # CONFIG_MMC_SDHCI_BRCMSTB is not set # CONFIG_MMC_SDHCI_XENON is not set # CONFIG_MMC_SDHCI_OMAP is not set @@ -7070,6 +7108,7 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_NVIDIA_VRS10 is not set # CONFIG_RTC_DRV_NCT3018Y is not set CONFIG_RTC_DRV_RK808=y +# CONFIG_RTC_DRV_ROCKCHIP is not set # CONFIG_RTC_DRV_RS5C372 is not set # CONFIG_RTC_DRV_ISL1208 is not set # CONFIG_RTC_DRV_ISL12022 is not set @@ -7189,6 +7228,7 @@ CONFIG_RTC_DRV_CADENCE=m # CONFIG_RTC_DRV_MT6397 is not set # CONFIG_RTC_DRV_MT7622 is not set # CONFIG_RTC_DRV_XGENE is not set +CONFIG_RTC_DRV_PIC32=y # CONFIG_RTC_DRV_R7301 is not set # CONFIG_RTC_DRV_STM32 is not set # CONFIG_RTC_DRV_RTD119X is not set @@ -7231,8 +7271,6 @@ CONFIG_DMA_OF=y # CONFIG_IMG_MDC_DMA is not set # CONFIG_INTEL_IDMA64 is not set # CONFIG_K3_DMA is not set -# CONFIG_LOONGSON1_APB_DMA is not set -# CONFIG_LOONGSON2_APB_DMA is not set # CONFIG_MCF_EDMA is not set # CONFIG_MILBEAUT_HDMAC is not set # CONFIG_MILBEAUT_XDMAC is not set @@ -7271,6 +7309,9 @@ CONFIG_PL330_DMA=m # CONFIG_TI_EDMA is not set # CONFIG_DMA_OMAP is not set # CONFIG_INTEL_LDMA is not set +# CONFIG_LOONGSON1_APB_DMA is not set +# CONFIG_LOONGSON2_APB_DMA is not set +# CONFIG_LOONGSON2_APB_CMC_DMA is not set # CONFIG_STM32_DMA is not set # CONFIG_STM32_MDMA is not set # CONFIG_STM32_DMA3 is not set @@ -7287,7 +7328,6 @@ CONFIG_ASYNC_TX_DMA=y CONFIG_SYNC_FILE=y # CONFIG_SW_SYNC is not set CONFIG_UDMABUF=y -CONFIG_DMABUF_MOVE_NOTIFY=y # CONFIG_DMABUF_DEBUG is not set # CONFIG_DMABUF_SELFTESTS is not set CONFIG_DMABUF_HEAPS=y @@ -7303,7 +7343,6 @@ CONFIG_IRQ_BYPASS_MANAGER=y CONFIG_VIRT_DRIVERS=y CONFIG_VMGENID=y # CONFIG_EFI_SECRET is not set -# CONFIG_ARM_PKVM_GUEST is not set # CONFIG_ARM_CCA_GUEST is not set CONFIG_VIRTIO_ANCHOR=y CONFIG_VIRTIO=m @@ -7363,11 +7402,6 @@ CONFIG_STAGING=y CONFIG_STAGING_MEDIA=y # CONFIG_VIDEO_MAX96712 is not set # CONFIG_VIDEO_MESON_VDEC is not set - -# -# StarFive media platform drivers -# -# CONFIG_VIDEO_STARFIVE_CAMSS is not set # CONFIG_VIDEO_SUNXI is not set # CONFIG_STAGING_MEDIA_DEPRECATED is not set # CONFIG_FB_TFT is not set @@ -7444,7 +7478,6 @@ CONFIG_COMMON_CLK_PWM=y # CONFIG_CLK_ACTIONS is not set # CONFIG_COMMON_CLK_ASPEED is not set # CONFIG_COMMON_CLK_AST2700 is not set -# CONFIG_CLK_BAIKAL_T1 is not set # CONFIG_CLK_BCM2711_DVP is not set # CONFIG_CLK_BCM2835 is not set # CONFIG_CLK_BCM_63XX is not set @@ -7457,6 +7490,7 @@ CONFIG_COMMON_CLK_PWM=y # CONFIG_CLK_BCM_NS2 is not set # CONFIG_CLK_BCM_SR is not set # CONFIG_CLK_RASPBERRYPI is not set +# CONFIG_COMMON_CLK_EIC7700 is not set # CONFIG_COMMON_CLK_HI3516CV300 is not set # CONFIG_COMMON_CLK_HI3519 is not set # CONFIG_COMMON_CLK_HI3559A is not set @@ -7554,6 +7588,7 @@ CONFIG_MCHP_CLK_MPFS=y # CONFIG_CLK_RENESAS is not set CONFIG_COMMON_CLK_ROCKCHIP=y # CONFIG_CLK_PX30 is not set +CONFIG_CLK_RV1103B=y CONFIG_CLK_RV110X=y CONFIG_CLK_RV1126=y CONFIG_CLK_RV1126B=y @@ -7594,6 +7629,7 @@ CONFIG_CLK_RK3588=y # CONFIG_CLK_STARFIVE_JH7110_SYS is not set # CONFIG_CLK_SUNXI is not set # CONFIG_SUNXI_CCU is not set +# CONFIG_TENSTORRENT_ATLANTIS_PRCM is not set # CONFIG_CLK_THEAD_TH1520_AP is not set # CONFIG_COMMON_CLK_STM32MP is not set # CONFIG_COMMON_CLK_TI_ADPLL is not set @@ -7609,7 +7645,6 @@ CONFIG_HWSPINLOCK=y # CONFIG_HWSPINLOCK_SPRD is not set # CONFIG_HWSPINLOCK_STM32 is not set # CONFIG_HWSPINLOCK_SUN6I is not set -# CONFIG_HSEM_U8500 is not set # # Clock Source drivers @@ -7752,6 +7787,7 @@ CONFIG_ARM_SMMU_V3=y # CONFIG_TEGRA241_CMDQV is not set # CONFIG_QCOM_IOMMU is not set # CONFIG_IOMMUFD is not set +# CONFIG_RISCV_IOMMU is not set # CONFIG_OMAP_IOMMU is not set CONFIG_ROCKCHIP_IOMMU=y # CONFIG_SUN50I_IOMMU is not set @@ -7893,6 +7929,7 @@ CONFIG_ROCKCHIP_IOMMU=y # CONFIG_SOC_RENESAS is not set CONFIG_ROCKCHIP_GRF=y CONFIG_ROCKCHIP_IODOMAIN=y +# CONFIG_ROCKCHIP_NPOR_POWERGOOD is not set # CONFIG_SOC_SAMSUNG is not set # @@ -7969,6 +8006,7 @@ CONFIG_ARM_SCPI_POWER_DOMAIN=m # # Qualcomm PM Domains # +# CONFIG_QCOM_CPR is not set # end of Qualcomm PM Domains CONFIG_ROCKCHIP_PM_DOMAINS=y @@ -8027,7 +8065,6 @@ CONFIG_MEMORY=y # CONFIG_ATMEL_EBI is not set # CONFIG_BRCMSTB_DPFE is not set # CONFIG_BRCMSTB_MEMC is not set -# CONFIG_BT1_L2_CTL is not set # CONFIG_TI_AEMIF is not set # CONFIG_TI_EMIF is not set # CONFIG_OMAP_GPMC is not set @@ -8199,6 +8236,7 @@ CONFIG_HID_SENSOR_ACCEL_3D=m # CONFIG_QCOM_SPMI_IADC is not set # CONFIG_QCOM_SPMI_VADC is not set # CONFIG_QCOM_SPMI_ADC5 is not set +# CONFIG_QCOM_SPMI_ADC5_GEN3 is not set # CONFIG_RCAR_GYRO_ADC is not set # CONFIG_ROHM_BD79112 is not set # CONFIG_ROHM_BD79124 is not set @@ -8697,6 +8735,7 @@ CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE=m # CONFIG_SRF08 is not set # CONFIG_VCNL3020 is not set # CONFIG_VL53L0X_I2C is not set +# CONFIG_VL53L1X_I2C is not set # CONFIG_AW96103 is not set # end of Proximity and distance sensors @@ -8810,6 +8849,7 @@ CONFIG_IRQ_MSI_LIB=y # CONFIG_BCM7120_L2_IRQ is not set # CONFIG_BRCMSTB_L2_IRQ is not set # CONFIG_LAN966X_OIC is not set +CONFIG_PIC32_EVIC=y # CONFIG_JCORE_AIC is not set # CONFIG_RENESAS_INTC_IRQPIN is not set # CONFIG_RENESAS_IRQC is not set @@ -8869,9 +8909,11 @@ CONFIG_RESET_POLARFIRE_SOC=y # CONFIG_RESET_RZV2H_USB2PHY is not set CONFIG_RESET_SCMI=y # CONFIG_RESET_SIMPLE is not set +# CONFIG_RESET_SKY1 is not set # CONFIG_RESET_SOCFPGA is not set # CONFIG_RESET_SUNPLUS is not set # CONFIG_RESET_SUNXI is not set +# CONFIG_RESET_TENSTORRENT_ATLANTIS is not set # CONFIG_RESET_TH1520 is not set # CONFIG_RESET_TI_SCI is not set # CONFIG_RESET_TI_SYSCON is not set @@ -8901,16 +8943,15 @@ CONFIG_RESET_SCMI=y # CONFIG_GENERIC_PHY=y CONFIG_GENERIC_PHY_MIPI_DPHY=y +# CONFIG_PHY_AIROHA_PCIE is not set +# CONFIG_PHY_CAN_TRANSCEIVER is not set # CONFIG_PHY_GOOGLE_USB is not set +# CONFIG_USB_LGM_PHY is not set # CONFIG_PHY_LPC18XX_USB_OTG is not set +# CONFIG_PHY_NXP_PTN3222 is not set # CONFIG_PHY_PISTACHIO_USB is not set # CONFIG_PHY_SNPS_EUSB2 is not set # CONFIG_PHY_XGENE is not set -# CONFIG_USB_LGM_PHY is not set -# CONFIG_PHY_CAN_TRANSCEIVER is not set -# CONFIG_PHY_AIROHA_PCIE is not set -# CONFIG_PHY_NXP_PTN3222 is not set -# CONFIG_PHY_SPACEMIT_K1_PCIE is not set # CONFIG_PHY_SUN4I_USB is not set # CONFIG_PHY_SUN6I_MIPI_DPHY is not set # CONFIG_PHY_SUN9I_USB is not set @@ -8947,6 +8988,8 @@ CONFIG_GENERIC_PHY_MIPI_DPHY=y # CONFIG_PHY_CADENCE_DPHY_RX is not set # CONFIG_PHY_CADENCE_SIERRA is not set # CONFIG_PHY_CADENCE_SALVO is not set +# CONFIG_PHY_CANAAN_USB is not set +# CONFIG_PHY_EIC7700_SATA is not set # CONFIG_PHY_FSL_IMX8MQ_USB is not set # CONFIG_PHY_MIXEL_LVDS_PHY is not set # CONFIG_PHY_MIXEL_MIPI_DPHY is not set @@ -8961,6 +9004,10 @@ CONFIG_GENERIC_PHY_MIPI_DPHY=y # CONFIG_PHY_HISTB_COMBPHY is not set # CONFIG_PHY_HISI_INNO_USB2 is not set # CONFIG_PHY_INGENIC_USB is not set +# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set +# CONFIG_PHY_INTEL_KEEMBAY_USB is not set +# CONFIG_PHY_INTEL_LGM_COMBO is not set +# CONFIG_PHY_INTEL_LGM_EMMC is not set # CONFIG_PHY_LANTIQ_VRX200_PCIE is not set # CONFIG_PHY_LANTIQ_RCU_USB2 is not set # CONFIG_ARMADA375_USBCLUSTER_PHY is not set @@ -9040,6 +9087,7 @@ CONFIG_PHY_ROCKCHIP_USB=y # CONFIG_PHY_UNIPHIER_PCIE is not set # CONFIG_PHY_UNIPHIER_AHCI is not set # CONFIG_PHY_SOPHGO_CV1800_USB2 is not set +# CONFIG_PHY_SPACEMIT_K1_PCIE is not set # CONFIG_PHY_SPACEMIT_K1_USB2 is not set # CONFIG_PHY_ST_SPEAR1310_MIPHY is not set # CONFIG_PHY_ST_SPEAR1340_MIPHY is not set @@ -9060,10 +9108,6 @@ CONFIG_PHY_ROCKCHIP_USB=y # CONFIG_OMAP_USB2 is not set # CONFIG_TI_PIPE3 is not set # CONFIG_PHY_TUSB1210 is not set -# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set -# CONFIG_PHY_INTEL_KEEMBAY_USB is not set -# CONFIG_PHY_INTEL_LGM_COMBO is not set -# CONFIG_PHY_INTEL_LGM_EMMC is not set # CONFIG_PHY_XILINX_ZYNQMP is not set # end of PHY Subsystem @@ -9168,6 +9212,7 @@ CONFIG_NVMEM_LAYOUTS=y # CONFIG_FSI is not set # CONFIG_TEE is not set CONFIG_MULTIPLEXER=y +# CONFIG_MUX_CORE is not set # # Multiplexer drivers @@ -9309,8 +9354,8 @@ CONFIG_FAT_DEFAULT_IOCHARSET="ascii" CONFIG_FAT_DEFAULT_UTF8=y CONFIG_EXFAT_FS=m CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" -# CONFIG_NTFS3_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS3_FS is not set # end of DOS/FAT/EXFAT/NT Filesystems # @@ -9432,7 +9477,6 @@ CONFIG_NFSD_SCSILAYOUT=y CONFIG_NFSD_V4_2_INTER_SSC=y # CONFIG_NFSD_V4_SECURITY_LABEL is not set # CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set -# CONFIG_NFSD_V4_DELEG_TIMESTAMPS is not set # CONFIG_NFSD_V4_POSIX_ACLS is not set CONFIG_GRACE_PERIOD=m CONFIG_LOCKD=m @@ -9552,13 +9596,17 @@ CONFIG_SECURITY_PATH=y # CONFIG_SECURITY_LOADPIN is not set CONFIG_SECURITY_YAMA=y # CONFIG_SECURITY_SAFESETID is not set -# CONFIG_SECURITY_LOCKDOWN_LSM is not set +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y +CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y +# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set +# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set # CONFIG_SECURITY_LANDLOCK is not set # CONFIG_SECURITY_IPE is not set # CONFIG_INTEGRITY is not set # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,ipe,bpf" # # Kernel hardening options @@ -9602,7 +9650,6 @@ CONFIG_RANDSTRUCT_NONE=y # end of Kernel hardening options # end of Security options -CONFIG_XOR_BLOCKS=y CONFIG_ASYNC_CORE=m CONFIG_ASYNC_MEMCPY=m CONFIG_ASYNC_XOR=m @@ -9625,7 +9672,6 @@ CONFIG_CRYPTO_HASH=y CONFIG_CRYPTO_HASH2=y CONFIG_CRYPTO_RNG=y CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_RNG_DEFAULT=y CONFIG_CRYPTO_AKCIPHER2=y CONFIG_CRYPTO_AKCIPHER=y CONFIG_CRYPTO_KPP2=y @@ -9639,7 +9685,7 @@ CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_PCRYPT=m # CONFIG_CRYPTO_CRYPTD is not set CONFIG_CRYPTO_AUTHENC=y -# CONFIG_CRYPTO_KRB5ENC is not set +CONFIG_CRYPTO_KRB5ENC=y # CONFIG_CRYPTO_BENCHMARK is not set CONFIG_CRYPTO_ENGINE=m # end of Crypto core or helper @@ -9664,7 +9710,7 @@ CONFIG_CRYPTO_AES=y # CONFIG_CRYPTO_ARIA is not set CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_BLOWFISH_COMMON=m -CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAMELLIA=y CONFIG_CRYPTO_CAST_COMMON=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m @@ -9710,18 +9756,16 @@ CONFIG_CRYPTO_ESSIV=y # Hashes, digests, and MACs # CONFIG_CRYPTO_BLAKE2B=y -CONFIG_CRYPTO_CMAC=m -CONFIG_CRYPTO_GHASH=y +CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y -# CONFIG_CRYPTO_MICHAEL_MIC is not set CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_SHA3=y -# CONFIG_CRYPTO_SM3_GENERIC is not set +# CONFIG_CRYPTO_SM3 is not set CONFIG_CRYPTO_STREEBOG=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_XCBC=m @@ -9778,8 +9822,6 @@ CONFIG_CRYPTO_USER_API_AEAD=m # Accelerated Cryptographic Algorithms for CPU (arm64) # CONFIG_CRYPTO_GHASH_ARM64_CE=m -# CONFIG_CRYPTO_SM3_NEON is not set -CONFIG_CRYPTO_SM3_ARM64_CE=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_AES_ARM64_BS=m @@ -9836,6 +9878,12 @@ CONFIG_PKCS7_MESSAGE_PARSER=y # # Certificates for signature checking # +CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" +CONFIG_MODULE_SIG_KEY_TYPE_RSA=y +# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set +# CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_44 is not set +# CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_65 is not set +# CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_87 is not set CONFIG_SYSTEM_TRUSTED_KEYRING=y CONFIG_SYSTEM_TRUSTED_KEYS="" # CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set @@ -9848,7 +9896,8 @@ CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" CONFIG_OPENSSL_SUPPORTS_ML_DSA=y # end of Certificates for signature checking -# CONFIG_CRYPTO_KRB5 is not set +CONFIG_CRYPTO_KRB5=y +# CONFIG_CRYPTO_KRB5_SELFTESTS is not set CONFIG_BINARY_PRINTF=y # @@ -9880,23 +9929,23 @@ CONFIG_CRC_T10DIF_ARCH=y CONFIG_CRC32=y CONFIG_CRC32_ARCH=y CONFIG_CRC64=y +CONFIG_CRC64_ARCH=y CONFIG_CRC_OPTIMIZATIONS=y - -# -# Crypto library routines -# CONFIG_CRYPTO_HASH_INFO=y CONFIG_CRYPTO_LIB_UTILS=y CONFIG_CRYPTO_LIB_AES=y CONFIG_CRYPTO_LIB_AES_ARCH=y +CONFIG_CRYPTO_LIB_AES_CBC_MACS=y CONFIG_CRYPTO_LIB_ARC4=m -CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_GF128MUL=m CONFIG_CRYPTO_LIB_BLAKE2B=y CONFIG_CRYPTO_LIB_CHACHA=y CONFIG_CRYPTO_LIB_CHACHA_ARCH=y CONFIG_CRYPTO_LIB_CURVE25519=m CONFIG_CRYPTO_LIB_CURVE25519_GENERIC=y CONFIG_CRYPTO_LIB_DES=m +CONFIG_CRYPTO_LIB_GF128HASH=y +CONFIG_CRYPTO_LIB_GF128HASH_ARCH=y CONFIG_CRYPTO_LIB_MD5=y CONFIG_CRYPTO_LIB_NH=m CONFIG_CRYPTO_LIB_NH_ARCH=y @@ -9913,9 +9962,8 @@ CONFIG_CRYPTO_LIB_SHA512=y CONFIG_CRYPTO_LIB_SHA512_ARCH=y CONFIG_CRYPTO_LIB_SHA3=y CONFIG_CRYPTO_LIB_SHA3_ARCH=y -CONFIG_CRYPTO_LIB_SM3=y -# end of Crypto library routines - +CONFIG_XOR_BLOCKS=y +CONFIG_XOR_BLOCKS_ARCH=y CONFIG_XXHASH=y CONFIG_AUDIT_GENERIC=y CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y @@ -9972,6 +10020,7 @@ CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED=y +CONFIG_ARCH_HAS_BATCHED_DMA_SYNC=y CONFIG_SWIOTLB=y # CONFIG_SWIOTLB_DYNAMIC is not set CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y @@ -10327,6 +10376,7 @@ CONFIG_SYNTH_EVENTS=y # CONFIG_SYNTH_EVENT_GEN_TEST is not set # CONFIG_KPROBE_EVENT_GEN_TEST is not set # CONFIG_RV is not set +# CONFIG_TRACE_REMOTE_TEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_SAMPLE_FTRACE_DIRECT=y CONFIG_HAVE_SAMPLE_FTRACE_DIRECT_MULTI=y @@ -10373,3 +10423,4 @@ CONFIG_MEMTEST=y CONFIG_IO_URING_ZCRX=y CONFIG_IO_URING_BPF=y +CONFIG_IO_URING_BPF_OPS=y diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0001-ASoC-codecs-rt5640-Fix-output-mixer-input-channel-li.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0001-ASoC-codecs-rt5640-Fix-output-mixer-input-channel-li.patch new file mode 100644 index 000000000..c6c29cd60 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0001-ASoC-codecs-rt5640-Fix-output-mixer-input-channel-li.patch @@ -0,0 +1,93 @@ +From 0a07c82cce09160952c41a53f94acb084b8b6a3b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 19 Aug 2022 22:01:23 +0200 +Subject: [PATCH 01/84] ASoC: codecs: rt5640: Fix output mixer input channel + list + +It did not match the RT5640 datasheet. Now it does. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 14 +++++++------- + sound/soc/codecs/rt5640.h | 16 ++++++---------- + 2 files changed, 13 insertions(+), 17 deletions(-) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index f6c6294e1588..e3a90be5294b 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -651,8 +651,8 @@ static const struct snd_kcontrol_new rt5640_spk_r_mix[] = { + }; + + static const struct snd_kcontrol_new rt5640_out_l_mix[] = { +- SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER, +- RT5640_M_SM_L_OM_L_SFT, 1, 1), ++ SOC_DAPM_SINGLE("BST3 Switch", RT5640_OUT_L3_MIXER, ++ RT5640_M_BST3_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER, +@@ -668,10 +668,10 @@ static const struct snd_kcontrol_new rt5640_out_l_mix[] = { + }; + + static const struct snd_kcontrol_new rt5640_out_r_mix[] = { +- SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER, +- RT5640_M_SM_L_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER, +- RT5640_M_BST4_OM_R_SFT, 1, 1), ++ RT5640_M_BST2_OM_R_SFT, 1, 1), ++ SOC_DAPM_SINGLE("BST3 Switch", RT5640_OUT_R3_MIXER, ++ RT5640_M_BST3_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER, +@@ -1603,8 +1603,8 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { + {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, + {"SPK MIXR", "DAC R2 Switch", "DAC R2"}, + +- {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"}, +- {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"}, ++ {"OUT MIXL", "BST3 Switch", "BST3"}, ++ {"OUT MIXR", "BST3 Switch", "BST3"}, + + {"OUT MIXL", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXL", "DAC L2 Switch", "DAC L2"}, +diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h +index 8a12cee76bdc..5c5398c82dcd 100644 +--- a/sound/soc/codecs/rt5640.h ++++ b/sound/soc/codecs/rt5640.h +@@ -654,12 +654,8 @@ + #define RT5640_G_DAC_L1_OM_L_SFT 7 + + /* Output Left Mixer Control 3 (0x4f) */ +-#define RT5640_M_SM_L_OM_L (0x1 << 8) +-#define RT5640_M_SM_L_OM_L_SFT 8 +-#define RT5640_M_BST3_OM_L (0x1 << 7) +-#define RT5640_M_BST3_OM_L_SFT 7 +-#define RT5640_M_BST2_OM_L (0x1 << 6) +-#define RT5640_M_BST2_OM_L_SFT 6 ++#define RT5640_M_BST3_OM_L (0x1 << 6) ++#define RT5640_M_BST3_OM_L_SFT 6 + #define RT5640_M_BST1_OM_L (0x1 << 5) + #define RT5640_M_BST1_OM_L_SFT 5 + #define RT5640_M_IN_L_OM_L (0x1 << 4) +@@ -694,12 +690,12 @@ + #define RT5640_G_DAC_R1_OM_R_SFT 7 + + /* Output Right Mixer Control 3 (0x52) */ +-#define RT5640_M_SM_L_OM_R (0x1 << 8) +-#define RT5640_M_SM_L_OM_R_SFT 8 + #define RT5640_M_BST4_OM_R (0x1 << 7) + #define RT5640_M_BST4_OM_R_SFT 7 +-#define RT5640_M_BST2_OM_R (0x1 << 6) +-#define RT5640_M_BST2_OM_R_SFT 6 ++#define RT5640_M_BST2_OM_R (0x1 << 7) ++#define RT5640_M_BST2_OM_R_SFT 7 ++#define RT5640_M_BST3_OM_R (0x1 << 6) ++#define RT5640_M_BST3_OM_R_SFT 6 + #define RT5640_M_BST1_OM_R (0x1 << 5) + #define RT5640_M_BST1_OM_R_SFT 5 + #define RT5640_M_IN_R_OM_R (0x1 << 4) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0002-ASoC-codecs-rt5640-Fix-hpout-restore-when-lout-is-en.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0002-ASoC-codecs-rt5640-Fix-hpout-restore-when-lout-is-en.patch new file mode 100644 index 000000000..57a6c27b5 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0002-ASoC-codecs-rt5640-Fix-hpout-restore-when-lout-is-en.patch @@ -0,0 +1,32 @@ +From 06cc977b69459292246e9b08dfe433fc43e5d5f3 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 20 Aug 2022 20:32:07 +0200 +Subject: [PATCH 02/84] ASoC: codecs: rt5640: Fix hpout restore when lout is + enabled + +When line out and heaphones are enabled, rt5640_lout_event will cause +headpone state restore to fail, by calling hp_amp_power_on again after +rt5640_pmu_depop. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index e3a90be5294b..5376206e308b 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -995,7 +995,8 @@ static int rt5640_lout_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +- hp_amp_power_on(component); ++ //hp_amp_power_on(component); ++ //XXX: ^^ breaks hpout restore + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_LM, RT5640_PWR_LM); + snd_soc_component_update_bits(component, RT5640_OUTPUT, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0003-ASoC-codecs-rt5640-Add-input-mixer-input-volume-cont.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0003-ASoC-codecs-rt5640-Add-input-mixer-input-volume-cont.patch new file mode 100644 index 000000000..3217c7d66 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0003-ASoC-codecs-rt5640-Add-input-mixer-input-volume-cont.patch @@ -0,0 +1,48 @@ +From 07c3af029c04522de7b35646a6374b8241fdc28c Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 14 Apr 2023 22:35:37 +0200 +Subject: [PATCH 03/84] ASoC: codecs: rt5640: Add input mixer input volume + controls + +These are useful to attenuate strong signals from analog inputs. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index 5376206e308b..03d8196d218c 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -342,6 +342,7 @@ static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0); + static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); + static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000); + static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); ++static const DECLARE_TLV_DB_MINMAX(rec_gain_tlv, -1800, 0); + + /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ + static const DECLARE_TLV_DB_RANGE(bst_tlv, +@@ -413,6 +414,19 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { + SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT2, 8, 0, bst_tlv), + ++ /* RECMIXL Gain Controls */ ++ SOC_SINGLE_TLV("RECMIXL INL Gain", RT5640_REC_L1_MIXER, 10, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXL BST2 Gain", RT5640_REC_L1_MIXER, 7, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXL BST3 Gain", RT5640_REC_L1_MIXER, 1, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXL BST1 Gain", RT5640_REC_L2_MIXER, 13, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXL OUTMIXL Gain", RT5640_REC_L2_MIXER, 10, 6, 1, rec_gain_tlv), ++ ++ SOC_SINGLE_TLV("RECMIXR INR Gain", RT5640_REC_R1_MIXER, 10, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXR BST2 Gain", RT5640_REC_R1_MIXER, 7, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXR BST3 Gain", RT5640_REC_R1_MIXER, 1, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXR BST1 Gain", RT5640_REC_R2_MIXER, 13, 6, 1, rec_gain_tlv), ++ SOC_SINGLE_TLV("RECMIXR OUTMIXR Gain", RT5640_REC_R2_MIXER, 10, 6, 1, rec_gain_tlv), ++ + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, + RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0004-ASoC-codecs-rt5640-Allow-to-control-single-ended-dif.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0004-ASoC-codecs-rt5640-Allow-to-control-single-ended-dif.patch new file mode 100644 index 000000000..eae72e075 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0004-ASoC-codecs-rt5640-Allow-to-control-single-ended-dif.patch @@ -0,0 +1,32 @@ +From 68b5fa757087e8fdf3fe6c7bad67c200503946f8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 14 Apr 2023 22:37:02 +0200 +Subject: [PATCH 04/84] ASoC: codecs: rt5640: Allow to control + single-ended/differential mode + +Not for upstream. This is useful for debugging, trying things without +having to modify DT. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index 03d8196d218c..1553a0b1f936 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -427,6 +427,10 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { + SOC_SINGLE_TLV("RECMIXR BST1 Gain", RT5640_REC_R2_MIXER, 13, 6, 1, rec_gain_tlv), + SOC_SINGLE_TLV("RECMIXR OUTMIXR Gain", RT5640_REC_R2_MIXER, 10, 6, 1, rec_gain_tlv), + ++ SOC_SINGLE("IN3 Differential Mode", RT5640_IN1_IN2, 6, 1, 0), ++ SOC_SINGLE("IN2 Differential Mode", RT5640_IN3_IN4, 6, 1, 0), ++ SOC_SINGLE("LOUT Differential Mode", 0xfa, 14, 1, 0), ++ + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, + RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0005-ASoC-codecs-rt5640-Keep-the-codec-enabled-when-idle.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0005-ASoC-codecs-rt5640-Keep-the-codec-enabled-when-idle.patch new file mode 100644 index 000000000..af2e9bdc8 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0005-ASoC-codecs-rt5640-Keep-the-codec-enabled-when-idle.patch @@ -0,0 +1,28 @@ +From 801e51af0f5dc01654220afd90a8844c8a7c0a3f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 29 Jul 2023 17:33:24 +0200 +Subject: [PATCH 05/84] ASoC: codecs: rt5640: Keep the codec enabled when idle + +This makes analog audio work even without any i2s streams being enabled. + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index 1553a0b1f936..d6859a079538 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -2954,6 +2954,8 @@ static const struct snd_soc_component_driver soc_component_dev_rt5640 = { + .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets), + .dapm_routes = rt5640_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes), ++ .suspend_bias_off = 1, ++ .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0006-ASoC-codecs-rt5640-Add-support-for-power-supplies.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0006-ASoC-codecs-rt5640-Add-support-for-power-supplies.patch new file mode 100644 index 000000000..44da1e08b --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0006-ASoC-codecs-rt5640-Add-support-for-power-supplies.patch @@ -0,0 +1,54 @@ +From fc33dcc63c708630afafbc6c0b8541680113c95a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 7 Sep 2023 22:58:22 +0200 +Subject: [PATCH 06/84] ASoC: codecs: rt5640: Add support for power supplies + +Signed-off-by: Ondrej Jirman +--- + sound/soc/codecs/rt5640.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index d6859a079538..7d7dad5ed884 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -38,6 +39,13 @@ + + #define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING)) + ++static const char *const rt5640_supply_names[] = { ++ "avdd", ++ "cpvdd", ++ "dbvdd", ++ "spkvdd", ++}; ++ + static const struct regmap_range_cfg rt5640_ranges[] = { + { .name = "PR", .range_min = RT5640_PR_BASE, + .range_max = RT5640_PR_BASE + 0xb4, +@@ -3031,6 +3039,14 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) + msleep(400); + } + ++ ret = devm_regulator_bulk_get_enable(&i2c->dev, ++ ARRAY_SIZE(rt5640_supply_names), ++ rt5640_supply_names); ++ if (ret) { ++ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); ++ return ret; ++ } ++ + rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); + if (IS_ERR(rt5640->regmap)) { + ret = PTR_ERR(rt5640->regmap); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0007-media-imx258-Add-i2c-supply.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0007-media-imx258-Add-i2c-supply.patch new file mode 100644 index 000000000..5fe0e133c --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0007-media-imx258-Add-i2c-supply.patch @@ -0,0 +1,27 @@ +From 5774ba8941ddb45470bfa9cc903d63e52a678b90 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 22 May 2022 03:25:06 +0200 +Subject: [PATCH 07/84] media: imx258: Add i2c supply + +Sensor depends on I2C bus to be powered up to work. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index bc9ee449a87c..7bfe127daf60 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -486,6 +486,7 @@ static const char * const imx258_supply_name[] = { + "vana", /* Analog (2.8V) supply */ + "vdig", /* Digital Core (1.2V) supply */ + "vif", /* IF (1.8V) supply */ ++ "i2c", /* I2C BUS I/O (1.8V) supply */ + }; + + #define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_name) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0008-media-imx258-Add-reset-gpio.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0008-media-imx258-Add-reset-gpio.patch new file mode 100644 index 000000000..22d0d76b8 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0008-media-imx258-Add-reset-gpio.patch @@ -0,0 +1,69 @@ +From faeafc9db9ac2d7f191c09fe3edfa9891a7ddc2f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 9 Sep 2024 16:49:41 +0200 +Subject: [PATCH 08/84] media: imx258: Add reset gpio + +IMX258 features a reset input that needs to be controlled when powering +up the chip. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index 7bfe127daf60..16cabbea9074 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -681,6 +681,7 @@ struct imx258 { + struct mutex mutex; + + struct clk *clk; ++ struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[IMX258_NUM_SUPPLIES]; + }; + +@@ -1125,12 +1126,20 @@ static int imx258_power_on(struct device *dev) + return ret; + } + ++ mdelay(20); ++ + ret = clk_prepare_enable(imx258->clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); + } + ++ usleep_range(1000, 2000); ++ ++ gpiod_set_value_cansleep(imx258->reset_gpio, 0); ++ ++ usleep_range(400, 500); ++ + return ret; + } + +@@ -1140,6 +1149,7 @@ static int imx258_power_off(struct device *dev) + struct imx258 *imx258 = to_imx258(sd); + + clk_disable_unprepare(imx258->clk); ++ gpiod_set_value_cansleep(imx258->reset_gpio, 1); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); + + return 0; +@@ -1388,6 +1398,11 @@ static int imx258_probe(struct i2c_client *client) + return dev_err_probe(imx258->dev, ret, + "failed to get regulators\n"); + ++ imx258->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(imx258->reset_gpio)) ++ return PTR_ERR(imx258->reset_gpio); ++ + imx258->clk = devm_v4l2_sensor_clk_get_legacy(imx258->dev, NULL, false, + 0); + if (IS_ERR(imx258->clk)) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0009-media-imx258-Drop-interface-speed-to-1224-mbps.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0009-media-imx258-Drop-interface-speed-to-1224-mbps.patch new file mode 100644 index 000000000..b97a2641b --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0009-media-imx258-Drop-interface-speed-to-1224-mbps.patch @@ -0,0 +1,78 @@ +From 9b48bbc02eb1a3ef4a734f64a95c1e843b1192d1 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 15 Sep 2024 23:41:50 +0200 +Subject: [PATCH 09/84] media: imx258: Drop interface speed to 1224 mbps + +Pinephone Pro can't do 1272 mbps. This is just a hack to make the new +upstream driver work quickly with the phone. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index 16cabbea9074..f0e8b81ece48 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -213,12 +213,12 @@ static const struct cci_reg_sequence mipi_1267mbps_19_2mhz_4l[] = { + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, + }; + +-static const struct cci_reg_sequence mipi_1272mbps_24mhz_2l[] = { ++static const struct cci_reg_sequence mipi_1224mbps_24mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 10 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, +- { IMX258_REG_PLL_IVT_MPY, 212 }, ++ { IMX258_REG_PLL_IVT_MPY, 204 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, +@@ -226,16 +226,16 @@ static const struct cci_reg_sequence mipi_1272mbps_24mhz_2l[] = { + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, +- { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 2 }, ++ { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1224 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, + }; + +-static const struct cci_reg_sequence mipi_1272mbps_24mhz_4l[] = { ++static const struct cci_reg_sequence mipi_1224mbps_24mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, +- { IMX258_REG_PLL_IVT_MPY, 212 }, ++ { IMX258_REG_PLL_IVT_MPY, 204 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, +@@ -243,7 +243,7 @@ static const struct cci_reg_sequence mipi_1272mbps_24mhz_4l[] = { + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, +- { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 4 }, ++ { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1224 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, + }; + +@@ -568,11 +568,11 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = { + .link_cfg = { + [IMX258_2_LANE_MODE] = { + .lf_to_pix_rate_factor = 2, +- .reg_list = REGS(mipi_1272mbps_24mhz_2l), ++ .reg_list = REGS(mipi_1224mbps_24mhz_2l), + }, + [IMX258_4_LANE_MODE] = { + .lf_to_pix_rate_factor = 4, +- .reg_list = REGS(mipi_1272mbps_24mhz_4l), ++ .reg_list = REGS(mipi_1224mbps_24mhz_4l), + }, + } + }, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0010-DEBUG-media-imx258-Add-debug-register-access.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0010-DEBUG-media-imx258-Add-debug-register-access.patch new file mode 100644 index 000000000..e0200fbbe --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0010-DEBUG-media-imx258-Add-debug-register-access.patch @@ -0,0 +1,81 @@ +From a14e1b917fd223d3b5063a48c98bd92df1a97c09 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 9 Jun 2022 01:04:18 +0200 +Subject: [PATCH 10/84] [DEBUG] media: imx258: Add debug register access + +This is useful during development. + +Signed-off-by: Ondrej Jirman +--- + drivers/media/i2c/imx258.c | 48 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c +index f0e8b81ece48..aebe00fd878a 100644 +--- a/drivers/media/i2c/imx258.c ++++ b/drivers/media/i2c/imx258.c +@@ -1214,6 +1214,53 @@ static int imx258_identify_module(struct imx258 *imx258) + return 0; + } + ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int imx258_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct imx258 *imx258 = to_imx258(sd); ++ u64 val = 0; ++ int ret; ++ ++ if (reg->reg > 0xffff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ mutex_lock(&imx258->mutex); ++ ret = cci_read(imx258->regmap, CCI_REG8(reg->reg), &val, NULL); ++ mutex_unlock(&imx258->mutex); ++ if (ret) ++ return -EIO; ++ ++ reg->val = val; ++ return 0; ++} ++ ++static int imx258_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct imx258 *imx258 = to_imx258(sd); ++ int ret; ++ ++ if (reg->reg > 0xffff || reg->val > 0xff) ++ return -EINVAL; ++ ++ mutex_lock(&imx258->mutex); ++ ret = cci_write(imx258->regmap, CCI_REG8(reg->reg), reg->val, NULL); ++ mutex_unlock(&imx258->mutex); ++ ++ return ret; ++} ++#endif ++ ++static const struct v4l2_subdev_core_ops imx258_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = imx258_g_register, ++ .s_register = imx258_s_register, ++#endif ++}; ++ + static const struct v4l2_subdev_video_ops imx258_video_ops = { + .s_stream = imx258_set_stream, + }; +@@ -1227,6 +1274,7 @@ static const struct v4l2_subdev_pad_ops imx258_pad_ops = { + }; + + static const struct v4l2_subdev_ops imx258_subdev_ops = { ++ .core = &imx258_core_ops, + .video = &imx258_video_ops, + .pad = &imx258_pad_ops, + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0011-ASoC-rockchip-Fix-doubling-of-playback-speed-after-s.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0011-ASoC-rockchip-Fix-doubling-of-playback-speed-after-s.patch new file mode 100644 index 000000000..f5ab6b5a8 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0011-ASoC-rockchip-Fix-doubling-of-playback-speed-after-s.patch @@ -0,0 +1,46 @@ +From 2cfdbc171b7b0d0d03edd78fae516fa1310fcf17 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 15 Aug 2022 02:23:37 +0200 +Subject: [PATCH 11/84] ASoC: rockchip: Fix doubling of playback speed after + system sleep + +There is some issue with CRU on RK3399 that can be reproduced by: + +- playing some audio and stopping the playback (so that runtime PM suspends I2S) +- putting system to deep sleep and resuming it (using mainline TF-A) +- playing some audio (audio plays at 2x the normal speed) + +If the audio is kept playing during system sleep cycle, the issue +does not manifest. Relevant registers in CRU are identical in bug/ +no-bug scenarios, so this patch is just touching the CRU registers +without actually changing anything in the end. Touching the registers +fixes the playback speed, though. + +Inspired by: https://github.com/djselbeck/linux/commit/cb4be5dec3fa18c4b344c11fed3fc57aa3bea424 + +Signed-off-by: Ondrej Jirman +--- + sound/soc/rockchip/rockchip_i2s.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c +index 0a0a95b4f520..6dce1db85427 100644 +--- a/sound/soc/rockchip/rockchip_i2s.c ++++ b/sound/soc/rockchip/rockchip_i2s.c +@@ -114,6 +114,13 @@ static int i2s_runtime_resume(struct device *dev) + if (ret) + clk_disable_unprepare(i2s->mclk); + ++ if (ret == 0) { ++ unsigned long rate = clk_get_rate(i2s->mclk); ++ ++ clk_set_rate(i2s->mclk, rate - 1); ++ clk_set_rate(i2s->mclk, rate); ++ } ++ + return ret; + } + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0012-Revert-usb-dwc3-Abort-suspend-on-soft-disconnect-fai.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0012-Revert-usb-dwc3-Abort-suspend-on-soft-disconnect-fai.patch new file mode 100644 index 000000000..e7ba02960 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0012-Revert-usb-dwc3-Abort-suspend-on-soft-disconnect-fai.patch @@ -0,0 +1,89 @@ +From db566db3b72a281b3e941c1070cfb30688207950 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 9 Aug 2025 10:10:42 +0200 +Subject: [PATCH 12/84] Revert "usb: dwc3: Abort suspend on soft disconnect + failure" + +This reverts commit 630a1dec3b0eba2a695b9063f1c205d585cbfec9. +--- + drivers/usb/dwc3/core.c | 9 ++------- + drivers/usb/dwc3/gadget.c | 22 +++++++++++++--------- + 2 files changed, 15 insertions(+), 16 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 65213896de99..9d4293f02c9a 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -2476,7 +2476,6 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + { + u32 reg; + int i; +- int ret; + + if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) { + dwc->susphy_state = (dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)) & +@@ -2495,9 +2494,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + case DWC3_GCTL_PRTCAP_DEVICE: + if (pm_runtime_suspended(dwc->dev)) + break; +- ret = dwc3_gadget_suspend(dwc); +- if (ret) +- return ret; ++ dwc3_gadget_suspend(dwc); + synchronize_irq(dwc->irq_gadget); + dwc3_core_exit(dwc); + break; +@@ -2533,9 +2530,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + break; + + if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { +- ret = dwc3_gadget_suspend(dwc); +- if (ret) +- return ret; ++ dwc3_gadget_suspend(dwc); + synchronize_irq(dwc->irq_gadget); + } + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 3d4ca68e584c..519a1dd23a30 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -4842,15 +4842,8 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) + int ret; + + ret = dwc3_gadget_soft_disconnect(dwc); +- /* +- * Attempt to reset the controller's state. Likely no +- * communication can be established until the host +- * performs a port reset. +- */ +- if (ret && dwc->softconnect) { +- dwc3_gadget_soft_connect(dwc); +- return -EAGAIN; +- } ++ if (ret) ++ goto err; + + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->gadget_driver) +@@ -4858,6 +4851,17 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; ++ ++err: ++ /* ++ * Attempt to reset the controller's state. Likely no ++ * communication can be established until the host ++ * performs a port reset. ++ */ ++ if (dwc->softconnect) ++ dwc3_gadget_soft_connect(dwc); ++ ++ return ret; + } + + int dwc3_gadget_resume(struct dwc3 *dwc) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0013-thermal-rockchip-Add-support-for-RV1106-SoC.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0013-thermal-rockchip-Add-support-for-RV1106-SoC.patch new file mode 100644 index 000000000..6263d5a9e --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0013-thermal-rockchip-Add-support-for-RV1106-SoC.patch @@ -0,0 +1,175 @@ +From 0b2ffa2ff9972608365e3f7395e2b0320e49b216 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 25 Jun 2024 00:00:14 +0200 +Subject: [PATCH 13/84] thermal: rockchip: Add support for RV1106 SoC + +This SoC includes a thermal sensor, which can be supported by this +driver. Add support for it. + +Signed-off-by: Ondrej Jirman +--- + drivers/thermal/rockchip_thermal.c | 95 +++++++++++++++++++++++++++--- + 1 file changed, 86 insertions(+), 9 deletions(-) + +diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c +index c49ddf70f86e..5dc1e8f27774 100644 +--- a/drivers/thermal/rockchip_thermal.c ++++ b/drivers/thermal/rockchip_thermal.c +@@ -210,6 +210,8 @@ struct rockchip_thermal_data { + #define TSADCV2_AUTO_PERIOD_HT 0x6c + #define TSADCV3_AUTO_PERIOD 0x154 + #define TSADCV3_AUTO_PERIOD_HT 0x158 ++#define TSADCV9_Q_MAX 0x210 ++#define TSADCV9_FLOW_CON 0x218 + + #define TSADCV2_AUTO_EN BIT(0) + #define TSADCV2_AUTO_EN_MASK BIT(16) +@@ -249,10 +251,18 @@ struct rockchip_thermal_data { + #define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */ + #define TSADCV5_USER_INTER_PD_SOC 0xfc0 /* 97us, at least 90us */ + ++#define TSADCV9_AUTO_SRC (0x10001 << 0) ++#define TSADCV9_PD_MODE (0x10001 << 4) ++#define TSADCV9_Q_MAX_VAL (0xffff0400 << 0) ++ + #define GRF_SARADC_TESTBIT 0x0e644 + #define GRF_TSADC_TESTBIT_L 0x0e648 + #define GRF_TSADC_TESTBIT_H 0x0e64c + ++#define RV1106_VOGRF_TSADC_CON 0x6000C ++#define RV1106_VOGRF_TSADC_TSEN (0x10001 << 8) ++#define RV1106_VOGRF_TSADC_ANA (0xff0007 << 0) ++ + #define PX30_GRF_SOC_CON2 0x0408 + + #define RK3568_GRF_TSADC_CON 0x0600 +@@ -292,6 +302,15 @@ struct tsadc_table { + int temp; + }; + ++static const struct tsadc_table rv1106_code_table[] = { ++ {0, -40000}, ++ {396, -40000}, ++ {504, 25000}, ++ {605, 85000}, ++ {673, 125000}, ++ {TSADCV2_DATA_MASK, 125000}, ++}; ++ + static const struct tsadc_table rv1108_table[] = { + {0, -40000}, + {374, -40000}, +@@ -863,6 +882,35 @@ static void rk_tsadcv8_initialize(struct regmap *grf, void __iomem *regs, + regs + TSADCV2_AUTO_CON); + } + ++static void rk_tsadcv9_initialize(struct regmap *grf, void __iomem *regs, ++ enum tshut_polarity tshut_polarity) ++{ ++ regmap_write(grf, RV1106_VOGRF_TSADC_CON, RV1106_VOGRF_TSADC_TSEN); ++ udelay(10); ++ regmap_write(grf, RV1106_VOGRF_TSADC_CON, RV1106_VOGRF_TSADC_ANA); ++ udelay(100); ++ ++ writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV3_AUTO_PERIOD); ++ writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, ++ regs + TSADCV3_AUTO_PERIOD_HT); ++ writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, ++ regs + TSADCV3_HIGHT_INT_DEBOUNCE); ++ writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, ++ regs + TSADCV3_HIGHT_TSHUT_DEBOUNCE); ++ writel_relaxed(TSADCV9_AUTO_SRC, regs + TSADCV2_INT_PD); ++ writel_relaxed(TSADCV9_PD_MODE, regs + TSADCV9_FLOW_CON); ++ writel_relaxed(TSADCV9_Q_MAX_VAL, regs + TSADCV9_Q_MAX); ++ if (tshut_polarity == TSHUT_HIGH_ACTIVE) ++ writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_HIGH | ++ TSADCV2_AUTO_TSHUT_POLARITY_MASK, ++ regs + TSADCV2_AUTO_CON); ++ else ++ writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_MASK, ++ regs + TSADCV2_AUTO_CON); ++ writel_relaxed(TSADCV3_AUTO_Q_SEL_EN | (TSADCV3_AUTO_Q_SEL_EN << 16), ++ regs + TSADCV2_AUTO_CON); ++} ++ + static void rk_tsadcv2_irq_ack(void __iomem *regs) + { + u32 val; +@@ -1073,6 +1121,15 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, + writel_relaxed(val, regs + TSADCV2_INT_EN); + } + ++static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table, ++ int code, int trim_base, int trim_base_frac) ++{ ++ int temp = trim_base * 1000 + trim_base_frac * 100; ++ u32 base_code = rk_tsadcv2_temp_to_code(table, temp); ++ ++ return code - base_code; ++} ++ + static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs, + enum tshut_mode mode) + { +@@ -1089,15 +1146,6 @@ static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs, + writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN); + } + +-static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table, +- int code, int trim_base, int trim_base_frac) +-{ +- int temp = trim_base * 1000 + trim_base_frac * 100; +- u32 base_code = rk_tsadcv2_temp_to_code(table, temp); +- +- return code - base_code; +-} +- + static const struct rockchip_tsadc_chip px30_tsadc_data = { + /* cpu, gpu */ + .chn_offset = 0, +@@ -1120,6 +1168,31 @@ static const struct rockchip_tsadc_chip px30_tsadc_data = { + }, + }; + ++static const struct rockchip_tsadc_chip rv1106_tsadc_data = { ++ /* cpu */ ++ .chn_offset = 0, ++ .chn_num = 1, ++ ++ .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ ++ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ ++ .tshut_temp = 95000, ++ ++ .initialize = rk_tsadcv9_initialize, ++ .irq_ack = rk_tsadcv4_irq_ack, ++ .control = rk_tsadcv4_control, ++ .get_temp = rk_tsadcv4_get_temp, ++ .set_alarm_temp = rk_tsadcv3_alarm_temp, ++ .set_tshut_temp = rk_tsadcv3_tshut_temp, ++ .set_tshut_mode = rk_tsadcv4_tshut_mode, ++ ++ .table = { ++ .id = rv1106_code_table, ++ .length = ARRAY_SIZE(rv1106_code_table), ++ .data_mask = TSADCV2_DATA_MASK, ++ .mode = ADC_INCREMENT, ++ }, ++}; ++ + static const struct rockchip_tsadc_chip rv1108_tsadc_data = { + /* cpu */ + .chn_offset = 0, +@@ -1355,6 +1428,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { + { .compatible = "rockchip,px30-tsadc", + .data = (void *)&px30_tsadc_data, + }, ++ { ++ .compatible = "rockchip,rv1106-tsadc", ++ .data = (void *)&rv1106_tsadc_data, ++ }, + { + .compatible = "rockchip,rv1108-tsadc", + .data = (void *)&rv1108_tsadc_data, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0014-soc-rockchip-Add-support-for-power-monitoring-driver.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0014-soc-rockchip-Add-support-for-power-monitoring-driver.patch new file mode 100644 index 000000000..8c0e959ca --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0014-soc-rockchip-Add-support-for-power-monitoring-driver.patch @@ -0,0 +1,149 @@ +From 3f80fd5e6f79dc72d866cb1869a493e1e12e350d Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 24 Jun 2024 23:59:19 +0200 +Subject: [PATCH 14/84] soc: rockchip: Add support for power monitoring driver + +This driver reports brownouts into kernel log, so that the user +can debug causes of SoC instability. + +Signed-off-by: Ondrej Jirman +--- + drivers/soc/rockchip/Kconfig | 5 + + drivers/soc/rockchip/Makefile | 1 + + .../soc/rockchip/rockchip_npor_powergood.c | 101 ++++++++++++++++++ + 3 files changed, 107 insertions(+) + create mode 100644 drivers/soc/rockchip/rockchip_npor_powergood.c + +diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig +index 785f60c6f3ad..9408c9a71490 100644 +--- a/drivers/soc/rockchip/Kconfig ++++ b/drivers/soc/rockchip/Kconfig +@@ -30,4 +30,9 @@ config ROCKCHIP_DTPM + on this platform. That will create all the power capping capable + devices. + ++config ROCKCHIP_NPOR_POWERGOOD ++ bool "Rockchip NPOR Powergood" ++ help ++ Say y if SOC integrate ROCKCHIP NPOR Powergood. ++ + endif +diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile +index 23d414433c8c..06526238b9a5 100644 +--- a/drivers/soc/rockchip/Makefile ++++ b/drivers/soc/rockchip/Makefile +@@ -5,3 +5,4 @@ + obj-$(CONFIG_ROCKCHIP_GRF) += grf.o + obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o + obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o ++obj-$(CONFIG_ROCKCHIP_NPOR_POWERGOOD) += rockchip_npor_powergood.o +diff --git a/drivers/soc/rockchip/rockchip_npor_powergood.c b/drivers/soc/rockchip/rockchip_npor_powergood.c +new file mode 100644 +index 000000000000..1c92541ce2ae +--- /dev/null ++++ b/drivers/soc/rockchip/rockchip_npor_powergood.c +@@ -0,0 +1,101 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright (c) 2022 Rockchip Electronics Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct rknpor_powergood_info { ++ int irq; ++ irq_handler_t isr; ++ struct regmap *grf; ++ u32 grf_offset; ++ u8 status_bits_shift; ++}; ++ ++static irqreturn_t rv1106_npor_powergood_isr(int irq, void *data) ++{ ++ struct rknpor_powergood_info *powergood = data; ++ u32 voltage = 0; ++ ++ while (!voltage) { ++ regmap_read(powergood->grf, powergood->grf_offset, &voltage); ++ voltage = (voltage >> powergood->status_bits_shift) & 0x1; ++ } ++ ++ pr_err("%s voltage jitter detected\n", __func__); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct rknpor_powergood_info rv1106_soc_data = { ++ .grf_offset = 0x20020, ++ .status_bits_shift = 4, ++ .isr = rv1106_npor_powergood_isr, ++}; ++ ++static const struct of_device_id rockchip_npor_powergood_dt_match[] = { ++ { ++ .compatible = "rockchip,rv1106-npor-powergood", ++ .data = &rv1106_soc_data ++ }, ++ {}, ++}; ++ ++static int rockchip_npor_powergood_probe(struct platform_device *pdev) ++{ ++ struct rknpor_powergood_info *powergood; ++ const struct of_device_id *match; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ match = of_match_node(rockchip_npor_powergood_dt_match, pdev->dev.of_node); ++ powergood = (struct rknpor_powergood_info *)match->data; ++ ++ if (!powergood) ++ return -EINVAL; ++ ++ if (dev->parent && dev->parent->of_node) { ++ powergood->grf = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(powergood->grf)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(powergood->grf), "fail to find grf\n"); ++ } ++ ++ powergood->irq = platform_get_irq(pdev, 0); ++ if (powergood->irq < 0) ++ return powergood->irq; ++ ++ ret = devm_request_irq(&pdev->dev, powergood->irq, powergood->isr, 0, "rknpor_powergood", powergood); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "request irq fail\n"); ++ ++ dev_info(&pdev->dev, "probe success!\n"); ++ ++ return 0; ++} ++ ++static struct platform_driver rockchip_npor_powergood_driver = { ++ .probe = rockchip_npor_powergood_probe, ++ .driver = { ++ .name = "rockchip,rknpor-powergood", ++ .of_match_table = rockchip_npor_powergood_dt_match, ++ }, ++}; ++ ++static int __init rockchip_npor_powergood_init(void) ++{ ++ return platform_driver_register(&rockchip_npor_powergood_driver); ++} ++subsys_initcall_sync(rockchip_npor_powergood_init); ++MODULE_DESCRIPTION("Rockchip NPOR Powergood"); ++MODULE_LICENSE("GPL"); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch new file mode 100644 index 000000000..f7ea2d2c3 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch @@ -0,0 +1,868 @@ +From eea2b3dbea7c25240cd594f028c5a33d92cf70df Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 25 Jun 2024 00:01:40 +0200 +Subject: [PATCH 15/84] rtc: rockchip: Add support for RTC present in RV1106 + SoC + +This driver adds support for RTC present in Rockchip RV1106 SoC. + +Signed-off-by: Ondrej Jirman +--- + drivers/rtc/Kconfig | 10 + + drivers/rtc/Makefile | 1 + + drivers/rtc/rtc-rockchip.c | 810 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 821 insertions(+) + create mode 100644 drivers/rtc/rtc-rockchip.c + +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 364afc73f8ab..bc755773a31d 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -456,6 +456,16 @@ config RTC_DRV_RK808 + This driver can also be built as a module. If so, the module + will be called rk808-rtc. + ++config RTC_DRV_ROCKCHIP ++ tristate "Rockchip RTC" ++ depends on ARCH_ROCKCHIP ++ help ++ If you say yes here you will get support for the ++ RTC of Rockchip. ++ ++ This driver can also be built as a module. If so, the module ++ will be called rtc-rockchip. ++ + config RTC_DRV_RS5C372 + tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" + help +diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile +index 6cf7e066314e..7428e7f6274f 100644 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -148,6 +148,7 @@ obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o + obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o + obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o + obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o ++obj-$(CONFIG_RTC_DRV_ROCKCHIP) += rtc-rockchip.o + obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o + obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o + obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o +diff --git a/drivers/rtc/rtc-rockchip.c b/drivers/rtc/rtc-rockchip.c +new file mode 100644 +index 000000000000..3998463565b8 +--- /dev/null ++++ b/drivers/rtc/rtc-rockchip.c +@@ -0,0 +1,810 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2022 Rockchip Electronics Co., Ltd ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* RTC_CTRL_REG bitfields */ ++ ++#define RTC_REG(x) ((x)) ++#define RTC_SET_SECONDS RTC_REG(0x0) ++#define RTC_SET_MINUTES RTC_REG(0x4) ++#define RTC_SET_HOURS RTC_REG(0x8) ++#define RTC_SET_DAYS RTC_REG(0xc) ++#define RTC_SET_MONTHS RTC_REG(0x10) ++#define RTC_SET_YEARL RTC_REG(0x14) ++#define RTC_SET_YEARH RTC_REG(0x18) ++#define RTC_SET_WEEKS RTC_REG(0x1c) ++#define RTC_ALARM_SECONDS RTC_REG(0x20) ++#define RTC_ALARM_MINUTES RTC_REG(0x24) ++#define RTC_ALARM_HOURS RTC_REG(0x28) ++#define RTC_ALARM_DAYS RTC_REG(0x2c) ++#define RTC_ALARM_MONTHS RTC_REG(0x30) ++#define RTC_ALARM_YEARL RTC_REG(0x34) ++#define RTC_ALARM_YEARH RTC_REG(0x38) ++#define RTC_CTRL RTC_REG(0x3C) ++#define RTC_STATUS0 RTC_REG(0x40) ++#define RTC_STATUS1 RTC_REG(0x44) ++#define RTC_INT0_EN RTC_REG(0x48) ++#define RTC_INT1_EN RTC_REG(0x4c) ++#define RTC_MSEC_CTRL RTC_REG(0x50) ++#define RTC_MSEC_CNT RTC_REG(0x54) ++#define RTC_COMP_H RTC_REG(0x58) ++#define RTC_COMP_D RTC_REG(0x5c) ++#define RTC_COMP_M RTC_REG(0x60) ++#define RTC_ANALOG_CTRL RTC_REG(0x64) ++#define RTC_ANALOG_TEST RTC_REG(0x68) ++#define RTC_LDO_CTRL RTC_REG(0x6c) ++#define RTC_XO_TRIM0 RTC_REG(0x70) ++#define RTC_XO_TRIM1 RTC_REG(0x74) ++#define RTC_VPTAT_TRIM RTC_REG(0x78) ++#define RTC_ANALOG_EN RTC_REG(0x7c) ++#define RTC_CLK32K_TEST RTC_REG(0x80) ++#define RTC_TEST_ST RTC_REG(0x84) ++#define RTC_TEST_LEN RTC_REG(0x88) ++#define RTC_CNT_0 RTC_REG(0x8c) ++#define RTC_CNT_1 RTC_REG(0x90) ++#define RTC_CNT_2 RTC_REG(0x94) ++#define RTC_CNT_3 RTC_REG(0x98) ++#define RTC_MAX_REGISTER RTC_CNT_3 ++ ++#define VI_GRF_VI_MISC_CON0 0x50000 ++#define RTC_CLAMP_EN BIT(6) ++ ++/* RTC_CTRL_REG bitfields */ ++#define RTC_CTRL_REG_START_RTC BIT(0) ++#define RTC_TIMEOUT (3000 * 1000) ++ ++/* RK630 has a shadowed register for saving a "frozen" RTC time. ++ * When user setting "GET_TIME" to 1, the time will save in this shadowed ++ * register. If set "READSEL" to 1, user read rtc time register, actually ++ * get the time of that moment. If we need the real time, clr this bit. ++ */ ++#define RTC_CTRL_REG_RTC_GET_TIME BIT(6) ++#define RTC_CTRL_REG_RTC_READSEL_M BIT(7) ++#define RTC_INT_REG_ALARM_EN BIT(7) ++#define RTC_D2A_XO_EN BIT(0) ++#define RTC_D2A_CLK_OUT_EN BIT(5) ++ ++#define RTC_STATUS_MASK 0xFF ++ ++#define SECONDS_REG_MSK 0x7F ++#define MINUTES_REG_MAK 0x7F ++#define HOURS_REG_MSK 0x3F ++#define DAYS_REG_MSK 0x3F ++#define MONTHS_REG_MSK 0x1F ++#define YEARS_REG_MSK 0xFF ++#define WEEKS_REG_MSK 0x7 ++ ++#define RTC_VREF_INIT 0x40 ++ ++#define D2A_POR_REG_SEL1 BIT(4) ++#define D2A_POR_REG_SEL0 BIT(1) ++ ++#define NUM_TIME_REGS 8 ++#define NUM_ALARM_REGS 7 ++ ++#define DISABLE_ALARM_INT 0x3F ++#define ENABLE_ALARM_INT 0xFF ++#define ALARM_INT_STATUS BIT(4) ++ ++#define CLK32K_TEST_EN BIT(0) ++#define CLK32K_TEST_START BIT(0) ++#define CLK32K_TEST_STATUS BIT(1) ++#define CLK32K_TEST_DONE BIT(2) ++#define CLK32K_TEST_LEN 1 ++ ++#define CLK32K_COMP_DIR_ADD BIT(7) ++#define CLK32K_COMP_EN BIT(2) ++#define CLK32K_NO_COMP 0x1 ++ ++#define CLK32K_TEST_REF_CLK 24000000 ++ ++#define RTC_WRITE_MASK 0xc4522900 ++ ++enum { ++ ROCKCHIP_RV1106_RTC = 1, ++}; ++ ++struct rockchip_rtc { ++ struct regmap *regmap; ++ struct rtc_device *rtc; ++ struct regmap *grf; ++ struct clk_bulk_data *clks; ++ int num_clks; ++ int irq; ++ unsigned int flag; ++ unsigned int mode; ++ struct delayed_work trim_work; ++}; ++ ++static unsigned int rockchip_rtc_write(struct regmap *map, ++ u32 offset, u32 val) ++{ ++ return regmap_write(map, offset, val | RTC_WRITE_MASK); ++} ++ ++static unsigned int rockchip_rtc_update_bits(struct regmap *map, ++ u32 offset, u32 mask, ++ u32 set) ++{ ++ unsigned int val; ++ ++ regmap_read(map, offset, &val); ++ return regmap_write(map, offset, (val & ~mask) | set | RTC_WRITE_MASK); ++} ++ ++/* Read current time and date in RTC */ ++static int rockchip_rtc_read_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct rockchip_rtc *rtc = dev_get_drvdata(dev); ++ u32 rtc_data[NUM_TIME_REGS]; ++ int ret; ++ int yearl, yearh; ++ ++ /* No shadowed registers, need read time three time to update time */ ++ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS, ++ rtc_data, NUM_TIME_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); ++ return ret; ++ } ++ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS, ++ rtc_data, NUM_TIME_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); ++ return ret; ++ } ++ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS, ++ rtc_data, NUM_TIME_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); ++ return ret; ++ } ++ ++ tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK); ++ tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK); ++ tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK); ++ tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK); ++ tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1; ++ yearl = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)); ++ yearh = (bcd2bin(rtc_data[6] & YEARS_REG_MSK)); ++ tm->tm_year = yearh * 100 + yearl + 100; ++ tm->tm_wday = bcd2bin(rtc_data[7] & WEEKS_REG_MSK); ++ ++ dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", ++ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); ++ ++ return ret; ++} ++ ++/* Set current time and date in RTC */ ++static int rockchip_rtc_set_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct rockchip_rtc *rtc = dev_get_drvdata(dev); ++ u32 rtc_data[NUM_TIME_REGS]; ++ int ret, status = 0; ++ int yearl, yearh; ++ ++ dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", ++ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); ++ ++ rtc_data[0] = bin2bcd(tm->tm_sec) | RTC_WRITE_MASK; ++ rtc_data[1] = bin2bcd(tm->tm_min) | RTC_WRITE_MASK; ++ rtc_data[2] = bin2bcd(tm->tm_hour) | RTC_WRITE_MASK; ++ rtc_data[3] = bin2bcd(tm->tm_mday) | RTC_WRITE_MASK; ++ rtc_data[4] = bin2bcd(tm->tm_mon + 1) | RTC_WRITE_MASK; ++ if (tm->tm_year > 199) { ++ yearh = (tm->tm_year - 100) / 100; ++ yearl = tm->tm_year - 100 - yearh * 100; ++ } else { ++ yearh = 0; ++ yearl = tm->tm_year - 100 - yearh * 100; ++ } ++ rtc_data[5] = bin2bcd(yearl) | RTC_WRITE_MASK; ++ rtc_data[6] = bin2bcd(yearh) | RTC_WRITE_MASK; ++ rtc_data[7] = bin2bcd(tm->tm_wday) | RTC_WRITE_MASK; ++ ++ /* Stop RTC while updating the RTC registers */ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL, ++ RTC_CTRL_REG_START_RTC, 0); ++ if (ret) { ++ dev_err(dev, "Failed to update RTC control: %d\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_read_poll_timeout(rtc->regmap, RTC_STATUS1, status, ++ !(status & RTC_CTRL_REG_START_RTC), ++ 1, RTC_TIMEOUT); ++ if (ret) ++ dev_err(dev, ++ "%s:timeout Update RTC_STATUS1 : %d\n", ++ __func__, ret); ++ ++ ret = regmap_bulk_write(rtc->regmap, RTC_SET_SECONDS, ++ rtc_data, NUM_TIME_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to bull write rtc_data: %d\n", ret); ++ return ret; ++ } ++ ++ /* Start RTC again */ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL, ++ RTC_CTRL_REG_RTC_READSEL_M | ++ RTC_CTRL_REG_START_RTC, ++ RTC_CTRL_REG_RTC_READSEL_M | ++ RTC_CTRL_REG_START_RTC); ++ if (ret) { ++ dev_err(dev, "Failed to update RTC control: %d\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_read_poll_timeout(rtc->regmap, RTC_STATUS1, status, ++ (status & RTC_CTRL_REG_START_RTC), ++ 1, RTC_TIMEOUT); ++ if (ret) ++ dev_err(dev, ++ "%s:timeout Update RTC_STATUS1 : %d\n", ++ __func__, ret); ++ ++ return 0; ++} ++ ++/* Read alarm time and date in RTC */ ++static int rockchip_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct rockchip_rtc *rtc = dev_get_drvdata(dev); ++ u32 alrm_data[NUM_ALARM_REGS]; ++ u32 int_reg; ++ int yearl, yearh; ++ int ret; ++ ++ ret = regmap_bulk_read(rtc->regmap, ++ RTC_ALARM_SECONDS, ++ alrm_data, NUM_ALARM_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret); ++ return ret; ++ } ++ ++ alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK); ++ alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK); ++ alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK); ++ alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK); ++ alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1; ++ yearl = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)); ++ yearh = (bcd2bin(alrm_data[6] & YEARS_REG_MSK)); ++ alrm->time.tm_year = yearh * 100 + yearl + 100; ++ ++ ret = regmap_read(rtc->regmap, RTC_INT0_EN, &int_reg); ++ if (ret) { ++ dev_err(dev, "Failed to read RTC INT REG: %d\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(dev, ++ "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", ++ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, ++ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, ++ alrm->time.tm_min, alrm->time.tm_sec); ++ ++ alrm->enabled = (int_reg & RTC_INT_REG_ALARM_EN) ? 1 : 0; ++ ++ return 0; ++} ++ ++static int rockchip_rtc_stop_alarm(struct rockchip_rtc *rtc) ++{ ++ int ret; ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_INT0_EN, 0); ++ ++ return ret; ++} ++ ++static int rockchip_rtc_start_alarm(struct rockchip_rtc *rtc) ++{ ++ int ret; ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, RTC_STATUS_MASK); ++ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, 0); ++ ret = rockchip_rtc_write(rtc->regmap, RTC_INT0_EN, ENABLE_ALARM_INT); ++ ++ return ret; ++} ++ ++static int rockchip_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct rockchip_rtc *rtc = dev_get_drvdata(dev); ++ u32 alrm_data[NUM_ALARM_REGS]; ++ int yearl, yearh; ++ int ret; ++ ++ ret = rockchip_rtc_stop_alarm(rtc); ++ if (ret) { ++ dev_err(dev, "Failed to stop alarm: %d\n", ret); ++ return ret; ++ } ++ dev_dbg(dev, ++ "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", ++ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, ++ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, ++ alrm->time.tm_min, alrm->time.tm_sec); ++ ++ alrm_data[0] = bin2bcd(alrm->time.tm_sec) | RTC_WRITE_MASK; ++ alrm_data[1] = bin2bcd(alrm->time.tm_min) | RTC_WRITE_MASK; ++ alrm_data[2] = bin2bcd(alrm->time.tm_hour) | RTC_WRITE_MASK; ++ alrm_data[3] = bin2bcd(alrm->time.tm_mday) | RTC_WRITE_MASK; ++ alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1) | RTC_WRITE_MASK; ++ if (alrm->time.tm_year > 199) { ++ yearh = (alrm->time.tm_year - 100) / 100; ++ yearl = alrm->time.tm_year - 100 - yearh * 100; ++ } else { ++ yearh = 0; ++ yearl = alrm->time.tm_year - 100 - yearh * 100; ++ } ++ alrm_data[5] = bin2bcd(yearl) | RTC_WRITE_MASK; ++ alrm_data[6] = bin2bcd(yearh) | RTC_WRITE_MASK; ++ ++ ret = regmap_bulk_write(rtc->regmap, ++ RTC_ALARM_SECONDS, ++ alrm_data, NUM_ALARM_REGS); ++ if (ret) { ++ dev_err(dev, "Failed to bulk write: %d\n", ret); ++ return ret; ++ } ++ if (alrm->enabled) { ++ ret = rockchip_rtc_start_alarm(rtc); ++ if (ret) { ++ dev_err(dev, "Failed to start alarm: %d\n", ret); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++static int rockchip_rtc_alarm_irq_enable(struct device *dev, ++ unsigned int enabled) ++{ ++ struct rockchip_rtc *rtc = dev_get_drvdata(dev); ++ ++ if (enabled) ++ return rockchip_rtc_start_alarm(rtc); ++ ++ return rockchip_rtc_stop_alarm(rtc); ++} ++ ++/* ++ * We will just handle setting the frequency and make use the framework for ++ * reading the periodic interrupts. ++ * ++ */ ++static irqreturn_t rockchip_rtc_alarm_irq(int irq, void *data) ++{ ++ struct rockchip_rtc *rtc = data; ++ int ret, status; ++ ++ ret = regmap_read(rtc->regmap, RTC_STATUS0, &status); ++ if (ret) { ++ pr_err("Failed to read RTC INT REG: %d\n", ret); ++ return ret; ++ } ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, status); ++ if (ret) { ++ pr_err("%s:Failed to update RTC status: %d\n", __func__, ret); ++ return ret; ++ } ++ if (status & ALARM_INT_STATUS) { ++ pr_info("Alarm by: %s\n", __func__); ++ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct rtc_class_ops rockchip_rtc_ops = { ++ .read_time = rockchip_rtc_read_time, ++ .set_time = rockchip_rtc_set_time, ++ .read_alarm = rockchip_rtc_readalarm, ++ .set_alarm = rockchip_rtc_setalarm, ++ .alarm_irq_enable = rockchip_rtc_alarm_irq_enable, ++}; ++ ++/* ++ * Due to the analog generator 32k clock affected by ++ * temperature, voltage, clock precision need test ++ * with the environment change. In rtc test, ++ * use 24M clock as reference clock to measure the 32k clock. ++ * Before start test 32k clock, we should enable clk32k test(0x80), ++ * and configure test length, when rtc test done(0x84[2]), ++ * latch the 24M clock domain counter, ++ * and read out the counter from rtc_test ++ * registers(0x8c~0x98) via apb bus. ++ * In RTC digital design, we set three level compensation, ++ * the compensation value due to the ++ * RTC 32k clock test result, and if we need compensation, ++ * we need configure the compensation enable bit. ++ * Comp every hour, compensation at last minute every hour, ++ * and support add time and sub time by the MSB bit. ++ * Comp every day, compensation at last minute in last hour every day, ++ * and support add time and sub time by the MSB bit. ++ * Comp every month, compensation at last minute ++ * in last hour in last day every month, ++ * and support add time and sub time by the MSB bit. ++ */ ++static void rockchip_rtc_compensation_delay_work(struct work_struct *work) ++{ ++ struct rockchip_rtc *rtc = container_of(work, struct rockchip_rtc, trim_work.work); ++ u64 camp; ++ u32 count[4], counts, g_ref, tcamp; ++ int ret, done = 0, trim_dir, c_hour, ++ c_day, c_det_day, c_mon, c_det_mon; ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CLK32K_TEST, ++ CLK32K_TEST_EN, CLK32K_TEST_EN); ++ if (ret) { ++ pr_err("%s:Failed to update RTC CLK32K TEST: %d\n", ++ __func__, ret); ++ return; ++ } ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_TEST_LEN, ++ CLK32K_TEST_LEN); ++ if (ret) { ++ pr_err("%s:Failed to update RTC CLK32K TEST LEN: %d\n", ++ __func__, ret); ++ return; ++ } ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_TEST_ST, ++ CLK32K_TEST_START, ++ CLK32K_TEST_START); ++ if (ret) { ++ pr_err("%s:Failed to update RTC CLK32K TEST STATUS : %d\n", ++ __func__, ret); ++ return; ++ } ++ ret = regmap_read_poll_timeout(rtc->regmap, RTC_TEST_ST, done, ++ (done & CLK32K_TEST_DONE), 20000, RTC_TIMEOUT); ++ if (ret) ++ pr_err("%s:timeout waiting for RTC TEST STATUS : %d\n", ++ __func__, ret); ++ ++ ret = regmap_bulk_read(rtc->regmap, ++ RTC_CNT_0, ++ count, 4); ++ if (ret) { ++ pr_err("Failed to read RTC count REG: %d\n", ret); ++ return; ++ } ++ ++ counts = count[0] | (count[1] << 8) | ++ (count[2] << 16) | (count[3] << 24); ++ g_ref = CLK32K_TEST_REF_CLK * (CLK32K_TEST_LEN + 1); ++ ++ if (counts > g_ref) { ++ trim_dir = 0; ++ camp = 36ULL * (32768 * (counts - g_ref)); ++ do_div(camp, (g_ref / 100)); ++ } else { ++ trim_dir = CLK32K_COMP_DIR_ADD; ++ camp = 36ULL * (32768 * (g_ref - counts)); ++ do_div(camp, (g_ref / 100)); ++ } ++ tcamp = (u32)camp; ++ c_hour = DIV_ROUND_CLOSEST(tcamp, 32768); ++ c_day = DIV_ROUND_CLOSEST(24 * tcamp, 32768); ++ c_mon = DIV_ROUND_CLOSEST(30 * 24 * tcamp, 32768); ++ ++ if (c_hour > 1) ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_H, bin2bcd((c_hour - 1)) | trim_dir); ++ else ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_H, CLK32K_NO_COMP); ++ ++ if (c_day > c_hour * 23) { ++ c_det_day = c_day - c_hour * 23; ++ trim_dir = CLK32K_COMP_DIR_ADD; ++ } else { ++ c_det_day = c_hour * 24 - c_day; ++ trim_dir = 0; ++ } ++ ++ if (c_det_day > 1) ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_D, ++ bin2bcd((c_det_day - 1)) | trim_dir); ++ else ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_D, CLK32K_NO_COMP); ++ ++ if (c_mon > (29 * c_day + 23 * c_hour)) { ++ c_det_mon = c_mon - 29 * c_day - 23 * c_hour; ++ trim_dir = CLK32K_COMP_DIR_ADD; ++ } else { ++ c_det_mon = 29 * c_day + 23 * c_hour - c_mon; ++ trim_dir = 0; ++ } ++ ++ if (c_det_mon) ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_M, ++ bin2bcd((c_det_mon - 1)) | trim_dir); ++ else ++ rockchip_rtc_write(rtc->regmap, RTC_COMP_M, CLK32K_NO_COMP); ++ ++ ret = regmap_read(rtc->regmap, RTC_CTRL, &done); ++ if (ret) { ++ pr_err("Failed to read RTC_CTRL: %d\n", ret); ++ return; ++ } ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL, ++ CLK32K_COMP_EN, CLK32K_COMP_EN); ++ if (ret) { ++ pr_err("%s:Failed to update RTC CTRL : %d\n", __func__, ret); ++ return; ++ } ++ return; ++} ++ ++static bool rockchip_rtc_is_trimed(struct rockchip_rtc *rtc) ++{ ++ int ret, comp_done; ++ ++ ret = regmap_read(rtc->regmap, RTC_CTRL, &comp_done); ++ if (ret) { ++ pr_err("%s: Failed to read RTC_CTRL: %d\n", __func__, ret); ++ return false; ++ } ++ return (comp_done & CLK32K_COMP_EN) == CLK32K_COMP_EN; ++} ++ ++static void rockchip_rtc_trim_start(struct rockchip_rtc *rtc) ++{ ++ if (!rockchip_rtc_is_trimed(rtc)) ++ queue_delayed_work(system_long_wq, &rtc->trim_work, ++ msecs_to_jiffies(5000)); ++} ++ ++static void __maybe_unused rockchip_rtc_trim_close(struct rockchip_rtc *rtc) ++{ ++ if (!rockchip_rtc_is_trimed(rtc)) ++ cancel_delayed_work_sync(&rtc->trim_work); ++} ++ ++/* Enable the alarm if it should be enabled (in case it was disabled to ++ * prevent use as a wake source). ++ */ ++#ifdef CONFIG_PM_SLEEP ++/* Turn off the alarm if it should not be a wake source. */ ++static int rockchip_rtc_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev); ++ ++ if (device_may_wakeup(dev)) ++ enable_irq_wake(rtc->irq); ++ ++ rockchip_rtc_trim_close(rtc); ++ ++ if (rtc->grf) { ++ switch (rtc->mode) { ++ case ROCKCHIP_RV1106_RTC: ++ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0, ++ (RTC_CLAMP_EN << 16)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ clk_bulk_disable_unprepare(rtc->num_clks, rtc->clks); ++ ++ return 0; ++} ++ ++/* Enable the alarm if it should be enabled (in case it was disabled to ++ * prevent use as a wake source). ++ */ ++static int rockchip_rtc_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev); ++ int ret; ++ ++ if (device_may_wakeup(dev)) ++ disable_irq_wake(rtc->irq); ++ ++ if (rtc->grf) { ++ switch (rtc->mode) { ++ case ROCKCHIP_RV1106_RTC: ++ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0, ++ (RTC_CLAMP_EN << 16) | RTC_CLAMP_EN); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ret = clk_bulk_prepare_enable(rtc->num_clks, rtc->clks); ++ if (ret) { ++ dev_err(dev, "Cannot enable clock.\n"); ++ return ret; ++ } ++ rockchip_rtc_trim_start(rtc); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(rockchip_rtc_pm_ops, ++ rockchip_rtc_suspend, rockchip_rtc_resume); ++ ++static const struct of_device_id rockchip_rtc_of_match[] = { ++ { ++ .compatible = "rockchip,rv1106-rtc", ++ .data = (void *)ROCKCHIP_RV1106_RTC ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rockchip_rtc_of_match); ++ ++static void rockchip_rtc_clk_disable(void *data) ++{ ++ struct rockchip_rtc *rtc = data; ++ ++ clk_bulk_disable_unprepare(rtc->num_clks, rtc->clks); ++} ++ ++static int rockchip_rtc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct rockchip_rtc *rtc; ++ int ret; ++ struct rtc_time tm_read, tm = { ++ .tm_wday = 0, ++ .tm_year = 121, ++ .tm_mon = 0, ++ .tm_mday = 1, ++ .tm_hour = 12, ++ .tm_min = 0, ++ .tm_sec = 0, ++ }; ++ ++ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); ++ if (!rtc) ++ return -ENOMEM; ++ ++ rtc->regmap = device_node_to_regmap(np); ++ if (IS_ERR(rtc->regmap)) ++ return dev_err_probe(dev, PTR_ERR(rtc->regmap), ++ "no regmap available\n"); ++ ++ rtc->mode = (uintptr_t)device_get_match_data(dev); ++ rtc->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(rtc->grf)) { ++ dev_warn(dev, "Missing rockchip,grf property\n"); ++ rtc->grf = NULL; ++ } else { ++ switch (rtc->mode) { ++ case ROCKCHIP_RV1106_RTC: ++ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0, ++ (RTC_CLAMP_EN << 16) | RTC_CLAMP_EN); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ platform_set_drvdata(pdev, rtc); ++ ++ rtc->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rtc->clks); ++ if (rtc->num_clks < 1) ++ return -ENODEV; ++ ret = clk_bulk_prepare_enable(rtc->num_clks, rtc->clks); ++ if (ret) ++ return dev_err_probe(dev, ret, "Cannot enable clock.\n"); ++ ret = devm_add_action_or_reset(dev, rockchip_rtc_clk_disable, rtc); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to add clk disable action."); ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_VPTAT_TRIM, ++ D2A_POR_REG_SEL1, ++ D2A_POR_REG_SEL1); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to write RTC_VPTAT_TRIM\n"); ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_ANALOG_EN, ++ D2A_POR_REG_SEL0, ++ 0x00); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to write RTC_ANALOG_EN\n"); ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_LDO_CTRL, ++ RTC_D2A_XO_EN, ++ RTC_D2A_XO_EN); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to update RTC_LDO_CTRL\n"); ++ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_ANALOG_EN, ++ RTC_D2A_CLK_OUT_EN, ++ RTC_D2A_CLK_OUT_EN); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to update RTC_ANALOG_EN\n"); ++ ++ /* start rtc running by default, and use shadowed timer. */ ++ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL, ++ RTC_CTRL_REG_START_RTC | ++ RTC_CTRL_REG_RTC_READSEL_M, ++ RTC_CTRL_REG_RTC_READSEL_M | ++ RTC_CTRL_REG_START_RTC); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to update RTC control\n"); ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, RTC_STATUS_MASK); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to write RTC status0\n"); ++ ++ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, 0); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to write RTC status0\n"); ++ ++ device_init_wakeup(&pdev->dev, 1); ++ ++ rockchip_rtc_read_time(&pdev->dev, &tm_read); ++ if (rtc_valid_tm(&tm_read) != 0) ++ rockchip_rtc_set_time(&pdev->dev, &tm); ++ ++ rtc->rtc = devm_rtc_allocate_device(&pdev->dev); ++ if (IS_ERR(rtc->rtc)) ++ return PTR_ERR(rtc->rtc); ++ ++ rtc->rtc->ops = &rockchip_rtc_ops; ++ ++ rtc->irq = platform_get_irq(pdev, 0); ++ if (rtc->irq < 0) ++ return dev_err_probe(&pdev->dev, rtc->irq, "No IRQ resource\n"); ++ ++ /* request alarm irq of rtc */ ++ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, ++ &rockchip_rtc_alarm_irq, IRQF_ONESHOT, ++ "RTC alarm", rtc); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to request alarm IRQ %d\n", ++ rtc->irq); ++ ++ INIT_DELAYED_WORK(&rtc->trim_work, rockchip_rtc_compensation_delay_work); ++ rockchip_rtc_trim_start(rtc); ++ ++ return devm_rtc_register_device(rtc->rtc); ++} ++ ++static struct platform_driver rockchip_rtc_driver = { ++ .probe = rockchip_rtc_probe, ++ .driver = { ++ .name = "rockchip-rtc", ++ .pm = &rockchip_rtc_pm_ops, ++ .of_match_table = rockchip_rtc_of_match, ++ }, ++}; ++ ++module_platform_driver(rockchip_rtc_driver); ++ ++MODULE_DESCRIPTION("RTC driver for the rockchip"); ++MODULE_AUTHOR("Zhang Qing "); ++MODULE_LICENSE("GPL"); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0016-thermal-rockchip-Add-support-for-RK3506.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0016-thermal-rockchip-Add-support-for-RK3506.patch new file mode 100644 index 000000000..316465534 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0016-thermal-rockchip-Add-support-for-RK3506.patch @@ -0,0 +1,177 @@ +From b4c96d43b18506728a339871188209fb52fda313 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 6 Apr 2025 01:59:17 +0200 +Subject: [PATCH 16/84] thermal: rockchip: Add support for RK3506 + +Adapted from BSP. + +Signed-off-by: Ondrej Jirman +--- + drivers/thermal/rockchip_thermal.c | 88 +++++++++++++++++++++++++++++- + 1 file changed, 87 insertions(+), 1 deletion(-) + +diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c +index 5dc1e8f27774..44bf6dad8f16 100644 +--- a/drivers/thermal/rockchip_thermal.c ++++ b/drivers/thermal/rockchip_thermal.c +@@ -106,6 +106,7 @@ struct rockchip_tsadc_chip { + void __iomem *reg, enum tshut_polarity p); + void (*irq_ack)(void __iomem *reg); + void (*control)(void __iomem *reg, bool on); ++ void (*suspend)(struct regmap *grf, void __iomem *reg); + + /* Per-sensor methods */ + int (*get_temp)(const struct chip_tsadc_table *table, +@@ -222,6 +223,7 @@ struct rockchip_thermal_data { + #define TSADCV2_AUTO_TSHUT_POLARITY_MASK BIT(24) + + #define TSADCV3_AUTO_Q_SEL_EN BIT(1) ++#define TSADCV3_AUTO_Q_SEL_EN_MASK BIT(17) + + #define TSADCV2_INT_SRC_EN(chn) BIT(chn) + #define TSADCV2_INT_SRC_EN_MASK(chn) BIT(16 + (chn)) +@@ -255,6 +257,9 @@ struct rockchip_thermal_data { + #define TSADCV9_PD_MODE (0x10001 << 4) + #define TSADCV9_Q_MAX_VAL (0xffff0400 << 0) + ++#define TSADCV13_AUTO_PERIOD_TIME 2500 /* 2.5ms */ ++#define TSADCV13_Q_MAX_VAL 0x3ff /* 10bit 1023 */ ++ + #define GRF_SARADC_TESTBIT 0x0e644 + #define GRF_TSADC_TESTBIT_L 0x0e648 + #define GRF_TSADC_TESTBIT_H 0x0e64c +@@ -265,6 +270,8 @@ struct rockchip_thermal_data { + + #define PX30_GRF_SOC_CON2 0x0408 + ++#define RK3506_GRF_TSADC_CON 0x10 ++ + #define RK3568_GRF_TSADC_CON 0x0600 + #define RK3568_GRF_TSADC_ANA_REG0 (0x10001 << 0) + #define RK3568_GRF_TSADC_ANA_REG1 (0x10001 << 1) +@@ -284,7 +291,8 @@ struct rockchip_thermal_data { + + #define GRF_CON_TSADC_CH_INV (0x10001 << 1) + +- ++/* -40 to 125 is reliable, outside the range existed unreliability */ ++#define RK_MIN_TEMP (-60000) + #define RK_MAX_TEMP (180000) + + /** +@@ -545,6 +553,17 @@ static const struct tsadc_table rk3399_code_table[] = { + {TSADCV3_DATA_MASK, 125000}, + }; + ++static const struct tsadc_table rk3506_code_table[] = { ++ {0, RK_MIN_TEMP}, ++ {362, RK_MIN_TEMP}, ++ {395, -40000}, ++ {503, 25000}, ++ {604, 85000}, ++ {672, 125000}, ++ {757, RK_MAX_TEMP}, ++ {TSADCV2_DATA_MASK, RK_MAX_TEMP}, ++}; ++ + static const struct tsadc_table rk3568_code_table[] = { + {0, -40000}, + {1584, -40000}, +@@ -911,6 +930,38 @@ static void rk_tsadcv9_initialize(struct regmap *grf, void __iomem *regs, + regs + TSADCV2_AUTO_CON); + } + ++static void rk_tsadcv13_initialize(struct regmap *grf, void __iomem *regs, ++ enum tshut_polarity tshut_polarity) ++{ ++ regmap_write(grf, RK3506_GRF_TSADC_CON, RV1106_VOGRF_TSADC_TSEN); ++ udelay(10); ++ regmap_write(grf, RK3506_GRF_TSADC_CON, RV1106_VOGRF_TSADC_ANA); ++ udelay(100); ++ ++ writel_relaxed(TSADCV13_AUTO_PERIOD_TIME, regs + TSADCV3_AUTO_PERIOD); ++ writel_relaxed(TSADCV13_AUTO_PERIOD_TIME, ++ regs + TSADCV3_AUTO_PERIOD_HT); ++ writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, ++ regs + TSADCV3_HIGHT_INT_DEBOUNCE); ++ writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, ++ regs + TSADCV3_HIGHT_TSHUT_DEBOUNCE); ++ writel_relaxed(TSADCV13_Q_MAX_VAL, regs + TSADCV9_Q_MAX); ++ if (tshut_polarity == TSHUT_HIGH_ACTIVE) ++ writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_HIGH | ++ TSADCV2_AUTO_TSHUT_POLARITY_MASK, ++ regs + TSADCV2_AUTO_CON); ++ else ++ writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_MASK, ++ regs + TSADCV2_AUTO_CON); ++ writel_relaxed(TSADCV3_AUTO_Q_SEL_EN | TSADCV3_AUTO_Q_SEL_EN_MASK, ++ regs + TSADCV2_AUTO_CON); ++} ++ ++static void rk3506_tsadc_suspend(struct regmap *grf, void __iomem *regs) ++{ ++ regmap_write(grf, RK3506_GRF_TSADC_CON, RV1106_VOGRF_TSADC_TSEN & 0xffff0000); ++} ++ + static void rk_tsadcv2_irq_ack(void __iomem *regs) + { + u32 val; +@@ -1353,6 +1404,34 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { + }, + }; + ++static const struct rockchip_tsadc_chip rk3506_tsadc_data = { ++ /* cpu */ ++ .chn_offset = 0, ++ .chn_num = 1, ++ ++ .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ ++ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ ++ .tshut_temp = 95000, ++ ++ .initialize = rk_tsadcv13_initialize, ++ .suspend = rk3506_tsadc_suspend, ++ .irq_ack = rk_tsadcv4_irq_ack, ++ .control = rk_tsadcv4_control, ++ .get_temp = rk_tsadcv4_get_temp, ++ .set_alarm_temp = rk_tsadcv3_alarm_temp, ++ .set_tshut_temp = rk_tsadcv3_tshut_temp, ++ .set_tshut_mode = rk_tsadcv4_tshut_mode, ++ //.get_trim_code = rk_tsadcv2_get_trim_code, ++ //.trim_slope = 594, ++ ++ .table = { ++ .id = rk3506_code_table, ++ .length = ARRAY_SIZE(rk3506_code_table), ++ .data_mask = TSADCV3_DATA_MASK, ++ .mode = ADC_INCREMENT, ++ }, ++}; ++ + static const struct rockchip_tsadc_chip rk3568_tsadc_data = { + /* cpu, gpu */ + .chn_offset = 0, +@@ -1460,6 +1539,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { + .compatible = "rockchip,rk3399-tsadc", + .data = (void *)&rk3399_tsadc_data, + }, ++ { ++ .compatible = "rockchip,rk3506-tsadc", ++ .data = (void *)&rk3506_tsadc_data, ++ }, + { + .compatible = "rockchip,rk3568-tsadc", + .data = (void *)&rk3568_tsadc_data, +@@ -1894,6 +1977,9 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) + + thermal->chip->control(thermal->regs, false); + ++ if (thermal->chip->suspend) ++ thermal->chip->suspend(thermal->grf, thermal->regs); ++ + clk_disable(thermal->pclk); + clk_disable(thermal->clk); + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0017-mtd-spi-nor-gigadevice-Add-support-for-gd25lq128e.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0017-mtd-spi-nor-gigadevice-Add-support-for-gd25lq128e.patch new file mode 100644 index 000000000..fa318eaab --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0017-mtd-spi-nor-gigadevice-Add-support-for-gd25lq128e.patch @@ -0,0 +1,33 @@ +From c526256757f45b4afd820f4703e85156a5f4d37e Mon Sep 17 00:00:00 2001 +From: Martijn Braam +Date: Sat, 29 Jan 2022 23:06:29 +0100 +Subject: [PATCH 17/84] mtd: spi-nor: gigadevice: Add support for gd25lq128e + +The GD25LQ128EWIGR reports an JEDEC id with a different vendor, +otherwise it seems to fit neatly in the gigadevice list. + +Signed-off-by: Martijn Braam +--- + drivers/mtd/spi-nor/gigadevice.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c +index ef1edd0add70..44ec20a9b696 100644 +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -82,6 +82,12 @@ static const struct flash_info gigadevice_nor_parts[] = { + .size = SZ_16M, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, { ++ .id = SNOR_ID(0x25, 0x70, 0x18), ++ .name = "gd25lq128e", ++ .size = SZ_16M, ++ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0018-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0018-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch new file mode 100644 index 000000000..c2d4bb386 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0018-clk-rk3399-Export-SCLK_CIF_OUT_SRC-to-device-tree.patch @@ -0,0 +1,41 @@ +From 9383b1fc0830fc3a27d82fe538fb1e594525e9c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 22 Oct 2021 18:09:09 +0200 +Subject: [PATCH 18/84] clk: rk3399: Export SCLK_CIF_OUT_SRC to device tree + +So that it can be used in assigned-clock-parents. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/rockchip/clk-rk3399.c | 2 +- + include/dt-bindings/clock/rk3399-cru.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c +index c2b243d7a5e2..053983966b2f 100644 +--- a/drivers/clk/rockchip/clk-rk3399.c ++++ b/drivers/clk/rockchip/clk-rk3399.c +@@ -1259,7 +1259,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { + RK3399_CLKGATE_CON(27), 6, GFLAGS), + + /* cif */ +- COMPOSITE_NODIV(0, "clk_cifout_src", mux_pll_src_cpll_gpll_npll_p, 0, ++ COMPOSITE_NODIV(SCLK_CIF_OUT_SRC, "clk_cifout_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(56), 6, 2, MFLAGS, + RK3399_CLKGATE_CON(10), 7, GFLAGS), + +diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h +index 4c90c7703a83..9412df3ef6d8 100644 +--- a/include/dt-bindings/clock/rk3399-cru.h ++++ b/include/dt-bindings/clock/rk3399-cru.h +@@ -125,6 +125,7 @@ + #define SCLK_DDRC 168 + #define SCLK_TESTCLKOUT1 169 + #define SCLK_TESTCLKOUT2 170 ++#define SCLK_CIF_OUT_SRC 171 + + #define DCLK_VOP0 180 + #define DCLK_VOP1 181 +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0019-clk-rockchip-rk3399-Don-t-allow-to-reparent-dclk_vop.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0019-clk-rockchip-rk3399-Don-t-allow-to-reparent-dclk_vop.patch new file mode 100644 index 000000000..ff653621a --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0019-clk-rockchip-rk3399-Don-t-allow-to-reparent-dclk_vop.patch @@ -0,0 +1,35 @@ +From 6f8589b9204a397d5deaa8edd475322b9ec72759 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 24 Oct 2022 03:17:57 +0200 +Subject: [PATCH 19/84] clk: rockchip: rk3399: Don't allow to reparent + dclk_vop0 to frac clock + +frac clock requires 20x difference between intput and output, and kernel +code doesn't respect this requirement. + +Signed-off-by: Ondrej Jirman +--- + drivers/clk/rockchip/clk-rk3399.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c +index 053983966b2f..1f592d2d2d85 100644 +--- a/drivers/clk/rockchip/clk-rk3399.c ++++ b/drivers/clk/rockchip/clk-rk3399.c +@@ -279,11 +279,11 @@ static struct rockchip_clk_branch rk3399_uart4_pmu_fracmux __initdata = + RK3399_PMU_CLKSEL_CON(5), 8, 2, MFLAGS); + + static struct rockchip_clk_branch rk3399_dclk_vop0_fracmux __initdata = +- MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT, ++ MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + RK3399_CLKSEL_CON(49), 11, 1, MFLAGS); + + static struct rockchip_clk_branch rk3399_dclk_vop1_fracmux __initdata = +- MUX(DCLK_VOP1, "dclk_vop1", mux_dclk_vop1_p, CLK_SET_RATE_PARENT, ++ MUX(DCLK_VOP1, "dclk_vop1", mux_dclk_vop1_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + RK3399_CLKSEL_CON(50), 11, 1, MFLAGS); + + static struct rockchip_clk_branch rk3399_pmuclk_wifi_fracmux __initdata = +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0020-power-supply-Add-support-for-USB_BC_ENABLED-and-USB_.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0020-power-supply-Add-support-for-USB_BC_ENABLED-and-USB_.patch new file mode 100644 index 000000000..3909ff4e7 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0020-power-supply-Add-support-for-USB_BC_ENABLED-and-USB_.patch @@ -0,0 +1,45 @@ +From 2b6b4c84b9ee0ad52817dac708e52962033028bb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Mon, 22 Jun 2020 02:28:01 +0200 +Subject: [PATCH 20/84] power: supply: Add support for USB_BC_ENABLED and + USB_DCP_INPUT_CURRENT_LIMIT + +- USB_BC_ENABLED - enabling/disabling BC 1.2 USB type detection +- USB_DCP_INPUT_CURRENT_LIMIT - setting a default current limit for DCP/CDP + ports + +Signed-off-by: Ondrej Jirman +--- + drivers/power/supply/power_supply_sysfs.c | 2 ++ + include/linux/power_supply.h | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c +index f30a7b9ccd5e..5336a1737d5e 100644 +--- a/drivers/power/supply/power_supply_sysfs.c ++++ b/drivers/power/supply/power_supply_sysfs.c +@@ -218,6 +218,8 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = { + POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), + POWER_SUPPLY_ENUM_ATTR(TYPE), + POWER_SUPPLY_ENUM_ATTR(USB_TYPE), ++ POWER_SUPPLY_ATTR(USB_BC_ENABLED), ++ POWER_SUPPLY_ATTR(USB_DCP_INPUT_CURRENT_LIMIT), + POWER_SUPPLY_ENUM_ATTR(SCOPE), + POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), + POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +index 7a5e4c3242a0..161cf02e1f71 100644 +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -168,6 +168,8 @@ enum power_supply_property { + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ ++ POWER_SUPPLY_PROP_USB_BC_ENABLED, ++ POWER_SUPPLY_PROP_USB_DCP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0021-mfd-rk8xx-Enable-rk808-clkout2-function.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0021-mfd-rk8xx-Enable-rk808-clkout2-function.patch new file mode 100644 index 000000000..75ad5d1ec --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0021-mfd-rk8xx-Enable-rk808-clkout2-function.patch @@ -0,0 +1,44 @@ +From 33fe1a73fcef2b401a5b2bae0dc15809836d3604 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= +Date: Mon, 4 Jan 2021 17:57:49 +0100 +Subject: [PATCH 21/84] mfd: rk8xx: Enable rk808-clkout2 function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +??? + +Signed-of-by: Kamil TrzciÅ„ski +--- + drivers/mfd/rk8xx-core.c | 1 + + include/linux/mfd/rk808.h | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c +index 3dcf6abfda74..8dd162a5b124 100644 +--- a/drivers/mfd/rk8xx-core.c ++++ b/drivers/mfd/rk8xx-core.c +@@ -304,6 +304,7 @@ static const struct rk808_reg_data rk818_pre_init_reg[] = { + { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | + VB_LO_SEL_3500MV }, ++ { RK808_CLK32OUT_REG, CLK32KOUT2_FUNC_MASK, CLK32KOUT2_FUNC }, + }; + + static const struct regmap_irq rk801_irqs[] = { +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index 7ffc904c864c..5353a470128d 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -1051,6 +1051,8 @@ enum rk806_dvs_mode { + + #define VOUT_LO_INT BIT(0) + #define CLK32KOUT2_EN BIT(0) ++#define CLK32KOUT2_FUNC (0 << 1) ++#define CLK32KOUT2_FUNC_MASK BIT(1) + + #define TEMP105C 0x08 + #define TEMP115C 0x0c +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0022-power-supply-rk818_battery-Add-battery-charger-drive.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0022-power-supply-rk818_battery-Add-battery-charger-drive.patch new file mode 100644 index 000000000..1865d00fc --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0022-power-supply-rk818_battery-Add-battery-charger-drive.patch @@ -0,0 +1,2598 @@ +From 5e2319782b4fdf05161d264dde23ac10dc1b8949 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 11 Jun 2023 18:29:24 +0200 +Subject: [PATCH 22/84] power: supply: rk818_battery: Add battery charger + driver for RK818 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RK818 is a PMIC used on Pinephone. This is a battery charger driver +for it. + +Signed-of-by: OndÅ™ej Jirman +--- + drivers/mfd/rk8xx-core.c | 1 + + drivers/mfd/rk8xx-i2c.c | 38 +- + drivers/power/supply/Kconfig | 8 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/rk818_battery.c | 2369 ++++++++++++++++++++++++++ + include/linux/mfd/rk808.h | 81 +- + 6 files changed, 2495 insertions(+), 3 deletions(-) + create mode 100644 drivers/power/supply/rk818_battery.c + +diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c +index 8dd162a5b124..f43a886e91bf 100644 +--- a/drivers/mfd/rk8xx-core.c ++++ b/drivers/mfd/rk8xx-core.c +@@ -145,6 +145,7 @@ static const struct mfd_cell rk817s[] = { + static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, ++ { .name = "rk818-charger", .of_compatible = "rockchip,rk818-charger", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), +diff --git a/drivers/mfd/rk8xx-i2c.c b/drivers/mfd/rk8xx-i2c.c +index 2951b2911a37..1f5b291e127c 100644 +--- a/drivers/mfd/rk8xx-i2c.c ++++ b/drivers/mfd/rk8xx-i2c.c +@@ -132,13 +132,47 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg) + return false; + } + ++static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ /* ++ * Notes: ++ * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but ++ * we don't use that feature. It's better to cache. ++ * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since ++ * bits are cleared in case when we shutoff anyway, but better safe. ++ */ ++ ++ switch (reg) { ++ case RK808_SECONDS_REG ... RK808_WEEKS_REG: ++ case RK808_RTC_STATUS_REG: ++ case RK808_VB_MON_REG: ++ case RK808_THERMAL_REG: ++ case RK808_DCDC_EN_REG: ++ case RK808_LDO_EN_REG: ++ case RK808_DCDC_UV_STS_REG: ++ case RK808_LDO_UV_STS_REG: ++ case RK808_DCDC_PG_REG: ++ case RK808_LDO_PG_REG: ++ case RK808_DEVCTRL_REG: ++ case RK808_INT_STS_REG1: ++ case RK808_INT_STS_REG2: ++ case RK808_INT_STS_MSK_REG1: ++ case RK808_INT_STS_MSK_REG2: ++ case RK818_LDO8_ON_VSEL_REG: // TODO(ayufan):?? ++ case RK818_LDO8_SLP_VSEL_REG: // TODO(ayufan):?? ++ case RK818_SUP_STS_REG ... RK818_SAVE_DATA19: ++ return true; ++ } ++ ++ return false; ++} + + static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +- .max_register = RK818_USB_CTRL_REG, ++ .max_register = RK818_SAVE_DATA19, + .cache_type = REGCACHE_MAPLE, +- .volatile_reg = rk808_is_volatile_reg, ++ .volatile_reg = rk818_is_volatile_reg, + }; + + static const struct regmap_config rk801_regmap_config = { +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index 83392ed6a8da..9a8401fc76fa 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -1165,4 +1165,12 @@ config MACSMC_POWER + + Say Y or M here if you have an Apple Silicon based Mac. + ++config CHARGER_RK818 ++ bool "RK818 Charger driver" ++ depends on MFD_RK8XX ++ default n ++ help ++ If you say yes here you will get support for the charger of RK818 PMIC. ++ This driver can give support for Rk818 Charger Interface. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 7ee839dca7f3..087d75a200de 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -131,3 +131,4 @@ obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o + obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o + obj-$(CONFIG_MACSMC_POWER) += macsmc-power.o + obj-$(CONFIG_CHARGER_MAX77759) += max77759_charger.o ++obj-$(CONFIG_CHARGER_RK818) += rk818_battery.o +diff --git a/drivers/power/supply/rk818_battery.c b/drivers/power/supply/rk818_battery.c +new file mode 100644 +index 000000000000..8cac751f8644 +--- /dev/null ++++ b/drivers/power/supply/rk818_battery.c +@@ -0,0 +1,2369 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * rk818 battery and charger driver ++ * ++ * Copyright (c) 2026 Ondrej Jirman ++ * ++ * Design: ++ * - Coulomb counter is the primary SoC source; it is accurate when ++ * COFFSET is correctly calibrated. ++ * - OCV (open-circuit voltage) and relaxed voltage are used to ++ * periodically re-anchor the coulomb counter. ++ * - Display SoC (dsoc) tracks real SoC (rsoc) through a simple ++ * rate-limiter so it never jumps more than 1% per poll cycle. ++ * - At charge completion the coulomb counter is snapped to FCC. ++ * - COFFSET auto-calibration runs once per charge cycle when the ++ * charger reports RK818_CHG_STS_TERMINATED and current appears near zero. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* RK818_TS_CTRL_REG */ ++#define GG_EN BIT(7) ++#define TS1_CUR_MSK 0x03 ++#define ADC_CUR_20UA 0x00 ++#define ADC_CUR_40UA 0x01 ++#define ADC_CUR_60UA 0x02 ++#define ADC_CUR_80UA 0x03 ++ ++/* NTC battery temperature sensing on TS1 */ ++#define NTC_CALC_FACTOR_80UA 7 ++#define NTC_CALC_FACTOR_60UA 9 ++#define NTC_CALC_FACTOR_40UA 13 ++#define NTC_CALC_FACTOR_20UA 27 ++#define NTC_80UA_MAX_MEASURE 27500 ++#define NTC_60UA_MAX_MEASURE 36666 ++#define NTC_40UA_MAX_MEASURE 55000 ++#define NTC_20UA_MAX_MEASURE 110000 ++ ++/* RK818_ADC_CTRL_REG */ ++#define ADC_CUR_EN BIT(6) ++#define ADC_TS1_EN BIT(5) ++#define ADC_TS2_EN BIT(4) ++ ++/* RK818_GGCON_REG */ ++#define OCV_SAMP_MIN_MSK 0x0c ++#define OCV_SAMP_8MIN (0x00 << 2) ++#define ADC_CAL_MIN_MSK 0x30 ++#define ADC_CAL_8MIN (0x00 << 4) ++#define ADC_CUR_MODE BIT(1) ++ ++/* RK818_GGSTS_REG */ ++#define BAT_CON BIT(4) ++#define RELAX_VOL1_UPD BIT(3) ++#define RELAX_VOL2_UPD BIT(2) ++#define RELAX_VOL12_UPD_MSK (RELAX_VOL1_UPD | RELAX_VOL2_UPD) ++#define RELAX_VOL_STS BIT(0) ++ ++/* RK818_SUP_STS_REG */ ++#define BAT_EXS BIT(7) ++ ++#define RK818_CHG_STS_MASK (7u << 4) /* charger status */ ++#define RK818_CHG_STS_NONE (0u << 4) ++#define RK818_CHG_STS_WAKEUP_CUR (1u << 4) ++#define RK818_CHG_STS_TRICKLE_CUR (2u << 4) ++#define RK818_CHG_STS_CC_OR_CV (3u << 4) ++#define RK818_CHG_STS_TERMINATED (4u << 4) ++#define RK818_CHG_STS_USB_OV (5u << 4) ++#define RK818_CHG_STS_BAT_TEMP_FAULT (6u << 4) ++#define RK818_CHG_STS_TIMEOUT (7u << 4) ++ ++#define RK818_SUP_STS_USB_VLIM_EN BIT(3) /* input voltage limit enable */ ++#define RK818_SUP_STS_USB_ILIM_EN BIT(2) /* input current limit enable */ ++#define RK818_SUP_STS_USB_EXS BIT(1) /* USB power connected */ ++#define RK818_SUP_STS_USB_EFF BIT(0) /* USB fault */ ++ ++/* RK818_VB_MON_REG */ ++#define PLUG_IN_STS BIT(6) ++#define RK818_VBAT_LOW_3V4 0x06 ++ ++/* RK818_MISC_MARK_REG */ ++#define FG_INIT BIT(5) ++ ++/* RK818_INT_STS_MSK_REG1 */ ++#define VB_LOW_INT_EN BIT(1) ++ ++/* RK818_USB_CTRL_REG */ ++#define CHRG_CT_EN BIT(7) ++#define FINISH_CUR_MSK 0xc0 ++#define FINISH_100MA (0x00 << 6) ++#define FINISH_150MA (0x01 << 6) ++#define FINISH_200MA (0x02 << 6) ++#define FINISH_250MA (0x03 << 6) ++/* ++ * CHRG_TIMER_CCCV: CC/CV charging timeout threshold, REG2 bits [2:0]: ++ * 000: 4h, 001: 5h, 010: 6h, 011: 8h, ++ * 100: 10h, 101: 12h, 110: 14h, 111: 16h ++ */ ++#define CHRG_TIMER_CCCV_MSK 0x07 ++#define CHRG_TIMER_CCCV_4H 0x00 ++#define CHRG_TIMER_CCCV_5H 0x01 ++#define CHRG_TIMER_CCCV_6H 0x02 ++#define CHRG_TIMER_CCCV_8H 0x03 ++#define CHRG_TIMER_CCCV_10H 0x04 ++#define CHRG_TIMER_CCCV_12H 0x05 ++#define CHRG_TIMER_CCCV_14H 0x06 ++#define CHRG_TIMER_CCCV_16H 0x07 ++/* ++ * HOTDIE_TEMP: Hot-die temperature threshold, thermal reg bits [3:2]: ++ * 00: 85C, 01: 95C, 10: 105C, 11: 115C ++ */ ++#define HOTDIE_TEMP_MASK (0x3 << 2) ++#define HOTDIE_TEMP_85C (0x0 << 2) ++#define HOTDIE_TEMP_95C (0x1 << 2) ++#define HOTDIE_TEMP_105C (0x2 << 2) ++#define HOTDIE_TEMP_115C (0x3 << 2) ++ ++#define RK818_USB_CTRL_USB_ILIM_MASK (0xfu) ++#define RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET 4 ++#define RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK (0x7u << 4) ++ ++/* RK818_CHRG_CTRL_REG1 */ ++#define RK818_CHRG_CTRL_REG1_CHRG_EN BIT(7) ++#define RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET 4 ++#define RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK (0x7u << 4) ++#define RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET 0 ++#define RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK (0xfu << 0) ++ ++/* RK818_CHRG_CTRL_REG3 */ ++#define CHRG_TERM_DIG_SIGNAL BIT(5) ++#define CHRG_TIMER_CCCV_EN BIT(2) ++#define RK818_CHRG_CTRL_REG3_CHRG_TERM_DIGITAL BIT(5) ++ ++/* Current offset calibration */ ++#define COFFSET_MIN 0x780 ++#define COFFSET_MAX 0x980 ++#define COFFSET_DEFAULT 0x832 ++#define POFFSET_DEFAULT 42 ++/* Threshold in raw ADC counts below which current is considered zero (~12 mA) */ ++#define ADC_ZERO_THRESHOLD 4 ++#define ADC_CALIB_ITERATIONS 5 ++#define ADC_CALIB_MAX_RUNS 5 ++#define ADC_CALIB_MIN_BOOT_SEC (3 * 60) ++ ++/* SoC / capacity */ ++#define MIN_FCC 500 /* mAh */ ++ ++/* ++ * Coulomb counter LSB denominator. ++ * raw_counts / CC_LSB_DENOM * res_div = mAh (for a 20 mOhm sense resistor) ++ */ ++#define CC_LSB_DENOM 2390 ++ ++/* Maximum display SoC change per monitor cycle */ ++#define DSOC_DELTA_MAX 1 /* % */ ++ ++/* ++ * Conservative current estimate used to cross-check dsoc during sleep. ++ * The coulomb counter keeps running, so this is only used to bound ++ * how far dsoc can lag behind rsoc after a long suspend without a ++ * charger attached. ++ */ ++#define SLP_CURR_MA 20 ++ ++#define MONITOR_MS_DEFAULT (5 * 1000) ++ ++/* Safe non-zero divisor */ ++#define DIV(x) ((x) ? (x) : 1) ++ ++/* ------------------------------------------------------------------------- ++ * Platform data ++ * ------------------------------------------------------------------------- */ ++ ++/* ++ * Vendor-specific battery tunables only. ++ * ++ * Standard simple-battery properties (design capacity, design voltages, ++ * max charge current/voltage, internal resistance, OCV table, ...) are ++ * fetched by the power-supply core from the "monitored-battery" phandle ++ * and exposed via psy->battery_info. They must not be duplicated here. ++ */ ++ ++/* ------------------------------------------------------------------------- ++ * Driver state ++ * ------------------------------------------------------------------------- */ ++ ++/* ++ * Single entry in the NTC battery thermistor lookup table. ++ * Entries are sorted by temperature ascending; resistance therefore ++ * decreases monotonically as the index grows (NTC: cold = high R). ++ */ ++struct rk818_ntc_entry { ++ s32 temp; /* whole degrees Celsius */ ++ u32 res; /* thermistor resistance, ohms */ ++}; ++ ++struct rk818_battery { ++ struct platform_device *pdev; ++ struct rk808 *rk818; ++ struct regmap *regmap; ++ struct device *dev; ++ struct power_supply_battery_info *info; ++ struct workqueue_struct *wq; ++ struct delayed_work work; ++ ++ /* Device-tree tunables (previously in struct battery_platform_data) */ ++ u32 monitor_sec; ++ u32 sense_resist_mohm; /* 10 or 20 */ ++ u32 relax_entry_current_ma; ++ u32 relax_exit_current_ma; ++ u32 max_soc_offset_pct; ++ u32 feedback_temp; /* Celsius, 0 = disabled */ ++ u32 cccv_timeout_h; /* CC/CV safety timer hours; 0 = disabled */ ++ ++ /* NTC battery thermistor (TS1) */ ++ u32 ntc_size; ++ u32 ntc_factor; ++ u32 ntc_uA; ++ struct rk818_ntc_entry *ntc_entries; ++ int temperature; /* tenths of a degree C */ ++ ++ /* Boottime of last periodic calibration; see rk818_battery_work() */ ++ ktime_t last_calib; ++ ++ /* Power supplies */ ++ struct power_supply *usb_psy; ++ struct power_supply *charger_psy; ++ ++ /* USB input current limit state */ ++ bool apply_ilim; ++ ++ /* ++ * Voltage ADC calibration coefficients. ++ * voltage_mV = raw_adc * voltage_k / 1000 + voltage_b ++ */ ++ int voltage_k; ++ int voltage_b; ++ ++ /* Persistent current offset saved across reboots via POFFSET_REG */ ++ int poffset; ++ ++ /* ++ * Sense resistor divisor: ++ * 1 for 20 mOhm (default), 2 for 10 mOhm. ++ * Used to scale coulomb counter and current readings. ++ */ ++ u8 res_div; ++ ++ /* Latest measurements, updated each monitor cycle */ ++ int voltage_avg_mv; ++ int voltage_ocv_mv; ++ int voltage_relax_mv; ++ int current_avg_ma; ++ int remain_cap_mah; ++ int fcc_mah; ++ int rsoc; ++ int dsoc; ++ ++ int chrg_status; ++ bool is_charging; ++ ++ /* COFFSET auto-calibration state, reset each charge cycle */ ++ bool coffset_calib_done; ++ u32 coffset_calib_runs; ++ ++ /* Suspend/resume bookkeeping */ ++ ktime_t suspend_time; ++ bool sleep_chrg_online; ++ ++ /* Startup flags */ ++ bool is_first_power_on; ++ bool is_initialized; ++ u32 pwroff_min; ++ ++ u32 monitor_ms; ++ unsigned long boot_base_jiffies; ++}; ++ ++/* ------------------------------------------------------------------------- ++ * Time helpers ++ * ------------------------------------------------------------------------- */ ++ ++static u64 rk818_sec_since(unsigned long base_jiffies) ++{ ++ if (!base_jiffies) ++ return 0; ++ return jiffies_to_msecs(jiffies - base_jiffies) / 1000; ++} ++ ++/* ------------------------------------------------------------------------- ++ * Register access ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_read(struct rk818_battery *di, u8 reg) ++{ ++ int ret, val; ++ ++ ret = regmap_read(di->regmap, reg, &val); ++ if (ret) ++ dev_err(di->dev, "read reg 0x%02x failed: %d\n", reg, ret); ++ return val; ++} ++ ++static void rk818_write(struct rk818_battery *di, u8 reg, u8 val) ++{ ++ int ret; ++ ++ ret = regmap_write(di->regmap, reg, val); ++ if (ret) ++ dev_err(di->dev, "write reg 0x%02x failed: %d\n", reg, ret); ++} ++ ++static void rk818_clrsetbits(struct rk818_battery *di, u8 reg, u8 mask, u8 val) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(di->regmap, reg, mask, val); ++ if (ret) ++ dev_err(di->dev, "update reg 0x%02x failed: %d\n", reg, ret); ++} ++ ++/* Forward declarations for helpers used before their definition. */ ++static int rk818_charger_set_current_max(struct rk818_battery *di, int val); ++static int rk818_charger_set_voltage_max(struct rk818_battery *di, int val); ++ ++/* ++ * Read a big-endian 16-bit register pair and apply linear voltage calibration. ++ * @regh is the address of the high byte; @regh+1 is the low byte. ++ */ ++static int rk818_read_vol(struct rk818_battery *di, u8 regh) ++{ ++ int raw; ++ ++ raw = rk818_read(di, regh) << 8; ++ raw |= rk818_read(di, regh + 1); ++ return di->voltage_k * raw / 1000 + di->voltage_b; ++} ++ ++/* ++ * Read a big-endian signed 12-bit current ADC word and convert to mA. ++ * ++ * The LSB size depends on the sense resistor: ++ * 20 mOhm -> 1506 uA/LSB (res_div == 1) ++ * 10 mOhm -> 3012 uA/LSB (res_div == 2) ++ */ ++static int rk818_read_cur(struct rk818_battery *di, u8 regh) ++{ ++ int raw; ++ ++ raw = rk818_read(di, regh) << 8; ++ raw |= rk818_read(di, regh + 1); ++ ++ if (raw & 0x800) ++ raw -= 4096; ++ ++ return raw * (int)di->res_div * 1506 / 1000; ++} ++ ++/* ------------------------------------------------------------------------- ++ * OCV table interpolation ++ * ------------------------------------------------------------------------- ++ * OCV lookup is delegated to power_supply_batinfo_ocv2cap(), which ++ * operates on the standard simple-battery ocv-capacity-table parsed by ++ * the power-supply core into psy->battery_info. ++ */ ++ ++static int vol_to_soc(struct rk818_battery *di, int voltage_mv) ++{ ++ int cap; ++ ++ /* ++ * power_supply_batinfo_ocv2cap() takes microvolts and a ++ * temperature in whole degrees Celsius (matching the values in ++ * the battery node's ocv-capacity-celsius property), and ++ * returns the capacity percent (or a negative errno). ++ */ ++ cap = power_supply_batinfo_ocv2cap(di->info, voltage_mv * 1000, 20); ++ return cap < 0 ? 0 : cap; ++} ++ ++static int vol_to_cap(struct rk818_battery *di, int voltage_mv) ++{ ++ return vol_to_soc(di, voltage_mv) * di->fcc_mah / 100; ++} ++ ++/* ------------------------------------------------------------------------- ++ * Coulomb counter ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_get_coulomb_cap(struct rk818_battery *di) ++{ ++ int raw = 0; ++ ++ raw |= rk818_read(di, RK818_GASCNT3_REG) << 24; ++ raw |= rk818_read(di, RK818_GASCNT2_REG) << 16; ++ raw |= rk818_read(di, RK818_GASCNT1_REG) << 8; ++ raw |= rk818_read(di, RK818_GASCNT0_REG); ++ ++ return (raw / CC_LSB_DENOM) * di->res_div; ++} ++ ++static void rk818_set_coulomb_cap(struct rk818_battery *di, int cap_mah) ++{ ++ u32 raw = (u32)cap_mah * CC_LSB_DENOM / DIV(di->res_div); ++ ++ rk818_write(di, RK818_GASCNT_CAL_REG3, (raw >> 24) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG2, (raw >> 16) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG1, (raw >> 8) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG0, raw & 0xff); ++ ++ di->remain_cap_mah = cap_mah; ++ dev_dbg(di->dev, "coulomb cap set to %d mAh\n", cap_mah); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Charger / battery presence ++ * ------------------------------------------------------------------------- */ ++ ++static bool rk818_charger_online(struct rk818_battery *di) ++{ ++ return !!(rk818_read(di, RK818_VB_MON_REG) & PLUG_IN_STS); ++} ++ ++static int rk818_get_chrg_status(struct rk818_battery *di) ++{ ++ return rk818_read(di, RK818_SUP_STS_REG) & RK818_CHG_STS_MASK; ++} ++ ++static bool rk818_battery_present(struct rk818_battery *di) ++{ ++ return !!(rk818_read(di, RK818_SUP_STS_REG) & BAT_EXS); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Relaxed voltage (open-circuit approximation during low-current periods) ++ * ------------------------------------------------------------------------- */ ++ ++/* ++ * Returns the higher of the two hardware-captured relaxed voltage samples, ++ * or 0 if neither sample is valid yet. ++ */ ++static int rk818_get_relax_voltage(struct rk818_battery *di) ++{ ++ int rv1, rv2; ++ u8 sts; ++ ++ sts = rk818_read(di, RK818_GGSTS_REG); ++ if (!(sts & RELAX_VOL1_UPD) || !(sts & RELAX_VOL2_UPD)) ++ return 0; ++ ++ rv1 = rk818_read_vol(di, RK818_RELAX_VOL1_REGH); ++ rv2 = rk818_read_vol(di, RK818_RELAX_VOL2_REGH); ++ ++ return max(rv1, rv2); ++} ++ ++/* Clear the valid flags so the hardware will capture fresh samples. */ ++static void rk818_restart_relax(struct rk818_battery *di) ++{ ++ u8 sts = rk818_read(di, RK818_GGSTS_REG); ++ ++ rk818_write(di, RK818_GGSTS_REG, sts & ~RELAX_VOL12_UPD_MSK); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Voltage ADC calibration (k / b coefficients) ++ * ------------------------------------------------------------------------- */ ++ ++/* ++ * The PMIC stores two factory calibration points in VCALIB0 / VCALIB1: ++ * vcalib0 = ADC code at 3000 mV ++ * vcalib1 = ADC code at 4200 mV ++ * ++ * From these we derive: ++ * k = (4200 - 3000) * 1000 / (vcalib1 - vcalib0) ++ * b = 4200 - k * vcalib1 / 1000 ++ * voltage_mV = raw * k / 1000 + b ++ */ ++static void rk818_init_voltage_kb(struct rk818_battery *di) ++{ ++ int v0, v1; ++ ++ v0 = rk818_read(di, RK818_VCALIB0_REGL); ++ v0 |= rk818_read(di, RK818_VCALIB0_REGH) << 8; ++ v1 = rk818_read(di, RK818_VCALIB1_REGL); ++ v1 |= rk818_read(di, RK818_VCALIB1_REGH) << 8; ++ ++ di->voltage_k = (4200 - 3000) * 1000 / DIV(v1 - v0); ++ di->voltage_b = 4200 - di->voltage_k * v1 / 1000; ++ ++ dev_dbg(di->dev, "voltage cal: k=%d b=%d (calib0=%d calib1=%d)\n", ++ di->voltage_k, di->voltage_b, v0, v1); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Current offset calibration ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_get_coffset(struct rk818_battery *di) ++{ ++ int val; ++ ++ val = rk818_read(di, RK818_CAL_OFFSET_REGL); ++ val |= rk818_read(di, RK818_CAL_OFFSET_REGH) << 8; ++ return val; ++} ++ ++static void rk818_set_coffset(struct rk818_battery *di, int val) ++{ ++ if (val < COFFSET_MIN || val > COFFSET_MAX) { ++ dev_warn(di->dev, ++ "coffset 0x%x out of range [0x%x, 0x%x], ignoring\n", ++ val, COFFSET_MIN, COFFSET_MAX); ++ return; ++ } ++ rk818_write(di, RK818_CAL_OFFSET_REGH, (val >> 8) & 0xff); ++ rk818_write(di, RK818_CAL_OFFSET_REGL, val & 0xff); ++} ++ ++static int rk818_get_ioffset(struct rk818_battery *di) ++{ ++ int val; ++ ++ val = rk818_read(di, RK818_IOFFSET_REGL); ++ val |= rk818_read(di, RK818_IOFFSET_REGH) << 8; ++ return val; ++} ++ ++/* ++ * Read the raw signed 12-bit ADC code used for current sensing. ++ * This is the value before the COFFSET subtraction that the hardware ++ * applies internally, so it reflects the residual offset we want to zero. ++ */ ++static int rk818_get_iadc(struct rk818_battery *di) ++{ ++ int val; ++ ++ val = rk818_read(di, RK818_BAT_CUR_AVG_REGL); ++ val |= rk818_read(di, RK818_BAT_CUR_AVG_REGH) << 8; ++ if (val > 2047) ++ val -= 4096; ++ return val; ++} ++ ++/* ++ * rk818_init_coffset - restore the calibrated current offset on boot. ++ * ++ * COFFSET = IOFFSET (written by the PMIC at power-on) + POFFSET (saved by ++ * the driver after a successful auto-calibration). This combination ++ * cancels both the hardware reference offset and any board-level parasitic. ++ */ ++static void rk818_init_coffset(struct rk818_battery *di) ++{ ++ int ioffset, coffset; ++ ++ ioffset = rk818_get_ioffset(di); ++ di->poffset = rk818_read(di, RK818_POFFSET_REG); ++ if (!di->poffset) ++ di->poffset = POFFSET_DEFAULT; ++ ++ coffset = di->poffset + ioffset; ++ if (coffset < COFFSET_MIN || coffset > COFFSET_MAX) ++ coffset = COFFSET_DEFAULT; ++ ++ rk818_set_coffset(di, coffset); ++ dev_dbg(di->dev, ++ "coffset init: poffset=0x%x ioffset=0x%x -> coffset=0x%x\n", ++ di->poffset, ioffset, coffset); ++} ++ ++/* ++ * rk818_auto_calib_coffset - iteratively nudge COFFSET until BAT_CUR_AVG = 0. ++ * ++ * Only attempted when the charger reports RK818_CHG_STS_TERMINATED and the charger is ++ * present, so battery current should genuinely be close to zero. After ++ * convergence, POFFSET is saved so the correction survives a reboot. ++ * ++ * Returns true if calibration converged. ++ */ ++static bool rk818_auto_calib_coffset(struct rk818_battery *di) ++{ ++ int i, iadc, coffset, saved_coffset, ioffset; ++ ++ if (di->coffset_calib_done || ++ di->coffset_calib_runs >= ADC_CALIB_MAX_RUNS || ++ di->chrg_status != RK818_CHG_STS_TERMINATED || ++ rk818_sec_since(di->boot_base_jiffies) < ADC_CALIB_MIN_BOOT_SEC || ++ abs(di->current_avg_ma) < ADC_ZERO_THRESHOLD) ++ return false; ++ ++ di->coffset_calib_runs++; ++ saved_coffset = rk818_get_coffset(di); ++ ++ for (i = 0; i < ADC_CALIB_ITERATIONS; i++) { ++ if (!rk818_charger_online(di)) { ++ rk818_set_coffset(di, saved_coffset); ++ dev_info(di->dev, ++ "coffset calib aborted: charger removed\n"); ++ return false; ++ } ++ ++ iadc = rk818_get_iadc(di); ++ coffset = rk818_get_coffset(di); ++ rk818_set_coffset(di, coffset + iadc); ++ msleep(2000); ++ ++ iadc = rk818_get_iadc(di); ++ if (abs(iadc) < ADC_ZERO_THRESHOLD) { ++ ioffset = rk818_get_ioffset(di); ++ coffset = rk818_get_coffset(di); ++ di->poffset = coffset - ioffset; ++ rk818_write(di, RK818_POFFSET_REG, di->poffset); ++ di->coffset_calib_done = true; ++ dev_info(di->dev, ++ "coffset calib done: coffset=0x%x ioffset=0x%x poffset=0x%x\n", ++ coffset, ioffset, di->poffset); ++ return true; ++ } ++ } ++ ++ rk818_set_coffset(di, saved_coffset); ++ dev_info(di->dev, ++ "coffset calib incomplete after %d iters (run %u/%u)\n", ++ ADC_CALIB_ITERATIONS, di->coffset_calib_runs, ++ ADC_CALIB_MAX_RUNS); ++ return false; ++} ++ ++/* ------------------------------------------------------------------------- ++ * FCC (fully-charged capacity) persistence ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_read_fcc(struct rk818_battery *di) ++{ ++ u32 fcc = 0; ++ ++ fcc |= (u32)rk818_read(di, RK818_NEW_FCC_REG3) << 24; ++ fcc |= (u32)rk818_read(di, RK818_NEW_FCC_REG2) << 16; ++ fcc |= (u32)rk818_read(di, RK818_NEW_FCC_REG1) << 8; ++ fcc |= (u32)rk818_read(di, RK818_NEW_FCC_REG0); ++ ++ u32 design_mah = di->info->charge_full_design_uah / 1000; ++ ++ if (fcc < MIN_FCC || fcc > design_mah) { ++ dev_info(di->dev, ++ "stored FCC %u mAh out of range, using design capacity\n", ++ fcc); ++ fcc = design_mah; ++ } ++ ++ return (int)clamp(fcc, (u32)MIN_FCC, design_mah); ++} ++ ++static void rk818_save_fcc(struct rk818_battery *di, u32 fcc) ++{ ++ rk818_write(di, RK818_NEW_FCC_REG3, (fcc >> 24) & 0xff); ++ rk818_write(di, RK818_NEW_FCC_REG2, (fcc >> 16) & 0xff); ++ rk818_write(di, RK818_NEW_FCC_REG1, (fcc >> 8) & 0xff); ++ rk818_write(di, RK818_NEW_FCC_REG0, fcc & 0xff); ++ dev_info(di->dev, "FCC saved: %u mAh\n", fcc); ++} ++ ++/* ------------------------------------------------------------------------- ++ * State persistence ++ * ------------------------------------------------------------------------- */ ++ ++static void rk818_save_dsoc(struct rk818_battery *di) ++{ ++ rk818_write(di, RK818_SOC_REG, (u8)clamp(di->dsoc, 0, 100)); ++} ++ ++static void rk818_save_remain_cap(struct rk818_battery *di) ++{ ++ int cap = clamp(di->remain_cap_mah, 0, ++ (int)(di->info->charge_full_design_uah / 1000)); ++ ++ rk818_write(di, RK818_REMAIN_CAP_REG3, (cap >> 24) & 0xff); ++ rk818_write(di, RK818_REMAIN_CAP_REG2, (cap >> 16) & 0xff); ++ rk818_write(di, RK818_REMAIN_CAP_REG1, (cap >> 8) & 0xff); ++ rk818_write(di, RK818_REMAIN_CAP_REG0, cap & 0xff); ++} ++ ++static int rk818_read_saved_cap(struct rk818_battery *di) ++{ ++ int cap = 0; ++ ++ cap |= rk818_read(di, RK818_REMAIN_CAP_REG3) << 24; ++ cap |= rk818_read(di, RK818_REMAIN_CAP_REG2) << 16; ++ cap |= rk818_read(di, RK818_REMAIN_CAP_REG1) << 8; ++ cap |= rk818_read(di, RK818_REMAIN_CAP_REG0); ++ ++ return cap; ++} ++ ++/* ------------------------------------------------------------------------- ++ * Hardware configuration ++ * ------------------------------------------------------------------------- */ ++ ++static void rk818_enable_gauge(struct rk818_battery *di) ++{ ++ u8 val = rk818_read(di, RK818_TS_CTRL_REG); ++ ++ rk818_write(di, RK818_TS_CTRL_REG, val | GG_EN); ++} ++ ++static void rk818_configure_sampling(struct rk818_battery *di) ++{ ++ u8 ggcon = rk818_read(di, RK818_GGCON_REG); ++ ++ ggcon = (ggcon & ~OCV_SAMP_MIN_MSK) | OCV_SAMP_8MIN; ++ ggcon = (ggcon & ~ADC_CAL_MIN_MSK) | ADC_CAL_8MIN; ++ ggcon |= ADC_CUR_MODE; ++ rk818_write(di, RK818_GGCON_REG, ggcon); ++} ++ ++static void rk818_configure_relax_thresholds(struct rk818_battery *di) ++{ ++ int enter_raw, exit_raw; ++ ++ enter_raw = (int)di->relax_entry_current_ma * 1000 / 1506 / ++ DIV(di->res_div); ++ exit_raw = (int)di->relax_exit_current_ma * 1000 / 1506 / ++ DIV(di->res_div); ++ ++ rk818_write(di, RK818_RELAX_ENTRY_THRES_REGL, enter_raw & 0xff); ++ rk818_write(di, RK818_RELAX_ENTRY_THRES_REGH, (enter_raw >> 8) & 0xff); ++ rk818_write(di, RK818_RELAX_EXIT_THRES_REGL, exit_raw & 0xff); ++ rk818_write(di, RK818_RELAX_EXIT_THRES_REGH, (exit_raw >> 8) & 0xff); ++ ++ rk818_restart_relax(di); ++} ++ ++/* ++ * Set the VBAT low-voltage monitor to 3.4 V in IRQ (not hard-shutdown) mode ++ * so the system receives a wakeup event when the battery is nearly empty. ++ * Unmask the corresponding interrupt so it is actually delivered. ++ */ ++static void rk818_set_low_voltage_irq(struct rk818_battery *di) ++{ ++ u8 val = rk818_read(di, RK818_VB_MON_REG); ++ ++ val = (val & ~(VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK)) | ++ RK818_VBAT_LOW_3V4 | EN_VBAT_LOW_IRQ; ++ rk818_write(di, RK818_VB_MON_REG, val); ++ /* Clear the mask bit to enable the interrupt */ ++ rk818_clrsetbits(di, RK818_INT_STS_MSK_REG1, VB_LOW_INT_EN, 0); ++} ++ ++/* ++ * Encode a Celsius temperature into the HOTDIE threshold register bits, ++ * rounding down to the nearest supported table entry (85/95/105/115 C). ++ */ ++static u8 rk818_encode_hotdie_temp(u32 celsius) ++{ ++ if (celsius >= 115) ++ return HOTDIE_TEMP_115C; ++ if (celsius >= 105) ++ return HOTDIE_TEMP_105C; ++ if (celsius >= 95) ++ return HOTDIE_TEMP_95C; ++ ++ return HOTDIE_TEMP_85C; ++} ++ ++/* ++ * Encode a termination-current value (microamps) into FINISH_CUR_MSK bits. ++ * Rounds down to the nearest supported step (100/150/200/250 mA). ++ */ ++static u8 rk818_encode_finish_current(int term_ua) ++{ ++ int term_ma = term_ua / 1000; ++ ++ if (term_ma >= 250) ++ return FINISH_250MA; ++ if (term_ma >= 200) ++ return FINISH_200MA; ++ if (term_ma >= 150) ++ return FINISH_150MA; ++ return FINISH_100MA; ++} ++ ++/* ++ * Encode a CC/CV safety-timer duration (hours) into CHRG_TIMER_CCCV bits. ++ * Rounds down to the nearest supported step (4/5/6/8/10/12/14/16 h). ++ */ ++static u8 rk818_encode_cccv_timeout(u32 hours) ++{ ++ if (hours >= 16) ++ return CHRG_TIMER_CCCV_16H; ++ if (hours >= 14) ++ return CHRG_TIMER_CCCV_14H; ++ if (hours >= 12) ++ return CHRG_TIMER_CCCV_12H; ++ if (hours >= 10) ++ return CHRG_TIMER_CCCV_10H; ++ if (hours >= 8) ++ return CHRG_TIMER_CCCV_8H; ++ if (hours >= 6) ++ return CHRG_TIMER_CCCV_6H; ++ if (hours >= 5) ++ return CHRG_TIMER_CCCV_5H; ++ return CHRG_TIMER_CCCV_4H; ++} ++ ++/* ++ * Configure charge termination current and thermal feedback register. ++ * Termination current, constant-charge voltage, and constant-charge ++ * current are taken from the simple-battery info where available and ++ * fall back to safe defaults otherwise. ++ */ ++static void rk818_configure_charger(struct rk818_battery *di) ++{ ++ u8 finish_ma, usb_ctrl, chrg2, chrg3, thermal; ++ int term_ua; ++ ++ /* ++ * Apply max CC current and max CV voltage from the simple-battery ++ * info. The register set helpers clamp to supported hardware steps. ++ */ ++ if (di->info->constant_charge_current_max_ua > 0) ++ rk818_charger_set_current_max(di, ++ di->info->constant_charge_current_max_ua); ++ if (di->info->constant_charge_voltage_max_uv > 0) ++ rk818_charger_set_voltage_max(di, ++ di->info->constant_charge_voltage_max_uv); ++ ++ /* ++ * Termination current: prefer charge_term_current_ua from the DT; ++ * fall back to ~C/30 of FCC scaled by sense resistor, then encode ++ * to the nearest supported FINISH_*MA step. ++ */ ++ term_ua = di->info->charge_term_current_ua; ++ if (term_ua <= 0) { ++ if (di->sense_resist_mohm == 10) ++ term_ua = 100000; ++ else if (di->fcc_mah >= 5000) ++ term_ua = 250000; ++ else if (di->fcc_mah >= 4000) ++ term_ua = 200000; ++ else if (di->fcc_mah >= 3000) ++ term_ua = 150000; ++ else ++ term_ua = 100000; ++ } ++ finish_ma = rk818_encode_finish_current(term_ua); ++ ++ usb_ctrl = rk818_read(di, RK818_USB_CTRL_REG); ++ chrg2 = rk818_read(di, RK818_CHRG_CTRL_REG2); ++ chrg3 = rk818_read(di, RK818_CHRG_CTRL_REG3); ++ thermal = rk818_read(di, RK818_THERMAL_REG); ++ ++ /* Use digital charge-termination signal */ ++ chrg3 |= CHRG_TERM_DIG_SIGNAL; ++ ++ /* ++ * CC/CV safety timer: enable when a non-zero timeout is set in DT, ++ * otherwise leave disabled. The hardware timer guards against a ++ * stuck charger that would otherwise keep pushing CC indefinitely ++ * on a broken cell. ++ */ ++ if (di->cccv_timeout_h) { ++ chrg2 = (chrg2 & ~CHRG_TIMER_CCCV_MSK) | ++ rk818_encode_cccv_timeout(di->cccv_timeout_h); ++ chrg3 |= CHRG_TIMER_CCCV_EN; ++ } else { ++ chrg3 &= ~CHRG_TIMER_CCCV_EN; ++ } ++ ++ chrg2 = (chrg2 & ~FINISH_CUR_MSK) | finish_ma; ++ ++ if (di->feedback_temp) { ++ usb_ctrl |= CHRG_CT_EN; ++ thermal = (thermal & ~HOTDIE_TEMP_MASK) | ++ rk818_encode_hotdie_temp(di->feedback_temp); ++ } else { ++ usb_ctrl &= ~CHRG_CT_EN; ++ } ++ ++ rk818_write(di, RK818_USB_CTRL_REG, usb_ctrl); ++ rk818_write(di, RK818_CHRG_CTRL_REG2, chrg2); ++ rk818_write(di, RK818_CHRG_CTRL_REG3, chrg3); ++ rk818_write(di, RK818_THERMAL_REG, thermal); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Startup SoC initialisation ++ * ------------------------------------------------------------------------- */ ++ ++static bool rk818_is_first_power_on(struct rk818_battery *di) ++{ ++ u8 val = rk818_read(di, RK818_GGSTS_REG); ++ ++ if (val & BAT_CON) { ++ rk818_write(di, RK818_GGSTS_REG, val & ~BAT_CON); ++ return true; ++ } ++ return false; ++} ++ ++static bool rk818_was_warm_initialized(struct rk818_battery *di) ++{ ++ u8 val = rk818_read(di, RK818_MISC_MARK_REG); ++ ++ if (val & FG_INIT) { ++ rk818_write(di, RK818_MISC_MARK_REG, val & ~FG_INIT); ++ return true; ++ } ++ return false; ++} ++ ++/* ++ * rk818_init_soc - choose the correct starting dsoc / remain_cap on boot. ++ * ++ * Decision priority: ++ * 1. First ever power-on: use OCV-derived SoC (battery was just connected). ++ * 2. Warm reboot (FG_INIT flag set): the coulomb counter is still live; ++ * trust it and the saved dsoc directly. ++ * 3. Cold boot after >= 30 min off: OCV has settled; re-anchor from OCV. ++ * 4. Short cold boot: start from saved dsoc, but jump to OCV if the ++ * discrepancy exceeds max_soc_offset_pct. ++ */ ++static void rk818_init_soc(struct rk818_battery *di) ++{ ++ int ocv_vol, ocv_soc, ocv_cap; ++ int saved_dsoc, saved_cap; ++ u8 cur, last; ++ ++ di->fcc_mah = rk818_read_fcc(di); ++ di->is_first_power_on = rk818_is_first_power_on(di); ++ di->is_initialized = rk818_was_warm_initialized(di); ++ ++ cur = rk818_read(di, RK818_NON_ACT_TIMER_CNT_REG); ++ last = rk818_read(di, RK818_NON_ACT_TIMER_CNT_SAVE_REG); ++ rk818_write(di, RK818_NON_ACT_TIMER_CNT_SAVE_REG, cur); ++ di->pwroff_min = (cur != last) ? cur : 0; ++ ++ ocv_vol = rk818_read_vol(di, RK818_BAT_OCV_REGH); ++ ocv_soc = vol_to_soc(di, ocv_vol); ++ ocv_cap = vol_to_cap(di, ocv_vol); ++ ++ if (di->is_first_power_on) { ++ di->dsoc = ocv_soc; ++ di->remain_cap_mah = ocv_cap; ++ rk818_save_fcc(di, (u32)di->fcc_mah); ++ dev_info(di->dev, ++ "first power-on: dsoc=%d cap=%d mAh ocv=%d mV\n", ++ di->dsoc, di->remain_cap_mah, ocv_vol); ++ goto done; ++ } ++ ++ saved_dsoc = rk818_read(di, RK818_SOC_REG); ++ saved_cap = rk818_read_saved_cap(di); ++ ++ if (di->is_initialized) { ++ di->dsoc = saved_dsoc; ++ di->remain_cap_mah = rk818_get_coulomb_cap(di); ++ dev_info(di->dev, "warm boot: dsoc=%d cap=%d mAh\n", ++ di->dsoc, di->remain_cap_mah); ++ goto done; ++ } ++ ++ if (di->pwroff_min >= 30) { ++ di->dsoc = ocv_soc; ++ di->remain_cap_mah = ocv_cap; ++ dev_info(di->dev, ++ "OCV re-anchor after %u min off: dsoc %d -> %d, cap=%d mAh\n", ++ di->pwroff_min, saved_dsoc, ++ di->dsoc, di->remain_cap_mah); ++ goto done; ++ } ++ ++ di->dsoc = saved_dsoc; ++ di->remain_cap_mah = max(0, saved_cap); ++ ++ if (abs(ocv_soc - saved_dsoc) >= (int)di->max_soc_offset_pct) { ++ dev_info(di->dev, ++ "OCV offset too large (saved=%d ocv=%d), jumping\n", ++ saved_dsoc, ocv_soc); ++ di->dsoc = ocv_soc; ++ di->remain_cap_mah = ocv_cap; ++ } ++ ++done: ++ di->dsoc = clamp(di->dsoc, 0, 100); ++ di->remain_cap_mah = max(0, di->remain_cap_mah); ++ ++ rk818_set_coulomb_cap(di, di->remain_cap_mah); ++ di->rsoc = di->dsoc; ++ ++ dev_info(di->dev, ++ "init done: dsoc=%d rsoc=%d cap=%d mAh fcc=%d mAh\n", ++ di->dsoc, di->rsoc, di->remain_cap_mah, di->fcc_mah); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Periodic calibration ++ * ++ * Refreshes the voltage k/b coefficients and re-applies the current offset ++ * every CALIB_INTERVAL_MIN minutes so long-running drift is corrected ++ * without a reboot. Driven from the main monitor work via ++ * ktime_get_boottime() so suspend time is accounted for automatically. ++ * ------------------------------------------------------------------------- */ ++ ++#define CALIB_INTERVAL_MIN 8 ++ ++static void rk818_periodic_calib(struct rk818_battery *di) ++{ ++ int poffset, ioffset; ++ ++ poffset = rk818_read(di, RK818_POFFSET_REG); ++ di->poffset = poffset ? poffset : POFFSET_DEFAULT; ++ ioffset = rk818_get_ioffset(di); ++ rk818_set_coffset(di, ioffset + di->poffset); ++ rk818_init_voltage_kb(di); ++ ++ dev_dbg(di->dev, "periodic calib: coffset=0x%x poffset=%d\n", ++ rk818_get_coffset(di), di->poffset); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Main monitor work ++ * ------------------------------------------------------------------------- */ ++ ++/* ++ * rk818_update_soc - recompute rsoc from the coulomb counter and step dsoc ++ * toward it by at most DSOC_DELTA_MAX per call. ++ * ++ * Special cases: ++ * - RK818_CHG_STS_TERMINATED: snap remain_cap to fcc so rsoc hits 100 cleanly. ++ * - Valid relaxed voltage: re-anchor the coulomb counter to the OCV cap. ++ * - Voltage below shutdown threshold: force dsoc to 0. ++ */ ++static void rk818_update_soc(struct rk818_battery *di) ++{ ++ di->remain_cap_mah = rk818_get_coulomb_cap(di); ++ ++ if (di->chrg_status == RK818_CHG_STS_TERMINATED && ++ di->remain_cap_mah != di->fcc_mah) { ++ dev_info(di->dev, "charge finish: cap %d -> %d mAh\n", ++ di->remain_cap_mah, di->fcc_mah); ++ rk818_set_coulomb_cap(di, di->fcc_mah); ++ } ++ ++ di->voltage_relax_mv = rk818_get_relax_voltage(di); ++ if (di->voltage_relax_mv > 0) { ++ int ocv_cap = vol_to_cap(di, di->voltage_relax_mv); ++ int ocv_soc = vol_to_soc(di, di->voltage_relax_mv); ++ ++ dev_dbg(di->dev, ++ "relax OCV re-anchor: vol=%d mV soc=%d%% cap=%d mAh (was %d mAh)\n", ++ di->voltage_relax_mv, ocv_soc, ocv_cap, ++ di->remain_cap_mah); ++ rk818_set_coulomb_cap(di, ocv_cap); ++ rk818_restart_relax(di); ++ } ++ ++ di->remain_cap_mah = rk818_get_coulomb_cap(di); ++ di->rsoc = clamp(di->remain_cap_mah * 100 / DIV(di->fcc_mah), 0, 100); ++ ++ if (di->voltage_avg_mv < di->info->voltage_min_design_uv / 1000) { ++ dev_dbg(di->dev, ++ "voltage %d mV below shutdown threshold %u mV\n", ++ di->voltage_avg_mv, di->info->voltage_min_design_uv / 1000); ++ di->dsoc = 0; ++ return; ++ } ++ ++ di->dsoc = clamp(di->dsoc + ++ clamp(di->rsoc - di->dsoc, ++ -DSOC_DELTA_MAX, DSOC_DELTA_MAX), ++ 0, 100); ++} ++ ++/* ------------------------------------------------------------------------- ++ * NTC battery temperature sensing (TS1) ++ * ------------------------------------------------------------------------- */ ++ ++static void rk818_init_ts1_detect(struct rk818_battery *di) ++{ ++ u32 rmax; ++ u8 buf; ++ ++ if (!di->ntc_size) ++ return; ++ ++ /* ++ * Pick a sample current based on the largest resistance we might ++ * need to measure. Entries are sorted by temperature ascending, ++ * so the coldest entry -- entry 0 -- has the highest resistance. ++ */ ++ rmax = di->ntc_entries[0].res; ++ ++ buf = rk818_read(di, RK818_TS_CTRL_REG); ++ buf &= ~TS1_CUR_MSK; ++ if (rmax < NTC_80UA_MAX_MEASURE) { ++ di->ntc_factor = NTC_CALC_FACTOR_80UA; ++ di->ntc_uA = 80; ++ buf |= ADC_CUR_80UA; ++ } else if (rmax < NTC_60UA_MAX_MEASURE) { ++ di->ntc_factor = NTC_CALC_FACTOR_60UA; ++ di->ntc_uA = 60; ++ buf |= ADC_CUR_60UA; ++ } else if (rmax < NTC_40UA_MAX_MEASURE) { ++ di->ntc_factor = NTC_CALC_FACTOR_40UA; ++ di->ntc_uA = 40; ++ buf |= ADC_CUR_40UA; ++ } else { ++ di->ntc_factor = NTC_CALC_FACTOR_20UA; ++ di->ntc_uA = 20; ++ buf |= ADC_CUR_20UA; ++ } ++ rk818_write(di, RK818_TS_CTRL_REG, buf); ++ ++ /* Enable ADC TS1 channel */ ++ buf = rk818_read(di, RK818_ADC_CTRL_REG); ++ buf |= ADC_TS1_EN; ++ rk818_write(di, RK818_ADC_CTRL_REG, buf); ++} ++ ++/* ++ * Due to a hardware design issue, Vdelta = (R_sample + R_other) * I_avg is ++ * folded into the TS1 ADC value. Subtract it to recover the real reading ++ * by sampling at two different excitation currents and solving for Vdelta: ++ * ++ * adc1 - Vdelta ua1 (adc2 * ua1) - (adc1 * ua2) ++ * ------------- = ----- => Vdelta = ----------------------------- ++ * adc2 - Vdelta ua2 ua1 - ua2 ++ * ++ * Then, depending on current direction: ++ * charging: ADC = adc1 - |Vdelta| ++ * discharging: ADC = adc1 + |Vdelta| ++ */ ++static int rk818_get_ntc_res(struct rk818_battery *di) ++{ ++ int adc1 = 0, adc2 = 0; ++ int ua1, ua2, v_delta, val; ++ u8 buf; ++ ++ /* Read adc1 at the configured sample current */ ++ ua1 = di->ntc_uA; ++ adc1 |= rk818_read(di, RK818_TS1_ADC_REGL) << 0; ++ adc1 |= rk818_read(di, RK818_TS1_ADC_REGH) << 8; ++ ++ /* Switch to a different reference current for adc2 */ ++ ua2 = (ua1 != 20) ? 20 : 40; ++ buf = rk818_read(di, RK818_TS_CTRL_REG); ++ buf &= ~TS1_CUR_MSK; ++ buf |= ((ua2 - 20) / 20); ++ rk818_write(di, RK818_TS_CTRL_REG, buf); ++ ++ /* Allow the new sample current to settle, then read adc2 */ ++ msleep(1000); ++ adc2 |= rk818_read(di, RK818_TS1_ADC_REGL) << 0; ++ adc2 |= rk818_read(di, RK818_TS1_ADC_REGH) << 8; ++ ++ if (adc2 != adc1) ++ v_delta = abs((adc2 * ua1 - adc1 * ua2) / (ua2 - ua1)); ++ else ++ v_delta = 0; ++ ++ val = (di->current_avg_ma >= 0) ? (adc1 - v_delta) : (adc1 + v_delta); ++ ++ /* Restore the original sample current */ ++ rk818_init_ts1_detect(di); ++ ++ return val * di->ntc_factor; ++} ++ ++/* ++ * Binary-search bracket lookup over the NTC table. ++ * ++ * Entries are sorted by temperature ascending, so resistance is strictly ++ * descending. Given a measured resistance "ohm", return indices "low" and ++ * "high" such that: ++ * ++ * entries[low].res >= ohm >= entries[high].res (with low >= high) ++ * ++ * When "ohm" is out of range or an exact match, low == high. The caller ++ * is expected to then interpolate between entries[low] and entries[high]. ++ * ++ * Lifted in spirit from drivers/hwmon/ntc_thermistor.c's lookup_comp(). ++ */ ++static void rk818_ntc_lookup(const struct rk818_battery *di, u32 ohm, ++ unsigned int *low, unsigned int *high) ++{ ++ const struct rk818_ntc_entry *e = di->ntc_entries; ++ unsigned int n = di->ntc_size; ++ unsigned int start, end, mid; ++ ++ /* Clamp: colder than coldest -> first entry */ ++ if (ohm >= e[0].res) { ++ *low = 0; ++ *high = 0; ++ return; ++ } ++ /* Clamp: hotter than hottest -> last entry */ ++ if (ohm <= e[n - 1].res) { ++ *low = n - 1; ++ *high = n - 1; ++ return; ++ } ++ ++ /* ++ * Invariant: e[start].res > ohm >= e[end].res ++ * At entry, start=0 and end=n-1 satisfy it thanks to the clamps. ++ */ ++ start = 0; ++ end = n - 1; ++ while (start < end) { ++ mid = start + (end - start) / 2; ++ ++ if (ohm >= e[mid].res) ++ end = mid; ++ else ++ start = mid + 1; ++ } ++ ++ /* ++ * start == end, and e[end].res <= ohm < e[end - 1].res. ++ * So "low" (higher resistance) is end - 1 and "high" (lower ++ * resistance) is end -- unless we hit an exact match. ++ */ ++ *high = end; ++ if (ohm == e[end].res) ++ *low = end; ++ else ++ *low = end - 1; ++} ++ ++static void rk818_update_temperature(struct rk818_battery *di) ++{ ++ static int old_temp; ++ static bool first_time = true; ++ unsigned int low, high; ++ int res, temp_mc, temp; ++ ++ if (!di->ntc_size) ++ return; ++ ++ res = rk818_get_ntc_res(di); ++ if (res < 0) ++ res = 0; ++ ++ rk818_ntc_lookup(di, (u32)res, &low, &high); ++ ++ /* ++ * Interpolate in milli-degrees for precision, then convert to ++ * tenths (the unit power_supply expects for POWER_SUPPLY_PROP_TEMP). ++ * When low == high (clamp or exact match), fixp_linear_interpolate ++ * returns y0 directly. ++ */ ++ temp_mc = fixp_linear_interpolate(di->ntc_entries[low].res, ++ di->ntc_entries[low].temp * 1000, ++ di->ntc_entries[high].res, ++ di->ntc_entries[high].temp * 1000, ++ res); ++ temp = temp_mc / 100; ++ ++ if (first_time) { ++ old_temp = temp; ++ first_time = false; ++ } ++ ++ /* ++ * Reject single-sample glitches greater than 5 C from the previous ++ * reading; trust everything else. ++ */ ++ if (abs(temp - old_temp) > 50) ++ temp = old_temp; ++ else ++ old_temp = temp; ++ ++ /* ++ * Assign only once per cycle, at the end, with the final value. ++ * Writing a placeholder at the top of the function would be ++ * visible to userspace during the ~1 s window while we wait for ++ * adc2 to settle inside rk818_get_ntc_res(), causing the reported ++ * temperature to flap between the placeholder and the real value ++ * on every monitor poll. ++ */ ++ di->temperature = temp; ++ dev_dbg(di->dev, "temperature = %d\n", di->temperature); ++} ++ ++static void rk818_battery_work(struct work_struct *work) ++{ ++ struct rk818_battery *di = ++ container_of(work, struct rk818_battery, work.work); ++ int prev_dsoc = di->dsoc; ++ bool is_charging; ++ ktime_t now = ktime_get_boottime(); ++ ++ if (!di->last_calib || ++ ktime_after(now, ktime_add_ms(di->last_calib, ++ CALIB_INTERVAL_MIN * 60 * MSEC_PER_SEC))) { ++ rk818_periodic_calib(di); ++ di->last_calib = now; ++ } ++ ++ di->voltage_avg_mv = rk818_read_vol(di, RK818_BAT_VOL_REGH); ++ di->voltage_ocv_mv = rk818_read_vol(di, RK818_BAT_OCV_REGH); ++ di->current_avg_ma = rk818_read_cur(di, RK818_BAT_CUR_AVG_REGH); ++ di->chrg_status = rk818_get_chrg_status(di); ++ rk818_update_temperature(di); ++ ++ /* ++ * Derive is_charging from CHG_STS, not the current sign: during ++ * thermal foldback or at CC->CV transition, the measured current ++ * can dip near zero while the charger is still actively regulating. ++ */ ++ switch (di->chrg_status) { ++ case RK818_CHG_STS_WAKEUP_CUR: ++ case RK818_CHG_STS_TRICKLE_CUR: ++ case RK818_CHG_STS_CC_OR_CV: ++ is_charging = true; ++ break; ++ default: ++ is_charging = false; ++ break; ++ } ++ if (is_charging != di->is_charging) { ++ di->is_charging = is_charging; ++ if (is_charging) { ++ /* ++ * Reset flag so auto-calibration will run again the ++ * next time RK818_CHG_STS_TERMINATED is reached. ++ */ ++ di->coffset_calib_done = false; ++ } ++ } ++ ++ /* Clamp coulomb counter to [0, fcc] */ ++ if (di->remain_cap_mah > di->fcc_mah) { ++ rk818_set_coulomb_cap(di, di->fcc_mah); ++ } else if (di->remain_cap_mah < 0) { ++ /* ++ * Estimate from terminal voltage when the counter underflows. ++ * Subtract the resistive drop to get a rough OCV. ++ */ ++ int est_vol = di->voltage_avg_mv - ++ (di->info->factory_internal_resistance_uohm / 1000) * ++ di->current_avg_ma / 1000; ++ int est_cap = max(0, vol_to_cap(di, est_vol)); ++ ++ dev_info(di->dev, ++ "counter underflow, voltage estimate: %d mAh\n", ++ est_cap); ++ rk818_set_coulomb_cap(di, est_cap); ++ } ++ ++ if (di->chrg_status == RK818_CHG_STS_TERMINATED && rk818_charger_online(di)) ++ rk818_auto_calib_coffset(di); ++ ++ rk818_update_soc(di); ++ ++ rk818_save_dsoc(di); ++ rk818_save_remain_cap(di); ++ ++ if (di->dsoc != prev_dsoc) { ++ if (di->charger_psy) ++ power_supply_changed(di->charger_psy); ++ dev_info(di->dev, ++ "dsoc %d -> %d (rsoc=%d v=%d mV i=%d mA cap=%d mAh fcc=%d mAh)\n", ++ prev_dsoc, di->dsoc, di->rsoc, ++ di->voltage_avg_mv, di->current_avg_ma, ++ di->remain_cap_mah, di->fcc_mah); ++ } ++ ++ dev_dbg(di->dev, ++ "v=%d ocv=%d rv=%d i=%d cap=%d rsoc=%d dsoc=%d chrg=0x%x\n", ++ di->voltage_avg_mv, di->voltage_ocv_mv, di->voltage_relax_mv, ++ di->current_avg_ma, di->remain_cap_mah, ++ di->rsoc, di->dsoc, di->chrg_status); ++ ++ queue_delayed_work(di->wq, &di->work, ++ msecs_to_jiffies(di->monitor_ms)); ++} ++ ++/* ------------------------------------------------------------------------- ++ * Battery fuel-gauge properties (used by the charger psy get_property) ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_bat_get_property(struct rk818_battery *di, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ switch (di->chrg_status) { ++ case RK818_CHG_STS_WAKEUP_CUR: ++ case RK818_CHG_STS_TRICKLE_CUR: ++ case RK818_CHG_STS_CC_OR_CV: ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ break; ++ case RK818_CHG_STS_TERMINATED: ++ val->intval = rk818_charger_online(di) ? ++ POWER_SUPPLY_STATUS_FULL : ++ POWER_SUPPLY_STATUS_DISCHARGING; ++ break; ++ case RK818_CHG_STS_USB_OV: ++ case RK818_CHG_STS_BAT_TEMP_FAULT: ++ case RK818_CHG_STS_TIMEOUT: ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ break; ++ default: ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ break; ++ } ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = rk818_battery_present(di); ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ val->intval = di->dsoc; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ val->intval = di->voltage_avg_mv * 1000; /* uV */ ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_OCV: ++ val->intval = di->voltage_ocv_mv * 1000; /* uV */ ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ val->intval = di->current_avg_ma * 1000; /* uA */ ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_FULL: ++ val->intval = di->fcc_mah * 1000; /* uAh */ ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ val->intval = di->temperature; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------- ++ * Suspend / resume ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_battery_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct rk818_battery *di = platform_get_drvdata(pdev); ++ ++ cancel_delayed_work_sync(&di->work); ++ ++ di->sleep_chrg_online = rk818_charger_online(di); ++ di->current_avg_ma = rk818_read_cur(di, RK818_BAT_CUR_AVG_REGH); ++ di->remain_cap_mah = rk818_get_coulomb_cap(di); ++ di->rsoc = di->remain_cap_mah * 100 / DIV(di->fcc_mah); ++ di->suspend_time = ktime_get_boottime(); ++ ++ rk818_save_dsoc(di); ++ rk818_save_remain_cap(di); ++ rk818_set_low_voltage_irq(di); ++ ++ dev_info(di->dev, ++ "suspend: dsoc=%d rsoc=%d i=%d mA v=%d mV cap=%d mAh chrg=%d\n", ++ di->dsoc, di->rsoc, di->current_avg_ma, di->voltage_avg_mv, ++ di->remain_cap_mah, di->sleep_chrg_online); ++ ++ return 0; ++} ++ ++static int rk818_battery_resume(struct platform_device *pdev) ++{ ++ struct rk818_battery *di = platform_get_drvdata(pdev); ++ int sleep_sec, sleep_mah, sleep_soc_drop; ++ ++ di->current_avg_ma = rk818_read_cur(di, RK818_BAT_CUR_AVG_REGH); ++ di->voltage_avg_mv = rk818_read_vol(di, RK818_BAT_VOL_REGH); ++ di->remain_cap_mah = rk818_get_coulomb_cap(di); ++ di->rsoc = di->remain_cap_mah * 100 / DIV(di->fcc_mah); ++ ++ sleep_sec = ktime_ms_delta(ktime_get_boottime(), di->suspend_time) / ++ MSEC_PER_SEC; ++ ++ /* ++ * The coulomb counter runs through suspend, so remain_cap_mah already ++ * accounts for actual consumption. We only apply a conservative ++ * minimum-consumption correction to dsoc so it does not appear to ++ * lag behind reality after a long sleep without a charger attached. ++ * Clamp to rsoc so we never push dsoc below what the counter reports. ++ */ ++ if (!di->sleep_chrg_online && sleep_sec > 0) { ++ sleep_mah = SLP_CURR_MA * sleep_sec / 3600; ++ sleep_soc_drop = sleep_mah * 100 / DIV(di->fcc_mah); ++ di->dsoc = max(di->dsoc - sleep_soc_drop, max(di->rsoc, 0)); ++ ++ dev_info(di->dev, ++ "resume: slept %d s, est. drop %d%%, dsoc=%d rsoc=%d\n", ++ sleep_sec, sleep_soc_drop, di->dsoc, di->rsoc); ++ } ++ ++ rk818_save_dsoc(di); ++ rk818_set_low_voltage_irq(di); ++ ++ dev_info(di->dev, ++ "resume: dsoc=%d rsoc=%d i=%d mA v=%d mV cap=%d mAh\n", ++ di->dsoc, di->rsoc, di->current_avg_ma, ++ di->voltage_avg_mv, di->remain_cap_mah); ++ ++ queue_delayed_work(di->wq, &di->work, msecs_to_jiffies(1000)); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------- ++ * IRQ ++ * ------------------------------------------------------------------------- */ ++ ++static irqreturn_t rk818_vb_low_irq(int irq, void *data) ++{ ++ struct rk818_battery *di = data; ++ ++ dev_warn(di->dev, "low battery IRQ: v=%d mV dsoc=%d%%\n", ++ di->voltage_avg_mv, di->dsoc); ++ return IRQ_HANDLED; ++} ++ ++/* ========================================================================= ++ * USB supply ++ * ========================================================================= */ ++ ++static int rk818_usb_set_input_current_max(struct rk818_battery *di, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 450000) ++ reg = 1; ++ else if (val < 850000) ++ reg = 0; ++ else if (val < 1000000) ++ reg = 2; ++ else if (val < 3000000) ++ reg = 3 + (val - 1000000) / 250000; ++ else ++ reg = 11; ++ ++ dev_info(di->dev, "applying input current limit %d mA\n", val / 1000); ++ ++ ret = regmap_update_bits(di->regmap, RK818_USB_CTRL_REG, ++ RK818_USB_CTRL_USB_ILIM_MASK, reg); ++ if (ret) ++ dev_err(di->dev, ++ "USB input current limit setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_usb_get_input_current_max(struct rk818_battery *di, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(di->regmap, RK818_USB_CTRL_REG, ®); ++ if (ret) { ++ dev_err(di->dev, ++ "USB input current limit getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_USB_CTRL_USB_ILIM_MASK; ++ if (reg == 0) ++ *val = 450000; ++ else if (reg == 1) ++ *val = 80000; ++ else if (reg == 2) ++ *val = 850000; ++ else if (reg < 11) ++ *val = 1000000 + (reg - 3) * 250000; ++ else ++ *val = 3000000; ++ ++ return 0; ++} ++ ++static int rk818_usb_set_input_voltage_min(struct rk818_battery *di, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 2780000) ++ reg = 0; ++ else if (val < 3270000) ++ reg = (val - 2780000) / 70000; ++ else ++ reg = 7; ++ ++ ret = regmap_update_bits(di->regmap, RK818_USB_CTRL_REG, ++ RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK, ++ reg << RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET); ++ if (ret) ++ dev_err(di->dev, ++ "USB input voltage limit setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_usb_get_input_voltage_min(struct rk818_battery *di, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(di->regmap, RK818_USB_CTRL_REG, ®); ++ if (ret) { ++ dev_err(di->dev, ++ "USB input voltage limit getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_USB_CTRL_USB_CHG_SD_VSEL_MASK; ++ reg >>= RK818_USB_CTRL_USB_CHG_SD_VSEL_OFFSET; ++ ++ *val = 2780000 + (reg * 70000); ++ ++ return 0; ++} ++ ++static int rk818_usb_power_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ unsigned reg; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_PRESENT: ++ ret = regmap_read(di->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ val->intval = !!(reg & RK818_SUP_STS_USB_EXS); ++ break; ++ ++ case POWER_SUPPLY_PROP_HEALTH: ++ ret = regmap_read(di->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ if (!(reg & RK818_SUP_STS_USB_EXS)) { ++ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; ++ } else if (reg & RK818_SUP_STS_USB_EFF) { ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ } else { ++ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return rk818_usb_get_input_voltage_min(di, &val->intval); ++ ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ return rk818_usb_get_input_current_max(di, &val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk818_usb_power_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return rk818_usb_set_input_voltage_min(di, val->intval); ++ ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ return rk818_usb_set_input_current_max(di, val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int rk818_usb_power_prop_writeable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: ++ return 1; ++ ++ default: ++ return 0; ++ } ++} ++ ++/* Sync the input-current-limit with our parent supply (if we have one) */ ++static void rk818_usb_power_external_power_changed(struct power_supply *psy) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ union power_supply_propval val; ++ int ret; ++ ++ ret = power_supply_get_property_from_supplier(di->usb_psy, ++ POWER_SUPPLY_PROP_CURRENT_MAX, ++ &val); ++ if (ret) ++ return; ++ ++ /* ++ * We only want to start applying input current limit after we get first ++ * non-0 value from the supplier. Until then, we keep the limit applied ++ * by the bootloader. If we lower the limit before the charger is properly ++ * detected, we risk boot failure due to insufficient power. ++ */ ++ if (!di->apply_ilim) { ++ if (!val.intval) ++ return; ++ ++ di->apply_ilim = true; ++ } ++ ++ if (val.intval < 500000) ++ val.intval = 500000; ++ ++ rk818_usb_power_set_property(di->usb_psy, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ &val); ++} ++ ++static enum power_supply_property rk818_usb_power_props[] = { ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, ++ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, ++}; ++ ++static const struct power_supply_desc rk818_usb_desc = { ++ .name = "rk818-usb", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .properties = rk818_usb_power_props, ++ .num_properties = ARRAY_SIZE(rk818_usb_power_props), ++ .property_is_writeable = rk818_usb_power_prop_writeable, ++ .get_property = rk818_usb_power_get_property, ++ .set_property = rk818_usb_power_set_property, ++ .external_power_changed = rk818_usb_power_external_power_changed, ++}; ++ ++/* ========================================================================= ++ * Charger / battery supply ++ * ========================================================================= */ ++ ++static int rk818_charger_set_current_max(struct rk818_battery *di, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 1000000) ++ reg = 0; ++ else if (val < 3000000) ++ reg = (val - 1000000) / 200000; ++ else ++ reg = 10; ++ ++ ret = regmap_update_bits(di->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK, ++ reg << RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET); ++ if (ret) ++ dev_err(di->dev, ++ "Charging max current setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_charger_get_current_max(struct rk818_battery *di, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(di->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(di->dev, ++ "Charging max current getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_MASK; ++ reg >>= RK818_CHRG_CTRL_REG1_CHRG_CUR_SEL_OFFSET; ++ ++ *val = 1000000 + reg * 200000; ++ ++ return 0; ++} ++ ++static int rk818_charger_set_voltage_max(struct rk818_battery *di, int val) ++{ ++ unsigned reg; ++ int ret; ++ ++ if (val < 4050000) ++ reg = 0; ++ else if (val < 4350000) ++ reg = (val - 4050000) / 50000; ++ else ++ reg = 6; ++ ++ ret = regmap_update_bits(di->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK, ++ reg << RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET); ++ if (ret) ++ dev_err(di->dev, ++ "Charging end voltage setting failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rk818_charger_get_voltage_max(struct rk818_battery *di, int *val) ++{ ++ unsigned reg; ++ int ret; ++ ++ ret = regmap_read(di->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(di->dev, ++ "Charging end voltage getting failed (%d)\n", ret); ++ return ret; ++ } ++ ++ reg &= RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_MASK; ++ reg >>= RK818_CHRG_CTRL_REG1_CHRG_VOL_SEL_OFFSET; ++ ++ *val = 4050000 + reg * 50000; ++ ++ return 0; ++} ++ ++static int rk818_charger_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ unsigned reg; ++ int ret; ++ ++ /* Properties delegated to the fuel-gauge */ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ case POWER_SUPPLY_PROP_VOLTAGE_OCV: ++ case POWER_SUPPLY_PROP_PRESENT: ++ case POWER_SUPPLY_PROP_CAPACITY: ++ case POWER_SUPPLY_PROP_TEMP: ++ case POWER_SUPPLY_PROP_STATUS: ++ case POWER_SUPPLY_PROP_CHARGE_FULL: ++ return rk818_bat_get_property(di, psp, val); ++ default: ++ break; ++ } ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_ONLINE: ++ ret = regmap_read(di->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(di->dev, "failed to read the charger state (%d)\n", ret); ++ return ret; ++ } ++ ++ val->intval = !!(reg & RK818_CHRG_CTRL_REG1_CHRG_EN); ++ break; ++ ++ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: ++ ret = regmap_read(di->regmap, RK818_CHRG_CTRL_REG1, ®); ++ if (ret) { ++ dev_err(di->dev, "failed to read the charger state (%d)\n", ret); ++ return ret; ++ } ++ ++ if (reg & RK818_CHRG_CTRL_REG1_CHRG_EN) ++ val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; ++ else ++ val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; ++ ++ return 0; ++ ++ case POWER_SUPPLY_PROP_CHARGE_TYPE: ++ ret = regmap_read(di->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ switch (reg & RK818_CHG_STS_MASK) { ++ case RK818_CHG_STS_WAKEUP_CUR: ++ case RK818_CHG_STS_TRICKLE_CUR: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; ++ break; ++ case RK818_CHG_STS_CC_OR_CV: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; ++ break; ++ case RK818_CHG_STS_TERMINATED: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; ++ break; ++ default: ++ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; ++ break; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: ++ ret = regmap_read(di->regmap, RK818_CHRG_CTRL_REG2, ®); ++ if (ret) ++ return ret; ++ ++ val->intval = 100000 + ((reg >> 6) & 3) * 50000; ++ break; ++ ++ case POWER_SUPPLY_PROP_HEALTH: ++ ret = regmap_read(di->regmap, RK818_SUP_STS_REG, ®); ++ if (ret) ++ return ret; ++ ++ switch (reg & RK818_CHG_STS_MASK) { ++ case RK818_CHG_STS_USB_OV: ++ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ break; ++ case RK818_CHG_STS_BAT_TEMP_FAULT: ++ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; ++ break; ++ case RK818_CHG_STS_TIMEOUT: ++ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; ++ break; ++ default: ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ break; ++ } ++ ++ break; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ return rk818_charger_get_voltage_max(di, &val->intval); ++ ++ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: ++ ret = rk818_charger_get_current_max(di, &val->intval); ++ val->intval /= 10; ++ return ret; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ return rk818_charger_get_current_max(di, &val->intval); ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ++ val->intval = di->info->constant_charge_voltage_max_uv; ++ break; ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: ++ val->intval = di->info->constant_charge_current_max_ua; ++ break; ++ ++ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: ++ val->intval = di->info->energy_full_design_uwh; ++ return 0; ++ ++ case POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN: ++ val->intval = 0; ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rk818_charger_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct rk818_battery *di = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: ++ switch (val->intval) { ++ case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: ++ return regmap_update_bits(di->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_EN, ++ RK818_CHRG_CTRL_REG1_CHRG_EN); ++ case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: ++ return regmap_update_bits(di->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_EN, 0); ++ default: ++ return -EINVAL; ++ } ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ return rk818_charger_set_voltage_max(di, val->intval); ++ ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ return rk818_charger_set_current_max(di, val->intval); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int rk818_charger_prop_writeable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: ++ return 1; ++ ++ default: ++ return 0; ++ } ++} ++ ++static enum power_supply_property rk818_charger_props[] = { ++ POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, ++ POWER_SUPPLY_PROP_CHARGE_TYPE, ++ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, ++ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_PRECHARGE_CURRENT, ++ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, ++ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, ++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ ++ /* Fuel-gauge properties exposed on the battery supply */ ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_OCV, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_CHARGE_FULL, ++}; ++ ++static const struct power_supply_desc rk818_charger_desc = { ++ .name = "rk818-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = rk818_charger_props, ++ .num_properties = ARRAY_SIZE(rk818_charger_props), ++ .property_is_writeable = rk818_charger_prop_writeable, ++ .get_property = rk818_charger_get_property, ++ .set_property = rk818_charger_set_property, ++ .charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | ++ BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE), ++}; ++ ++static int rk818_parse_dt(struct rk818_battery *di) ++{ ++ struct device *dev = di->dev; ++ struct device_node *np = dev->of_node; ++ u32 val; ++ int length; ++ int ret; ++ ++ /* Defaults for optional properties */ ++ di->monitor_sec = 5; ++ di->relax_entry_current_ma = 300; ++ di->relax_exit_current_ma = 300; ++ di->max_soc_offset_pct = 60; ++ di->sense_resist_mohm = 20; ++ di->feedback_temp = 105; /* Celsius */ ++ di->cccv_timeout_h = 12; /* hours */ ++ ++ if (!of_property_read_u32(np, "rockchip,sense-resistor-micro-ohms", ++ &val)) ++ di->sense_resist_mohm = val / 1000; ++ ++ of_property_read_u32(np, "rockchip,monitor-sec", &di->monitor_sec); ++ ++ if (!of_property_read_u32(np, "rockchip,relax-entry-current-microamp", ++ &val)) ++ di->relax_entry_current_ma = val / 1000; ++ if (!of_property_read_u32(np, "rockchip,relax-exit-current-microamp", ++ &val)) ++ di->relax_exit_current_ma = val / 1000; ++ ++ of_property_read_u32(np, "rockchip,max-soc-offset-percent", ++ &di->max_soc_offset_pct); ++ ++ /* ++ * HOTDIE threshold, in Celsius. The register only supports ++ * 85/95/105/115 C; the nearest lower entry is selected at ++ * register-write time. A DT value of 0 disables the feature. ++ */ ++ of_property_read_u32(np, "rockchip,feedback-temp-celsius", ++ &di->feedback_temp); ++ ++ /* ++ * CC/CV safety timer, in hours. The register only supports ++ * 4/5/6/8/10/12/14/16 h; the nearest lower entry is selected at ++ * register-write time. A DT value of 0 disables the timer. ++ */ ++ of_property_read_u32(np, "rockchip,cccv-timeout-hours", ++ &di->cccv_timeout_h); ++ ++ /* ++ * NTC battery thermistor table. ++ * ++ * "rockchip,ntc-table" is a flat array of ++ * pairs. Degrees are signed (negative temperatures allowed), resistances ++ * are ohms. Entries must be sorted by temperature ascending, which for ++ * an NTC means resistance is strictly descending. The table may be ++ * sparse (any step, not necessarily 1 C) -- lookup interpolates. ++ * ++ * Example: ++ * rockchip,ntc-table = < ++ * (-20) 26458836 ++ * (-15) 20048010 ++ * ( -6) 12489321 ++ * ... ++ * >; ++ */ ++ if (of_find_property(np, "rockchip,ntc-table", &length)) { ++ unsigned int cells = length / sizeof(u32); ++ unsigned int i; ++ size_t size; ++ ++ if (cells < 4 || (cells & 1)) { ++ dev_err(di->dev, ++ "rockchip,ntc-table must be an array of >=2 pairs\n"); ++ return -EINVAL; ++ } ++ ++ di->ntc_size = cells / 2; ++ size = sizeof(*di->ntc_entries) * di->ntc_size; ++ di->ntc_entries = devm_kzalloc(di->dev, size, GFP_KERNEL); ++ if (!di->ntc_entries) ++ return -ENOMEM; ++ ++ for (i = 0; i < di->ntc_size; i++) { ++ struct rk818_ntc_entry *e = &di->ntc_entries[i]; ++ u32 temp_cell; ++ ++ /* ++ * of_property_read_s32_index does not exist -- read as ++ * u32 and reinterpret. DT cells are 32-bit two's ++ * complement, so a negative value like -20 arrives as ++ * 0xffffffec and the (s32) cast recovers -20. ++ */ ++ ret = of_property_read_u32_index(np, "rockchip,ntc-table", ++ 2 * i, &temp_cell); ++ if (ret) ++ return ret; ++ e->temp = (s32)temp_cell; ++ ++ ret = of_property_read_u32_index(np, "rockchip,ntc-table", ++ 2 * i + 1, &e->res); ++ if (ret) ++ return ret; ++ ++ if (i > 0) { ++ const struct rk818_ntc_entry *p = &di->ntc_entries[i - 1]; ++ ++ if (e->temp <= p->temp) { ++ dev_err(di->dev, ++ "rockchip,ntc-table: temps must be strictly ascending (entry %u: %d <= %d)\n", ++ i, e->temp, p->temp); ++ return -EINVAL; ++ } ++ if (e->res >= p->res) { ++ dev_err(di->dev, ++ "rockchip,ntc-table: resistances must be strictly descending (entry %u: %u >= %u)\n", ++ i, e->res, p->res); ++ return -EINVAL; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------- ++ * Probe / shutdown ++ * ------------------------------------------------------------------------- */ ++ ++static int rk818_battery_probe(struct platform_device *pdev) ++{ ++ struct rk808 *rk818 = dev_get_drvdata(pdev->dev.parent); ++ struct power_supply_config psy_cfg = { }; ++ struct device *dev = &pdev->dev; ++ struct rk818_battery *di; ++ int ret, vb_lo_irq; ++ ++ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); ++ if (!di) ++ return -ENOMEM; ++ ++ di->rk818 = rk818; ++ di->pdev = pdev; ++ di->dev = dev; ++ di->regmap = rk818->regmap; ++ platform_set_drvdata(pdev, di); ++ ++ ret = rk818_parse_dt(di); ++ if (ret) ++ return ret; ++ ++ if (!rk818_battery_present(di)) ++ return dev_err_probe(di->dev, -ENODEV, ++ "no battery detected\n"); ++ ++ di->res_div = (di->sense_resist_mohm == 20) ? 1 : 2; ++ di->monitor_ms = di->monitor_sec * 1000; ++ if (!di->monitor_ms) ++ di->monitor_ms = MONITOR_MS_DEFAULT; ++ ++ /* ++ * Plausible placeholder (20.0 C) until the first monitor cycle ++ * runs rk818_update_temperature(). Without this, userspace could ++ * briefly observe 0.0 C between power-supply registration and the ++ * first poll. ++ */ ++ di->temperature = 200; ++ ++ di->boot_base_jiffies = jiffies; ++ ++ /* ++ * Register power supplies first: the power-supply core will ++ * parse the "monitored-battery" phandle on our fwnode and ++ * populate psy->battery_info with the standard simple-battery ++ * properties. The gauge init sequence below depends on those ++ * values (design capacity, OCV table, internal resistance, ++ * shutdown voltage), so it must run afterwards. ++ */ ++ psy_cfg.drv_data = di; ++ psy_cfg.fwnode = dev_fwnode(dev); ++ ++ di->usb_psy = devm_power_supply_register(dev, &rk818_usb_desc, ++ &psy_cfg); ++ if (IS_ERR(di->usb_psy)) ++ return dev_err_probe(dev, PTR_ERR(di->usb_psy), ++ "register usb power supply fail\n"); ++ ++ di->charger_psy = devm_power_supply_register(dev, &rk818_charger_desc, ++ &psy_cfg); ++ if (IS_ERR(di->charger_psy)) ++ return dev_err_probe(dev, PTR_ERR(di->charger_psy), ++ "register charger power supply fail\n"); ++ ++ di->info = di->charger_psy->battery_info; ++ if (!di->info) ++ return dev_err_probe(dev, -ENODEV, ++ "monitored-battery info not available\n"); ++ ++ rk818_enable_gauge(di); ++ rk818_init_voltage_kb(di); ++ rk818_init_coffset(di); ++ rk818_configure_sampling(di); ++ rk818_configure_relax_thresholds(di); ++ rk818_init_soc(di); ++ rk818_configure_charger(di); ++ rk818_init_ts1_detect(di); ++ rk818_set_low_voltage_irq(di); ++ ++ /* disable voltage limit and enable input current limit */ ++ ret = regmap_update_bits(di->regmap, RK818_SUP_STS_REG, ++ RK818_SUP_STS_USB_ILIM_EN | RK818_SUP_STS_USB_VLIM_EN, ++ RK818_SUP_STS_USB_ILIM_EN); ++ if (ret) ++ dev_warn(di->dev, "failed to enable input current limit (%d)\n", ret); ++ ++ /* make sure analog control loop is enabled */ ++ ret = regmap_update_bits(di->regmap, RK818_CHRG_CTRL_REG3, ++ RK818_CHRG_CTRL_REG3_CHRG_TERM_DIGITAL, ++ 0); ++ if (ret) ++ dev_warn(di->dev, "failed to enable analog control loop (%d)\n", ret); ++ ++ /* enable charger and set some reasonable limits on each boot */ ++ ret = regmap_write(di->regmap, RK818_CHRG_CTRL_REG1, ++ RK818_CHRG_CTRL_REG1_CHRG_EN ++ | (1) /* 1.2A */ ++ | (5 << 4) /* 4.3V */); ++ if (ret) ++ dev_warn(di->dev, "failed to enable charger (%d)\n", ret); ++ ++ rk818_usb_power_external_power_changed(di->usb_psy); ++ ++ vb_lo_irq = regmap_irq_get_virq(rk818->irq_data, RK818_IRQ_VB_LO); ++ if (vb_lo_irq < 0) ++ return dev_err_probe(di->dev, vb_lo_irq, ++ "cannot get vb_lo virq\n"); ++ ++ ret = devm_request_threaded_irq(di->dev, vb_lo_irq, NULL, ++ rk818_vb_low_irq, ++ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ++ "rk818_vb_low", di); ++ if (ret) ++ return dev_err_probe(di->dev, ret, ++ "failed to request vb_lo IRQ\n"); ++ ++ enable_irq_wake(vb_lo_irq); ++ ++ di->wq = alloc_ordered_workqueue("rk818-bat", ++ WQ_MEM_RECLAIM | WQ_FREEZABLE); ++ if (!di->wq) ++ return -ENOMEM; ++ ++ INIT_DELAYED_WORK(&di->work, rk818_battery_work); ++ queue_delayed_work(di->wq, &di->work, msecs_to_jiffies(di->monitor_ms)); ++ ++ dev_info(di->dev, "ready: dsoc=%d%% fcc=%d mAh sense=%u mOhm\n", ++ di->dsoc, di->fcc_mah, di->sense_resist_mohm); ++ return 0; ++} ++ ++static void rk818_battery_shutdown(struct platform_device *pdev) ++{ ++ struct rk818_battery *di = platform_get_drvdata(pdev); ++ ++ cancel_delayed_work_sync(&di->work); ++ ++ rk818_save_dsoc(di); ++ rk818_save_remain_cap(di); ++ ++ dev_info(di->dev, ++ "shutdown: dsoc=%d rsoc=%d i=%d mA v=%d mV cap=%d mAh\n", ++ di->dsoc, di->rsoc, di->current_avg_ma, ++ di->voltage_avg_mv, di->remain_cap_mah); ++} ++ ++static const struct of_device_id rk818_battery_of_match[] = { ++ { .compatible = "rockchip,rk818-charger" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rk818_battery_of_match); ++ ++static struct platform_driver rk818_battery_driver = { ++ .probe = rk818_battery_probe, ++ .suspend = rk818_battery_suspend, ++ .resume = rk818_battery_resume, ++ .shutdown = rk818_battery_shutdown, ++ .driver = { ++ .name = "rk818-charger", ++ .of_match_table = rk818_battery_of_match, ++ }, ++}; ++module_platform_driver(rk818_battery_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:rk818-charger"); ++MODULE_AUTHOR("Ondrej Jirman "); ++MODULE_DESCRIPTION("RK818 battery and charger driver"); +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index 5353a470128d..05124cf4a523 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -280,6 +280,8 @@ enum rk818_reg { + RK818_ID_OTG_SWITCH, + }; + ++#define RK818_VB_MON_REG 0x21 ++#define RK818_THERMAL_REG 0x22 + #define RK818_DCDC_EN_REG 0x23 + #define RK818_LDO_EN_REG 0x24 + #define RK818_SLEEP_SET_OFF_REG1 0x25 +@@ -326,13 +328,90 @@ enum rk818_reg { + #define RK818_INT_STS_REG2 0x4e + #define RK818_INT_STS_MSK_REG2 0x4f + #define RK818_IO_POL_REG 0x50 ++#define RK818_OTP_VDD_EN_REG 0x51 + #define RK818_H5V_EN_REG 0x52 + #define RK818_SLEEP_SET_OFF_REG3 0x53 + #define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 + #define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 + #define RK818_BOOST_CTRL_REG 0x56 +-#define RK818_DCDC_ILMAX 0x90 ++#define RK818_DCDC_ILMAX_REG 0x90 ++#define RK818_CHRG_COMP_REG 0x9a ++#define RK818_SUP_STS_REG 0xa0 + #define RK818_USB_CTRL_REG 0xa1 ++#define RK818_CHRG_CTRL_REG1 0xa3 ++#define RK818_CHRG_CTRL_REG2 0xa4 ++#define RK818_CHRG_CTRL_REG3 0xa5 ++#define RK818_BAT_CTRL_REG 0xa6 ++#define RK818_BAT_HTS_TS1_REG 0xa8 ++#define RK818_BAT_LTS_TS1_REG 0xa9 ++#define RK818_BAT_HTS_TS2_REG 0xaa ++#define RK818_BAT_LTS_TS2_REG 0xab ++#define RK818_TS_CTRL_REG 0xac ++#define RK818_ADC_CTRL_REG 0xad ++#define RK818_ON_SOURCE_REG 0xae ++#define RK818_OFF_SOURCE_REG 0xaf ++#define RK818_GGCON_REG 0xb0 ++#define RK818_GGSTS_REG 0xb1 ++#define RK818_FRAME_SMP_INTERV_REG 0xb2 ++#define RK818_AUTO_SLP_CUR_THR_REG 0xb3 ++#define RK818_GASCNT_CAL_REG3 0xb4 ++#define RK818_GASCNT_CAL_REG2 0xb5 ++#define RK818_GASCNT_CAL_REG1 0xb6 ++#define RK818_GASCNT_CAL_REG0 0xb7 ++#define RK818_GASCNT3_REG 0xb8 ++#define RK818_GASCNT2_REG 0xb9 ++#define RK818_GASCNT1_REG 0xba ++#define RK818_GASCNT0_REG 0xbb ++#define RK818_BAT_CUR_AVG_REGH 0xbc ++#define RK818_BAT_CUR_AVG_REGL 0xbd ++#define RK818_TS1_ADC_REGH 0xbe ++#define RK818_TS1_ADC_REGL 0xbf ++#define RK818_TS2_ADC_REGH 0xc0 ++#define RK818_TS2_ADC_REGL 0xc1 ++#define RK818_BAT_OCV_REGH 0xc2 ++#define RK818_BAT_OCV_REGL 0xc3 ++#define RK818_BAT_VOL_REGH 0xc4 ++#define RK818_BAT_VOL_REGL 0xc5 ++#define RK818_RELAX_ENTRY_THRES_REGH 0xc6 ++#define RK818_RELAX_ENTRY_THRES_REGL 0xc7 ++#define RK818_RELAX_EXIT_THRES_REGH 0xc8 ++#define RK818_RELAX_EXIT_THRES_REGL 0xc9 ++#define RK818_RELAX_VOL1_REGH 0xca ++#define RK818_RELAX_VOL1_REGL 0xcb ++#define RK818_RELAX_VOL2_REGH 0xcc ++#define RK818_RELAX_VOL2_REGL 0xcd ++#define RK818_BAT_CUR_R_CALC_REGH 0xce ++#define RK818_BAT_CUR_R_CALC_REGL 0xcf ++#define RK818_BAT_VOL_R_CALC_REGH 0xd0 ++#define RK818_BAT_VOL_R_CALC_REGL 0xd1 ++#define RK818_CAL_OFFSET_REGH 0xd2 ++#define RK818_CAL_OFFSET_REGL 0xd3 ++#define RK818_NON_ACT_TIMER_CNT_REG 0xd4 ++#define RK818_VCALIB0_REGH 0xd5 ++#define RK818_VCALIB0_REGL 0xd6 ++#define RK818_VCALIB1_REGH 0xd7 ++#define RK818_VCALIB1_REGL 0xd8 ++#define RK818_IOFFSET_REGH 0xdd ++#define RK818_IOFFSET_REGL 0xde ++#define RK818_SOC_REG 0xe0 ++#define RK818_REMAIN_CAP_REG3 0xe1 ++#define RK818_REMAIN_CAP_REG2 0xe2 ++#define RK818_REMAIN_CAP_REG1 0xe3 ++#define RK818_REMAIN_CAP_REG0 0xe4 ++#define RK818_UPDAT_LEVE_REG 0xe5 ++#define RK818_NEW_FCC_REG3 0xe6 ++#define RK818_NEW_FCC_REG2 0xe7 ++#define RK818_NEW_FCC_REG1 0xe8 ++#define RK818_NEW_FCC_REG0 0xe9 ++#define RK818_NON_ACT_TIMER_CNT_SAVE_REG 0xea ++#define RK818_OCV_VOL_VALID_REG 0xeb ++#define RK818_REBOOT_CNT_REG 0xec ++#define RK818_POFFSET_REG 0xed ++#define RK818_MISC_MARK_REG 0xee ++#define RK818_HALT_CNT_REG 0xef ++#define RK818_CALC_REST_REGH 0xf0 ++#define RK818_CALC_REST_REGL 0xf1 ++#define RK818_SAVE_DATA19 0xf2 + + #define RK818_H5V_EN BIT(0) + #define RK818_REF_RDY_CTRL BIT(1) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0023-power-supply-rk818_battery-Add-code-docs-for-the-HW-.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0023-power-supply-rk818_battery-Add-code-docs-for-the-HW-.patch new file mode 100644 index 000000000..e514ec63f --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0023-power-supply-rk818_battery-Add-code-docs-for-the-HW-.patch @@ -0,0 +1,281 @@ +From 4d1a2cabfffeed3f41c697d6712941adbc74f6b6 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 11 Apr 2026 16:38:03 +0200 +Subject: [PATCH 23/84] power: supply: rk818_battery: Add code/docs for the HW + reverse engineering +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-of-by: OndÅ™ej Jirman +--- + drivers/power/supply/rk818_battery.c | 246 +++++++++++++++++++++++++++ + 1 file changed, 246 insertions(+) + +diff --git a/drivers/power/supply/rk818_battery.c b/drivers/power/supply/rk818_battery.c +index 8cac751f8644..a4af2631d3bf 100644 +--- a/drivers/power/supply/rk818_battery.c ++++ b/drivers/power/supply/rk818_battery.c +@@ -2195,6 +2195,247 @@ static int rk818_parse_dt(struct rk818_battery *di) + return 0; + } + ++/* HW assessment code for bringup, remove in the future */ ++ ++static unsigned rk818_v2_read16(struct rk818_battery *di, unsigned regh, ++ unsigned mul, unsigned off) ++{ ++ unsigned val; ++ ++ val = rk818_read(di, regh + 1); ++ val |= rk818_read(di, regh) << 8; ++ ++ return val * mul / 1000 + off; ++} ++ ++static int rk818_v2_read16s(struct rk818_battery *di, unsigned regh, int mul) ++{ ++ int val; ++ ++ val = rk818_read(di, regh + 1); ++ val |= rk818_read(di, regh) << 8; ++ ++ if (val & 0x800) ++ val -= 4096; ++ ++ return val * mul / 1000; ++} ++ ++static void rk818_v2_write16(struct rk818_battery *di, unsigned regh, int val, ++ unsigned mul, unsigned off) ++{ ++ unsigned r = (val - off) * 1000 / mul; ++ ++ rk818_write(di, regh + 1, r & 0xff); ++ rk818_write(di, regh, (r >> 8) & 0xff); ++} ++ ++static int rk818_v2_get_coulomb_cap(struct rk818_battery *di) ++{ ++ int val = 0; ++ ++ val |= rk818_read(di, RK818_GASCNT3_REG) << 24; ++ val |= rk818_read(di, RK818_GASCNT2_REG) << 16; ++ val |= rk818_read(di, RK818_GASCNT1_REG) << 8; ++ val |= rk818_read(di, RK818_GASCNT0_REG); ++ ++ return val; ++} ++ ++static void rk818_v2_set_coulomb_cap(struct rk818_battery *di, u32 capacity) ++{ ++ rk818_write(di, RK818_GASCNT_CAL_REG3, (capacity >> 24) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG2, (capacity >> 16) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG1, (capacity >> 8) & 0xff); ++ rk818_write(di, RK818_GASCNT_CAL_REG0, capacity & 0xff); ++} ++ ++ ++/* ++ * ADC Voltage readout calibration ++ * ------------------------------- ++ * ++ * Two values are stored in VCALIB registers: ++ * ++ * vcalib0 = adc register value at 3.0V ++ * vcalib1 = adc register value at 4.2V ++ * ++ * k = (4200 - 3000) * 1000 / (vcalib1 - vcalib0); ++ * b = 4200 - (k * vcalib1) / 1000; ++ * vol = reg_val * k / 1000 + b ++ * ++ * Max range for ADC seems to be ~4.4V (reg val is approx. 4095) ++ * ++ * ++ * ADC Current sensing calibration ++ * ------------------------------- ++ * ++ * For current sensing, we need to eliminate offset at 0A. PMIC stores the value ++ * read out from ADC when the current should be 0 (eg. when the system is off and ++ * not charging) to IOFFSET register. ++ * ++ * We can use this value to calculate current offset and write it to CAL_OFFSET ++ * register during driver initialization. Valid range is 0x780 - 0x980 ++ * ++ * This is usually not enough, so the driver has to perform its own offset removal, ++ * by waiting for a suitable state of the device (after charging is finished or ++ * when charging is blocked, while the device is connected to external power ++ * source) and then assume there's no current flow to/from the battery and cancel ++ * the offset based on reading out the BAT_CUR_AVG code word and adding it to ++ * CAL_OFFSET, until BAT_CUR_AVG becomes 0 over several iterations. ++ * ++ * General algorithm: ++ * ++ * while (abs(BAT_CUR_AVG) > 2) { ++ * CAL_OFFSET = CAL_OFFSET + BAT_CUR_AVG ++ * wait 2s ++ * } ++ * ++ * // save a difference of CAL_OFFSET from IOFFSET for reuse later ++ * POFFSET = CAL_OFFSET - IOFFSET ++ * ++ * POFFSET is used after re-boot to initialize COFFSET to POFFSET + IOFFSET. ++ * ++ * BAT_CUR_AVG is an averaged ADC reading for current sensing resistor compensated ++ * by subtracting CAL_OFFSET from the actual ADC value. ++ * ++ * BAT_CUR_AVG range is 0-4095, format 12bit 2's complement, representing values ++ * -2048 - 2047. ++ * ++ * Current sensing resistor can be arbitrary value, but two values are supported ++ * by the original driver: ++ * ++ * 20 mOhm - ADC code word needs to be multiplied by 1.506 to get current ++ * 10 mOhm - ADC code word needs to be multiplied by 3.012 to get current (PPP) ++ * ++ * Differential voltage range on sensing resistor is +- 61.65mV ++ * ++ * That means that full range is 2047 * 3.012 = +-6.165 A and LSB is 3mA ++ * ++ * Reasonalbe COFFSET initial value for my PPP is 0x81b (when IOFFSET is 0x804) ++ * ++ * ++ * Relaxed voltage measurement ++ * --------------------------- ++ * ++ * This is a voltage measured on battery after current decreases below ++ * RELAX_ENTRY_THRES or increases above RELAX_EXIT_THRES. ++ * ++ * To measure the voltage, configure the current thresholds and clear ++ * bits 2 and 3 in GGSTS, and wait until they're set again. ++ * ++ * After COFFSET is calibrated, it's possible to detect low current scenario. ++ * After 8 minutes of BAT_CUR_AVG being < RELAX_ENTRY_THRES, BAT_VOL is ++ * written to RELAX_VOL1 and RELAX_VOL1 is written to RELAX_VOL2. This happens ++ * every 8 minutes the condition is satisfied, whithout any need to trigger ++ * this behavior. It's a 2 level queue, where RELAX_VOL1 is last item, and ++ * RELAX_VOL2 is previous one. ++ * ++ * GGSTS.RELAX_VOL1_UPD and GGSTS.RELAX_VOL2_UPD are flags indicating validity ++ * of data in RELAX_VOL# registers. They can be cleared by the driver and are ++ * set by HW when registers are updated. ++ * ++ * GGCON.OCV_SAMPL_INTERV controls the update period? ++ * ++ * ++ * Automatic switch to lower sampling frequency when current is low ++ * ---------------------------------------------------------------- ++ * ++ * This is a "sleep" mode, and supposed to be used in suspend to ram state, when ++ * current is very stable, there doesn't need to be high sampling frequency for ++ * coulomb meter. ++ * ++ * AUTO_SLP_CUR_THR - current threshold for switching to sleep mode ++ * FRAME_SMP_INTERV - sampling interval in sleep mode ++ * AUTO_SLP_EN - enable bit ++ * NON_ACT_TIMER_CNT - sleep off counter? (minutes) ++ * ++ * ++ * Coulomb meter ++ * ------------- ++ * ++ * TS_CTRL.GG_EN - enable coulomb meter (default on) ++ * ADC_CUR_VOL_MODE - bit switching between voltage/0 and current/1 mode ++ * GASCNT_CAL - sets value directly to GASCNT ++ * GASCNT - accumulates values from ADC_RES_MODE every second when in current mode ++ * ++ * ++ * OCV and Rint sensing ++ * -------------------- ++ * ++ * BAT_OCV is also updated at this time. ++ * ++ * ADC_RES_MODE - enable flag ++ * RES_CUR_AVG_SEL - amount of ripple used for Rint sensing ++ * IV_AVG_UPD_STS - flag when sensing was successful, when set? ++ * BAT_VOL_R_CALC - stable voltage for Rint calc ++ * BAT_CUR_R_CALC - stable current for Rint calc ++ * BAT_OCV - voltage? ++ */ ++ ++static void rk818_v2_work(struct work_struct *work) ++{ ++ struct rk818_battery *di = ++ container_of(work, struct rk818_battery, work.work); ++ ++ unsigned vcalib0 = rk818_v2_read16(di, RK818_VCALIB0_REGH, 1000, 0); ++ unsigned vcalib1 = rk818_v2_read16(di, RK818_VCALIB1_REGH, 1000, 0); ++ unsigned ioffset = rk818_v2_read16(di, RK818_IOFFSET_REGH, 1000, 0); ++ ++ unsigned k = (4200 - 3000) * 1000 / (vcalib1 - vcalib0); ++ unsigned b = 4200 - (k * vcalib1) / 1000; ++ int csens = 3012; /* conductance of sensing resistor */ ++ ++ unsigned vol = rk818_v2_read16(di, RK818_BAT_VOL_REGH, k, b); ++ unsigned ocv = rk818_v2_read16(di, RK818_BAT_OCV_REGH, k, b); ++ unsigned rv1 = rk818_v2_read16(di, RK818_RELAX_VOL1_REGH, k, b); ++ unsigned rv2 = rk818_v2_read16(di, RK818_RELAX_VOL2_REGH, k, b); ++ ++ unsigned sts = rk818_read(di, RK818_GGSTS_REG); ++ unsigned rv1s = !!(sts & RELAX_VOL1_UPD); ++ unsigned rv2s = !!(sts & RELAX_VOL2_UPD); ++ unsigned rs = !!(sts & RELAX_VOL_STS); ++ ++ unsigned vol_r = rk818_v2_read16(di, RK818_BAT_VOL_R_CALC_REGH, k, b); ++ int cur_r = rk818_v2_read16s(di, RK818_BAT_CUR_R_CALC_REGH, csens); ++ ++ unsigned caloff = rk818_v2_read16(di, RK818_CAL_OFFSET_REGH, 1000, 0); ++ int cur_raw = rk818_v2_read16s(di, RK818_BAT_CUR_AVG_REGH, 1000); ++ int cur = rk818_v2_read16s(di, RK818_BAT_CUR_AVG_REGH, csens); ++ ++ int cap = rk818_v2_get_coulomb_cap(di); ++ ++ dev_info(di->dev, ++ "v2: cap=%d cur=%d cur_raw=%d caloff=%u ioffset=%u vol=%u rv1=%u(%u) rv2=%u(%u) rs=%u ocv=%u cur_r=%d vol_r=%u\n", ++ cap, cur, cur_raw, caloff, ioffset, vol, rv1, rv1s, rv2, rv2s, ++ rs, ocv, cur_r, vol_r); ++ ++ queue_delayed_work(di->wq, &di->work, msecs_to_jiffies(250)); ++} ++ ++static void rk818_v2_init(struct rk818_battery *di) ++{ ++ int csens = 3012; /* conductance of sensing resistor */ ++ unsigned sts; ++ ++ rk818_v2_set_coulomb_cap(di, 0); ++ ++ /* 100 mA relax thresholds */ ++ rk818_v2_write16(di, RK818_RELAX_ENTRY_THRES_REGH, 100, csens, 0); ++ rk818_v2_write16(di, RK818_RELAX_EXIT_THRES_REGH, 100, csens, 0); ++ ++ sts = rk818_read(di, RK818_GGSTS_REG); ++ rk818_write(di, RK818_GGSTS_REG, sts & ~RELAX_VOL12_UPD_MSK); ++ ++ di->wq = alloc_ordered_workqueue("%s", ++ WQ_MEM_RECLAIM | WQ_FREEZABLE, ++ "rk818-v2-bat-work"); ++ INIT_DELAYED_WORK(&di->work, rk818_v2_work); ++ queue_delayed_work(di->wq, &di->work, msecs_to_jiffies(2000)); ++ ++ dev_info(di->dev, "v2 driver ready\n"); ++} ++ + /* ------------------------------------------------------------------------- + * Probe / shutdown + * ------------------------------------------------------------------------- */ +@@ -2217,6 +2458,11 @@ static int rk818_battery_probe(struct platform_device *pdev) + di->regmap = rk818->regmap; + platform_set_drvdata(pdev, di); + ++ if (device_property_read_bool(dev, "debug")) { ++ rk818_v2_init(di); ++ return 0; ++ } ++ + ret = rk818_parse_dt(di); + if (ret) + return ret; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0024-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0024-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch new file mode 100644 index 000000000..711a53e0c --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0024-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch @@ -0,0 +1,41 @@ +From 89a656eae098b2a5089daba98f522eb3afadfcc2 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 7 May 2024 18:39:12 +0200 +Subject: [PATCH 24/84] Revert "usb: typec: tcpm: unregister existing source + caps before re-registration" + +This reverts commit 230ecdf71a644c9c73e0e6735b33173074ae3f94. + +(cherry picked from commit c64dcf8a8fb966ba3c537fd9722875a6fd1dd9ad) +--- + drivers/usb/typec/tcpm/tcpm.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index 7ef746a90a17..d839c72fa8ee 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -3306,7 +3306,7 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + { + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; +- struct usb_power_delivery_capabilities *cap = port->partner_source_caps; ++ struct usb_power_delivery_capabilities *cap; + + if (!port->partner_pd) + port->partner_pd = usb_power_delivery_register(NULL, &desc); +@@ -3316,11 +3316,6 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps); + caps.role = TYPEC_SOURCE; + +- if (cap) { +- usb_power_delivery_unregister_capabilities(cap); +- port->partner_source_caps = NULL; +- } +- + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0025-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0025-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch new file mode 100644 index 000000000..f93b226f4 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0025-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch @@ -0,0 +1,105 @@ +From e2139511dc9de1531aba8bed11bc20c4be3ea80a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 5 Sep 2022 00:56:07 +0200 +Subject: [PATCH 25/84] usb: typec: altmodes: displayport: Respect + DP_CAP_RECEPTACLE bit + +DP_CAP_RECEPTACLE swaps the meaning of pin assignments. So eg. +pin assignments for DFP_D plug are stored in DP_CAP_UFP_D_PIN_ASSIGN +bits. Yes, it's slightly confusing. :) + +Make the kernel report lack of match in supported pin configurations +between connected ports, so that the user is not confused why their +USB-C dock doesn't have working Alt-DP mode, in case the dock returns +wrong VDO. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 24fc74e1651b5ad8c83ac073b4327b09e09ca5a5) +--- + drivers/usb/typec/altmodes/displayport.c | 54 +++++++++++++++++++++--- + 1 file changed, 48 insertions(+), 6 deletions(-) + +diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c +index 263a89c5f324..722030137015 100644 +--- a/drivers/usb/typec/altmodes/displayport.c ++++ b/drivers/usb/typec/altmodes/displayport.c +@@ -128,11 +128,29 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) + /* Account for active cable capabilities */ + if (dp->plug_prime) + pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo); ++ ++ /* ++ * The Display Port Alt mode standard is not publicly available, ++ * so this is based on guesswork and real VDOs received from ++ * receptacle based and plug based Type-C alt mode supporting ++ * docks to make configuration work in practice: ++ * ++ * Plug (captive cable) based dock: port=c46 alt=c05 ++ * Recpetacle based dock: port=c46 alt=c0045 ++ * ++ pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); ++ pin_assign &= dp->alt->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) : ++ DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); ++ */ + break; + default: + break; + } + ++ dev_info(&dp->alt->dev, "con=%d pin_assign=%x (port=%x alt=%x)\n", ++ (int)con, (unsigned)pin_assign, dp->port->vdo, dp->alt->vdo); ++ + /* Determining the initial pin assignment. */ + if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) { + /* Is USB together with DP preferred */ +@@ -764,16 +782,40 @@ int dp_altmode_probe(struct typec_altmode *alt) + struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + struct fwnode_handle *fwnode; + struct dp_altmode *dp; ++ u32 port_pins, alt_pins; + + /* Port can only be DFP_U. */ +- if (typec_altmode_get_data_role(alt) != TYPEC_HOST) ++ if (typec_altmode_get_data_role(alt) != TYPEC_HOST) { ++ typec_altmode_put_plug(plug); + return -EPROTO; ++ } ++ ++ /* ++ * When port is a receptacle DP_CAP_xFP_D_PIN_ASSIGN macros have the ++ * regular meaning. When the port is a plug, the meaning is swapped. ++ * ++ * Check if we have any matching DFP_D<->UFP_D or UFP_D<->DFP_D pin assignment. ++ */ ++ port_pins = port->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) << 8 : ++ DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) << 8; ++ ++ alt_pins = alt->vdo & DP_CAP_RECEPTACLE ? ++ DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) << 8 : ++ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) << 8; ++ ++ /* Can't plug plug into a plug */ ++ if (!(port->vdo & DP_CAP_RECEPTACLE) && !(alt->vdo & DP_CAP_RECEPTACLE)) { ++ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (both are reported as plugs!)\n", ++ port->vdo, alt->vdo); ++ typec_altmode_put_plug(plug); ++ return -ENODEV; ++ } + +- /* Make sure we have compatible pin configurations */ +- if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & +- DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && +- !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & +- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) { ++ /* Make sure we have compatiple pin configurations */ ++ if (!(port_pins & alt_pins)) { ++ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (no shared pinconf %04x<->%04x (UUDD))\n", ++ port->vdo, alt->vdo, port_pins, alt_pins); + typec_altmode_put_plug(plug); + return -ENODEV; + } +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0026-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0026-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch new file mode 100644 index 000000000..2bd737a7e --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0026-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch @@ -0,0 +1,54 @@ +From 75ba5f3d7e740107b5e471f23e06015c8ae552e3 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 14 Aug 2022 16:23:28 +0200 +Subject: [PATCH 26/84] usb: typec: tcpm: Unregister altmodes before + registering new ones + +Just in case there are already altmodes registered. Otherwise the +kernel will fail with: + +[50554.467172] sysfs: cannot create duplicate filename '/bus/typec/devices/port0-partner.0' +[50554.467198] CPU: 2 PID: 897 Comm: 4-0022 Tainted: G C 5.18.15-1-MANJARO-ARM #1 +[50554.467208] Hardware name: Pine64 PinePhonePro (DT) +[50554.467214] Call trace: +[50554.467218] dump_backtrace.part.0+0xcc/0xe0 +[50554.467241] show_stack+0x18/0x6c +[50554.467249] dump_stack_lvl+0x64/0x80 +[50554.467262] dump_stack+0x18/0x34 +[50554.467270] sysfs_warn_dup+0x60/0x7c +[50554.467279] sysfs_do_create_link_sd+0xf0/0x100 +[50554.467286] sysfs_create_link+0x24/0x44 +[50554.467292] bus_add_device+0x64/0x120 +[50554.467304] device_add+0x320/0x890 +[50554.467312] device_register+0x20/0x30 +[50554.467318] typec_register_altmode+0x1e4/0x35c +[50554.467330] typec_partner_register_altmode+0x10/0x20 +[50554.467339] tcpm_pd_rx_handler+0x1804/0x18e0 +[50554.467346] kthread_worker_fn+0xa0/0x180 +[50554.467357] kthread+0x108/0x10c +[50554.467364] ret_from_fork+0x10/0x20 +[50554.467528] typec port0-partner: failed to register alternate mode (-17) + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 30d0f1a4e665f05a1b3a455c84be97ae9011285f) +--- + drivers/usb/typec/tcpm/tcpm.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index d839c72fa8ee..d30d7a0a4ac3 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -2030,6 +2030,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) + return; + + for (i = 0; i < modep->altmodes; i++) { ++ typec_unregister_altmode(port->partner_altmode[i]); ++ port->partner_altmode[i] = NULL; ++ + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0027-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0027-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch new file mode 100644 index 000000000..95fa5587e --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0027-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch @@ -0,0 +1,97 @@ +From dc4a3a79432f9cadd1e384806150ee0c50caac40 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sat, 18 Feb 2023 00:38:44 +0100 +Subject: [PATCH 27/84] usb: typec: tcpm: Fix PD devices/capabilities + registration + +Unregister caps before registering them. Store NULL to the struct +if registration fails, so that next attempt can succeed. + +Fixes "sysfs: cannot create duplicate filename +'/devices/virtual/usb_power_delivery/pd1/source-capabilities'" error. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 49530effc5b55eaee992aca23ef4302c64543e6d) +--- + drivers/usb/typec/tcpm/tcpm.c | 37 +++++++++++++++++++++++++++-------- + 1 file changed, 29 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +index d30d7a0a4ac3..3ba7511a2785 100644 +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -3310,15 +3310,22 @@ static int tcpm_register_source_caps(struct tcpm_port *port) + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; ++ struct usb_power_delivery *partner_pd; ++ ++ if (!port->partner_pd) { ++ partner_pd = usb_power_delivery_register(NULL, &desc); ++ if (IS_ERR(partner_pd)) ++ return PTR_ERR(partner_pd); + +- if (!port->partner_pd) +- port->partner_pd = usb_power_delivery_register(NULL, &desc); +- if (IS_ERR(port->partner_pd)) +- return PTR_ERR(port->partner_pd); ++ port->partner_pd = partner_pd; ++ } + + memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps); + caps.role = TYPEC_SOURCE; + ++ usb_power_delivery_unregister_capabilities(port->partner_source_caps); ++ port->partner_source_caps = NULL; ++ + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +@@ -3333,15 +3340,22 @@ static int tcpm_register_sink_caps(struct tcpm_port *port) + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; ++ struct usb_power_delivery *partner_pd; ++ ++ if (!port->partner_pd) { ++ partner_pd = usb_power_delivery_register(NULL, &desc); ++ if (IS_ERR(partner_pd)) ++ return PTR_ERR(partner_pd); + +- if (!port->partner_pd) +- port->partner_pd = usb_power_delivery_register(NULL, &desc); +- if (IS_ERR(port->partner_pd)) +- return PTR_ERR(port->partner_pd); ++ port->partner_pd = partner_pd; ++ } + + memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps); + caps.role = TYPEC_SINK; + ++ usb_power_delivery_unregister_capabilities(port->partner_sink_caps); ++ port->partner_sink_caps = NULL; ++ + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); +@@ -7746,10 +7760,17 @@ static int tcpm_port_register_pd(struct tcpm_port *port) + port->pds[i] = usb_power_delivery_register(port->dev, &desc); + if (IS_ERR(port->pds[i])) { + ret = PTR_ERR(port->pds[i]); ++ port->pds[i] = NULL; + goto err_unregister; + } + port->pd_list[i]->pd = port->pds[i]; + ++ usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap); ++ port->pd_list[i]->source_cap = NULL; ++ ++ usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap); ++ port->pd_list[i]->sink_cap = NULL; ++ + if (port->pd_list[i]->source_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->source_desc); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0028-usb-typec-fusb302-Slightly-increase-wait-time-for-BC.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0028-usb-typec-fusb302-Slightly-increase-wait-time-for-BC.patch new file mode 100644 index 000000000..f30f184c3 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0028-usb-typec-fusb302-Slightly-increase-wait-time-for-BC.patch @@ -0,0 +1,35 @@ +From 00a769f5a26e2aad92648a76fbe243224701322f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 30 Jan 2022 23:46:37 +0100 +Subject: [PATCH 28/84] usb: typec: fusb302: Slightly increase wait time for + BC1.2 result + +This is not strictly necessary, but it should not hurt. Looking at +kernel log timing, the BC1.2 detection completion time and this +timeout are quite tight. This adds some headspace just in case. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit f374eae3ca1bf1b0b2fdced9bb6eb475e92110c0) +--- + drivers/usb/typec/tcpm/fusb302.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 9ab1277b7ed1..7ccdb9df7dde 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -448,9 +448,9 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) + + /* + * USB2 Charger detection may still be in progress when we get here, +- * this can take upto 600ms, wait 800ms max. ++ * this can take upto 600ms, wait 1000ms max. + */ +- timeout = jiffies + msecs_to_jiffies(800); ++ timeout = jiffies + msecs_to_jiffies(1000); + do { + if (extcon_get_state(chip->extcon, EXTCON_CHG_USB_SDP) == 1) + current_limit = 500; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0029-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0029-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch new file mode 100644 index 000000000..f545450ba --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0029-usb-typec-fusb302-Set-the-current-before-enabling-pu.patch @@ -0,0 +1,52 @@ +From 52f27d4d137c4a1d5743618d8c4e5eb4b9fec2d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 7 Nov 2021 19:28:27 +0100 +Subject: [PATCH 29/84] usb: typec: fusb302: Set the current before enabling + pullups + +This seems more reasonable and should avoid short period of incorrect +current setting being applied to CC pin. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 7c245e878d2f39174a142921dcb14bcd6fd71878) +--- + drivers/usb/typec/tcpm/fusb302.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 7ccdb9df7dde..a0382b550e3f 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -638,6 +638,14 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) + goto done; + } + ++ /* adjust current for SRC */ ++ ret = fusb302_set_src_current(chip, cc_src_current[cc]); ++ if (ret < 0) { ++ fusb302_log(chip, "cannot set src current %s, ret=%d", ++ typec_cc_status_name[cc], ret); ++ goto done; ++ } ++ + ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, + switches0_mask, switches0_data); + if (ret < 0) { +@@ -648,14 +656,6 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) + chip->cc1 = TYPEC_CC_OPEN; + chip->cc2 = TYPEC_CC_OPEN; + +- /* adjust current for SRC */ +- ret = fusb302_set_src_current(chip, cc_src_current[cc]); +- if (ret < 0) { +- fusb302_log(chip, "cannot set src current %s, ret=%d", +- typec_cc_status_name[cc], ret); +- goto done; +- } +- + /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */ + switch (cc) { + case TYPEC_CC_RP_DEF: +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0030-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0030-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch new file mode 100644 index 000000000..827bc40dd --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0030-usb-typec-fusb302-Retry-reading-of-CC-pins-status-if.patch @@ -0,0 +1,77 @@ +From c4f121b253c37de7cc2aee59bff8dd523dfa562d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:53:27 +0100 +Subject: [PATCH 30/84] usb: typec: fusb302: Retry reading of CC pins status if + activity is detected + +This is just for testing, to see if this ever happens. It should +also help when this happens. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 48646c9a3afd88fb7cf7f1941bc3666fc23deea6) +--- + drivers/usb/typec/tcpm/fusb302.c | 34 ++++++++++++++++++++++++++++++-- + 1 file changed, 32 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index a0382b550e3f..0de0dc561c8f 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -1248,6 +1248,36 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip, + return ret; + } + ++static int fusb302_get_status0_stable(struct fusb302_chip *chip, u8 *status0) ++{ ++ int ret, tries = 0; ++ u8 reg; ++ ++try_again: ++ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, ®); ++ if (ret < 0) ++ return ret; ++ ++ if (reg & FUSB_REG_STATUS0_ACTIVITY) { ++ fusb302_log(chip, "activity reading CC status"); ++ if (++tries == 5) { ++ fusb302_log(chip, "failed to read stable status0 value"); ++ ++ /* ++ * The best we can do is to return at least something. ++ */ ++ *status0 = reg; ++ return 0; ++ } ++ ++ usleep_range(50, 100); ++ goto try_again; ++ } ++ ++ *status0 = reg; ++ return 0; ++} ++ + /* On error returns < 0, otherwise a typec_cc_status value */ + static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + enum typec_cc_polarity cc_polarity, +@@ -1275,7 +1305,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + return ret; + + usleep_range(50, 100); +- ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); ++ ret = fusb302_get_status0_stable(chip, &status0); + if (ret < 0) + return ret; + +@@ -1291,7 +1321,7 @@ static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + return ret; + + usleep_range(50, 100); +- ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); ++ ret = fusb302_get_status0_stable(chip, &status0); + if (ret < 0) + return ret; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0031-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0031-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch new file mode 100644 index 000000000..0aff562db --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0031-usb-typec-fusb302-Update-VBUS-state-even-if-VBUS-int.patch @@ -0,0 +1,44 @@ +From 91a432b505c84f9552e2d048b1cc4c863a84497f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Tue, 23 Nov 2021 17:57:06 +0100 +Subject: [PATCH 31/84] usb: typec: fusb302: Update VBUS state even if VBUS + interrupt is not triggered + +This seems to improve robustness. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 5097f6ffab505803eae7fb02a411f0c0d9be2732) +--- + drivers/usb/typec/tcpm/fusb302.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 0de0dc561c8f..20c9790a4fad 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -1567,14 +1567,16 @@ static void fusb302_irq_work(struct work_struct *work) + "IRQ: 0x%02x, a: 0x%02x, b: 0x%02x, status0: 0x%02x", + interrupt, interrupta, interruptb, status0); + +- if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { +- vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); ++ vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); ++ if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) + fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s", + str_on_off(vbus_present)); +- if (vbus_present != chip->vbus_present) { +- chip->vbus_present = vbus_present; +- tcpm_vbus_change(chip->tcpm_port); +- } ++ if (vbus_present != chip->vbus_present) { ++ chip->vbus_present = vbus_present; ++ if (!(interrupt & FUSB_REG_INTERRUPT_VBUSOK)) ++ fusb302_log(chip, "IRQ: VBUS changed without interrupt, vbus=%s", ++ str_on_off(vbus_present)); ++ tcpm_vbus_change(chip->tcpm_port); + } + + if ((interrupta & FUSB_REG_INTERRUPTA_TOGDONE) && intr_togdone) { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0032-usb-typec-fusb302-Add-OF-extcon-support.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0032-usb-typec-fusb302-Add-OF-extcon-support.patch new file mode 100644 index 000000000..e0b5b60d0 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0032-usb-typec-fusb302-Add-OF-extcon-support.patch @@ -0,0 +1,45 @@ +From aec8f45667160d719b2e938ae6f48b641a4a1a6c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 14 Nov 2021 01:14:25 +0100 +Subject: [PATCH 32/84] usb: typec: fusb302: Add OF extcon support + +Look the extcon provider up by phandle, so a device tree can point at the +USB2 PHY that runs BC1.2 charger detection. Until now it could only be +named through linux,extcon-name, which probe reserves for ACPI platforms, +leaving a DT board on a dedicated charger stuck on whatever the Type-C +resistors advertise. + +Resolve it on first use rather than in probe, because the provider can sit +behind a driver that waits on this one and resolving it during probe would +close that cycle. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit fe3483dca7f8cdc09397019ba583c345177f27c0) +--- + drivers/usb/typec/tcpm/fusb302.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 20c9790a4fad..756582f551be 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -443,6 +443,16 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) + int current_limit = 0; + unsigned long timeout; + ++ /* ++ * To avoid cycles in OF dependencies, we get extcon when necessary ++ * outside of probe function. ++ */ ++ if (of_property_present(chip->dev->of_node, "extcon") && !chip->extcon) { ++ chip->extcon = extcon_get_edev_by_phandle(chip->dev, 0); ++ if (IS_ERR(chip->extcon)) ++ chip->extcon = NULL; ++ } ++ + if (!chip->extcon) + return 0; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0033-usb-typec-fusb302-Fix-register-definitions.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0033-usb-typec-fusb302-Fix-register-definitions.patch new file mode 100644 index 000000000..bccca0324 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0033-usb-typec-fusb302-Fix-register-definitions.patch @@ -0,0 +1,50 @@ +From 007feb7fc3b91608df648b7e88a734907213c35a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 20 Nov 2021 14:33:58 +0100 +Subject: [PATCH 33/84] usb: typec: fusb302: Fix register definitions + +MEASURE_VBUS bit is at position 6. MDAC bits are also wrong. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 3d44e93f55bb27844753bb8b142d61ff80347f3a) +--- + drivers/usb/typec/tcpm/fusb302_reg.h | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h +index edc0e4b0f1e6..f37d226c5027 100644 +--- a/drivers/usb/typec/tcpm/fusb302_reg.h ++++ b/drivers/usb/typec/tcpm/fusb302_reg.h +@@ -27,14 +27,13 @@ + #define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1) + #define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0) + #define FUSB_REG_MEASURE 0x04 +-#define FUSB_REG_MEASURE_MDAC5 BIT(7) +-#define FUSB_REG_MEASURE_MDAC4 BIT(6) +-#define FUSB_REG_MEASURE_MDAC3 BIT(5) +-#define FUSB_REG_MEASURE_MDAC2 BIT(4) +-#define FUSB_REG_MEASURE_MDAC1 BIT(3) +-#define FUSB_REG_MEASURE_MDAC0 BIT(2) +-#define FUSB_REG_MEASURE_VBUS BIT(1) +-#define FUSB_REG_MEASURE_XXXX5 BIT(0) ++#define FUSB_REG_MEASURE_VBUS BIT(6) ++#define FUSB_REG_MEASURE_MDAC5 BIT(5) ++#define FUSB_REG_MEASURE_MDAC4 BIT(4) ++#define FUSB_REG_MEASURE_MDAC3 BIT(3) ++#define FUSB_REG_MEASURE_MDAC2 BIT(2) ++#define FUSB_REG_MEASURE_MDAC1 BIT(1) ++#define FUSB_REG_MEASURE_MDAC0 BIT(0) + #define FUSB_REG_CONTROL0 0x06 + #define FUSB_REG_CONTROL0_TX_FLUSH BIT(6) + #define FUSB_REG_CONTROL0_INT_MASK BIT(5) +@@ -105,7 +104,6 @@ + #define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1) + #define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0) + #define FUSB_REG_STATUS1A 0x3D +-#define FUSB_REG_STATUS1A_TOGSS BIT(3) + #define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0 + #define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1 + #define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2 +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0034-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0034-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch new file mode 100644 index 000000000..da3171f18 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0034-usb-typec-fusb302-Clear-interrupts-before-we-start-t.patch @@ -0,0 +1,42 @@ +From 415988faed2eca45c77ae549d9a5ee67df31bf7c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 20 Nov 2021 14:35:10 +0100 +Subject: [PATCH 34/84] usb: typec: fusb302: Clear interrupts before we start + toggling + +This is recommended by the datasheet. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 58df0404dd80e7fff7fe3e7c2e4dc1f61614153c) +--- + drivers/usb/typec/tcpm/fusb302.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index 756582f551be..fe5ac6ec954a 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -511,6 +511,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, + enum toggling_mode mode) + { + int ret = 0; ++ u8 reg; + + /* first disable toggling */ + ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL2, +@@ -569,6 +570,12 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, + } else { + /* Datasheet says vconn MUST be off when toggling */ + WARN(chip->vconn_on, "Vconn is on during toggle start"); ++ ++ /* clear interrupts */ ++ ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPT, ®); ++ if (ret < 0) ++ return ret; ++ + /* unmask TOGDONE interrupt */ + ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, + FUSB_REG_MASKA_TOGDONE); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0035-usb-typec-fusb302-Turn-off-VBUS-and-VCONN-on-shutdow.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0035-usb-typec-fusb302-Turn-off-VBUS-and-VCONN-on-shutdow.patch new file mode 100644 index 000000000..fcefc872a --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0035-usb-typec-fusb302-Turn-off-VBUS-and-VCONN-on-shutdow.patch @@ -0,0 +1,59 @@ +From 6190b644bd853b35f7908a27e17f30aa7b09a785 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 08:45:42 +0200 +Subject: [PATCH 35/84] usb: typec: fusb302: Turn off VBUS and VCONN on + shutdown + +Nothing turns the port off on the way down, so a source keeps VBUS and +VCONN applied across a reboot. The next kernel comes up with the port +already powered, which starts BC1.2 charger detection before any role is +known and leaves the Type-C state machine attaching to a port it never +saw come up. + +Drop both in a shutdown handler so a reboot looks like a cold boot. + +(cherry picked from commit 4109d433711e9873a50152640c5a89cf0c91cbe2) +--- + drivers/usb/typec/tcpm/fusb302.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c +index fe5ac6ec954a..ba886c949e47 100644 +--- a/drivers/usb/typec/tcpm/fusb302.c ++++ b/drivers/usb/typec/tcpm/fusb302.c +@@ -1839,6 +1839,24 @@ static int fusb302_probe(struct i2c_client *client) + return ret; + } + ++static void fusb302_shutdown(struct i2c_client *client) ++{ ++ struct fusb302_chip *chip = i2c_get_clientdata(client); ++ ++ /* ++ * Nothing else turns the port off on the way down, so a source ++ * would keep VBUS and VCONN applied across a reboot. The next ++ * kernel then comes up with the port already powered, which ++ * confuses both charger detection and the Type-C state machine. ++ */ ++ disable_irq(chip->gpio_int_n_irq); ++ cancel_work_sync(&chip->irq_work); ++ cancel_delayed_work_sync(&chip->bc_lvl_handler); ++ ++ tcpm_set_vconn(&chip->tcpc_dev, false); ++ tcpm_set_vbus(&chip->tcpc_dev, false, false); ++} ++ + static void fusb302_remove(struct i2c_client *client) + { + struct fusb302_chip *chip = i2c_get_clientdata(client); +@@ -1908,6 +1926,7 @@ static struct i2c_driver fusb302_driver = { + }, + .probe = fusb302_probe, + .remove = fusb302_remove, ++ .shutdown = fusb302_shutdown, + .id_table = fusb302_i2c_device_id, + }; + module_i2c_driver(fusb302_driver); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0036-phy-rockchip-inno-usb2-Add-support-for-RV1106-RV1103.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0036-phy-rockchip-inno-usb2-Add-support-for-RV1106-RV1103.patch new file mode 100644 index 000000000..cd076d828 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0036-phy-rockchip-inno-usb2-Add-support-for-RV1106-RV1103.patch @@ -0,0 +1,209 @@ +From 8188ebbecf26e4e5666b5e306da6d3b675451a0c Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 21 Jun 2024 01:42:38 +0200 +Subject: [PATCH 36/84] phy: rockchip-inno-usb2: Add support for RV1106/RV1103 + +Ported from BSP driver. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 9ce7df89d6d1a4408de349a80e51dceb49d95227) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 133 ++++++++++++++++++ + 1 file changed, 133 insertions(+) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 8f4c08e599aa..4ef42d9affb3 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -143,6 +143,7 @@ struct rockchip_usb2phy_port_cfg { + struct usb2phy_reg bvalid_det_en; + struct usb2phy_reg bvalid_det_st; + struct usb2phy_reg bvalid_det_clr; ++ struct usb2phy_reg bvalid_grf_sel; + struct usb2phy_reg disfall_en; + struct usb2phy_reg disfall_st; + struct usb2phy_reg disfall_clr; +@@ -248,6 +249,7 @@ struct rockchip_usb2phy { + struct regmap *grf; + struct regmap *usbgrf; + struct clk_bulk_data *clks; ++ void __iomem *phy_base; + struct clk *clk480m; + struct clk_hw clk480m_hw; + int num_clks; +@@ -1284,6 +1286,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) + { ++ struct regmap *base = get_reg_base(rphy); + int ret, id; + + rport->port_id = USB2PHY_PORT_OTG; +@@ -1317,6 +1320,23 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + goto out; + } + ++ /* ++ * Set the utmi bvalid come from the usb phy or grf. ++ * For most of Rockchip SoCs, them have VBUSDET pin ++ * for the usb phy to detect the USB VBUS and set ++ * the bvalid signal, so select the bvalid from the ++ * usb phy by default. And for those SoCs which don't ++ * have VBUSDET pin (e.g. RV1103), it needs to select ++ * the bvaid from the grf and set bvalid to be valid ++ * (high) by default. ++ */ ++ if (rport->port_cfg->bvalid_grf_sel.enable != 0) { ++ if (of_machine_is_compatible("rockchip,rv1103")) ++ property_enable(base, &rport->port_cfg->bvalid_grf_sel, true); ++ else ++ property_enable(base, &rport->port_cfg->bvalid_grf_sel, false); ++ } ++ + if (!IS_ERR(rphy->edev)) { + rport->event_nb.notifier_call = rockchip_otg_event; + +@@ -1388,6 +1408,13 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) + } + } + ++ if (of_device_is_compatible(np, "rockchip,rv1106-usb2phy")) { ++ rphy->phy_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); ++ if (IS_ERR(rphy->phy_base)) ++ return dev_err_probe(dev, PTR_ERR(rphy->phy_base), ++ "missing memory resource\n"); ++ } ++ + rphy->dev = dev; + phy_cfgs = device_get_match_data(dev); + rphy->chg_state = USB_CHG_STATE_UNDEFINED; +@@ -1506,6 +1533,63 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) + return ret; + } + ++static inline void phy_clear_bits(void __iomem *reg, u32 bits) ++{ ++ u32 tmp = readl(reg); ++ ++ tmp &= ~bits; ++ writel(tmp, reg); ++} ++ ++static inline void phy_set_bits(void __iomem *reg, u32 bits) ++{ ++ u32 tmp = readl(reg); ++ ++ tmp |= bits; ++ writel(tmp, reg); ++} ++ ++static inline void phy_update_bits(void __iomem *reg, u32 mask, u32 val) ++{ ++ u32 tmp = readl(reg); ++ ++ tmp &= ~mask; ++ tmp |= val & mask; ++ writel(tmp, reg); ++} ++ ++static int rv1106_usb2phy_tuning(struct rockchip_usb2phy *rphy) ++{ ++ /* Always enable pre-emphasis in SOF & EOP & chirp & non-chirp state */ ++ phy_update_bits(rphy->phy_base + 0x30, GENMASK(2, 0), 0x07); ++ ++// if (rockchip_get_cpu_version()) { ++// /* Set Tx HS pre_emphasize strength to 3'b001 */ ++// phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x01 << 3)); ++// } else { ++ /* Set Tx HS pre_emphasize strength to 3'b011 */ ++ phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x03 << 3)); ++// } ++ ++ /* Set RX Squelch trigger point configure to 4'b0000(112.5 mV) */ ++ phy_update_bits(rphy->phy_base + 0x64, GENMASK(6, 3), (0x00 << 3)); ++ ++ /* Turn off differential receiver by default to save power */ ++ phy_clear_bits(rphy->phy_base + 0x100, BIT(6)); ++ ++ /* Set 45ohm HS ODT value to 5'b10111 to increase driver strength */ ++ phy_update_bits(rphy->phy_base + 0x11c, GENMASK(4, 0), 0x17); ++ ++ /* Set Tx HS eye height tuning to 3'b011(462 mV)*/ ++ phy_update_bits(rphy->phy_base + 0x124, GENMASK(4, 2), (0x03 << 2)); ++ ++ /* Bypass Squelch detector calibration */ ++ phy_update_bits(rphy->phy_base + 0x1a4, GENMASK(7, 4), (0x01 << 4)); ++ phy_update_bits(rphy->phy_base + 0x1b4, GENMASK(7, 4), (0x01 << 4)); ++ ++ return 0; ++} ++ + static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) + { + /* Turn off differential receiver in suspend mode */ +@@ -2243,6 +2327,54 @@ static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = { + { /* sentinel */ } + }; + ++static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = { ++ { ++ .reg = 0xff3e0000, ++ .num_ports = 1, ++ .phy_tuning = rv1106_usb2phy_tuning, ++ .clkout_ctl = { 0x0058, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x0050, 8, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0100, 2, 2, 0, 1 }, ++ .bvalid_det_st = { 0x0104, 2, 2, 0, 1 }, ++ .bvalid_det_clr = { 0x0108, 2, 2, 0, 1 }, ++ .bvalid_grf_sel = { 0x0058, 15, 14, 0, 3 }, ++ /* ++ .iddig_output = { 0x0050, 10, 10, 0, 1 }, ++ .iddig_en = { 0x0050, 9, 9, 0, 1 }, ++ */ ++ .idfall_det_en = { 0x0100, 5, 5, 0, 1 }, ++ .idfall_det_st = { 0x0104, 5, 5, 0, 1 }, ++ .idfall_det_clr = { 0x0108, 5, 5, 0, 1 }, ++ .idrise_det_en = { 0x0100, 4, 4, 0, 1 }, ++ .idrise_det_st = { 0x0104, 4, 4, 0, 1 }, ++ .idrise_det_clr = { 0x0108, 4, 4, 0, 1 }, ++ .ls_det_en = { 0x0100, 0, 0, 0, 1 }, ++ .ls_det_st = { 0x0104, 0, 0, 0, 1 }, ++ .ls_det_clr = { 0x0108, 0, 0, 0, 1 }, ++ .utmi_avalid = { 0x0060, 10, 10, 0, 1 }, ++ .utmi_bvalid = { 0x0060, 9, 9, 0, 1 }, ++ //.utmi_iddig = { 0x0060, 6, 6, 0, 1 }, ++ .utmi_ls = { 0x0060, 5, 4, 0, 1 }, ++ }, ++ }, ++ .chg_det = { ++ //.chg_mode = { 0x0050, 8, 0, 0, 0x1d7 }, ++ .cp_det = { 0x0060, 13, 13, 0, 1 }, ++ .dcp_det = { 0x0060, 12, 12, 0, 1 }, ++ .dp_det = { 0x0060, 14, 14, 0, 1 }, ++ .idm_sink_en = { 0x0058, 8, 8, 0, 1 }, ++ .idp_sink_en = { 0x0058, 7, 7, 0, 1 }, ++ .idp_src_en = { 0x0058, 9, 9, 0, 1 }, ++ .rdm_pdwn_en = { 0x0058, 10, 10, 0, 1 }, ++ .vdm_src_en = { 0x0058, 12, 12, 0, 1 }, ++ .vdp_src_en = { 0x0058, 11, 11, 0, 1 }, ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ + static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { + { + .reg = 0x100, +@@ -2299,6 +2431,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { + { .compatible = "rockchip,rk3576-usb2phy", .data = &rk3576_phy_cfgs }, + { .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs }, + { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, ++ { .compatible = "rockchip,rv1106-usb2phy", .data = &rv1106_phy_cfgs }, + {} + }; + MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0037-phy-rockchip-inno-usb2-Add-support-for-RK3506.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0037-phy-rockchip-inno-usb2-Add-support-for-RK3506.patch new file mode 100644 index 000000000..96a5b6e87 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0037-phy-rockchip-inno-usb2-Add-support-for-RK3506.patch @@ -0,0 +1,156 @@ +From 6b13b8431d4bb87a6186a4855f4855470e0c9191 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 6 Apr 2025 02:00:19 +0200 +Subject: [PATCH 37/84] phy: rockchip-inno-usb2: Add support for RK3506 + +Adapted from BSP. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 370adcd5cf6464363b7ceb30ebd43243b11dded1) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 115 ++++++++++++++++++ + 1 file changed, 115 insertions(+) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 4ef42d9affb3..ffc0a7112b6e 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -1598,6 +1598,31 @@ static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) + BIT(2) << BIT_WRITEABLE_SHIFT | 0); + } + ++static int rk3506_usb2phy_tuning(struct rockchip_usb2phy *rphy) ++{ ++ int ret = 0; ++ ++ /* Turn off otg0 port differential receiver in suspend mode */ ++ phy_clear_bits(rphy->phy_base + 0x30, BIT(2)); ++ ++ /* Turn off otg1 port differential receiver in suspend mode */ ++ phy_clear_bits(rphy->phy_base + 0x430, BIT(2)); ++ ++ /* Set otg0 port HS eye height to 425mv(default is 450mv) */ ++ phy_update_bits(rphy->phy_base + 0x30, GENMASK(6, 4), (0x05 << 4)); ++ ++ /* Set otg1 port HS eye height to 425mv(default is 450mv) */ ++ phy_update_bits(rphy->phy_base + 0x430, GENMASK(6, 4), (0x05 << 4)); ++ ++ /* Choose the Tx fs/ls data as linestate from TX driver for otg0 port */ ++ phy_update_bits(rphy->phy_base + 0x94, GENMASK(6, 3), (0x03 << 3)); ++ ++ /* Choose the Tx fs/ls data as linestate from TX driver for otg1 port */ ++ phy_update_bits(rphy->phy_base + 0x494, GENMASK(6, 3), (0x03 << 3)); ++ ++ return ret; ++} ++ + static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy) + { + int ret; +@@ -2059,6 +2084,95 @@ static const struct rockchip_usb2phy_cfg rk3562_phy_cfgs[] = { + { /* sentinel */ } + }; + ++static const struct rockchip_usb2phy_cfg rk3506_phy_cfgs[] = { ++ { ++ .reg = 0xff2b0000, ++ .num_ports = 2, ++ .phy_tuning = rk3506_usb2phy_tuning, ++ //.vbus_detect = rockchip_usb2phy_vbus_det_control, ++ //.clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x0060, 8, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0150, 2, 2, 0, 1 }, ++ .bvalid_det_st = { 0x0154, 2, 2, 0, 1 }, ++ .bvalid_det_clr = { 0x0158, 2, 2, 0, 1 }, ++ .bvalid_grf_sel = { 0x0060, 14, 14, 0, 1 }, ++ //.bvalid_grf_con = { 0x0060, 15, 14, 1, 3 }, ++ //.iddig_output = { 0x0060, 10, 10, 0, 1 }, ++ //.iddig_en = { 0x0060, 9, 9, 0, 1 }, ++ .idfall_det_en = { 0x0150, 5, 5, 0, 1 }, ++ .idfall_det_st = { 0x0154, 5, 5, 0, 1 }, ++ .idfall_det_clr = { 0x0158, 5, 5, 0, 1 }, ++ .idrise_det_en = { 0x0150, 4, 4, 0, 1 }, ++ .idrise_det_st = { 0x0154, 4, 4, 0, 1 }, ++ .idrise_det_clr = { 0x0158, 4, 4, 0, 1 }, ++ .ls_det_en = { 0x0150, 0, 0, 0, 1 }, ++ .ls_det_st = { 0x0154, 0, 0, 0, 1 }, ++ .ls_det_clr = { 0x0158, 0, 0, 0, 1 }, ++ .disfall_en = { 0x0150, 7, 7, 0, 0 }, ++ .disfall_st = { 0x0154, 7, 7, 0, 1 }, ++ .disfall_clr = { 0x0158, 7, 7, 0, 1 }, ++ .disrise_en = { 0x0150, 6, 6, 0, 0 }, ++ .disrise_st = { 0x0154, 6, 6, 0, 1 }, ++ .disrise_clr = { 0x0158, 6, 6, 0, 1 }, ++ .utmi_avalid = { 0x0118, 1, 1, 0, 1 }, ++ .utmi_bvalid = { 0x0118, 0, 0, 0, 1 }, ++ //.utmi_iddig = { 0x0118, 6, 6, 0, 1 }, ++ .utmi_ls = { 0x0118, 5, 4, 0, 1 }, ++ .utmi_hstdet = { 0x0118, 7, 7, 0, 1 }, ++ //.vbus_det_en = { 0x003c, 15, 15, 1, 0 }, ++ //.port_ls_filter_con = { 0x0160, 19, 0, 0x30100, 0x20 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x0070, 8, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0170, 2, 2, 0, 1 }, ++ .bvalid_det_st = { 0x0174, 2, 2, 0, 1 }, ++ .bvalid_det_clr = { 0x0178, 2, 2, 0, 1 }, ++ .bvalid_grf_sel = { 0x0070, 14, 14, 0, 1 }, ++ //.bvalid_grf_con = { 0x0070, 15, 14, 1, 3 }, ++ //.iddig_output = { 0x0070, 10, 10, 0, 1 }, ++ //.iddig_en = { 0x0070, 9, 9, 0, 1 }, ++ .idfall_det_en = { 0x0170, 5, 5, 0, 1 }, ++ .idfall_det_st = { 0x0174, 5, 5, 0, 1 }, ++ .idfall_det_clr = { 0x0178, 5, 5, 0, 1 }, ++ .idrise_det_en = { 0x0170, 4, 4, 0, 1 }, ++ .idrise_det_st = { 0x0174, 4, 4, 0, 1 }, ++ .idrise_det_clr = { 0x0178, 4, 4, 0, 1 }, ++ .ls_det_en = { 0x0170, 0, 0, 0, 1 }, ++ .ls_det_st = { 0x0174, 0, 0, 0, 1 }, ++ .ls_det_clr = { 0x0178, 0, 0, 0, 1 }, ++ .disfall_en = { 0x0170, 7, 7, 0, 0 }, ++ .disfall_st = { 0x0174, 7, 7, 0, 1 }, ++ .disfall_clr = { 0x0178, 7, 7, 0, 1 }, ++ .disrise_en = { 0x0170, 6, 6, 0, 0 }, ++ .disrise_st = { 0x0174, 6, 6, 0, 1 }, ++ .disrise_clr = { 0x0178, 6, 6, 0, 1 }, ++ .utmi_avalid = { 0x0118, 9, 9, 0, 1 }, ++ .utmi_bvalid = { 0x0118, 8, 8, 0, 1 }, ++ //.utmi_iddig = { 0x0118, 14, 14, 0, 1 }, ++ .utmi_ls = { 0x0118, 13, 12, 0, 1 }, ++ .utmi_hstdet = { 0x0118, 15, 15, 0, 1 }, ++ //.vbus_det_en = { 0x043c, 15, 15, 1, 0 }, ++ //.port_ls_filter_con = { 0x0180, 19, 0, 0x30100, 0x20 }, ++ } ++ }, ++ .chg_det = { ++ //.chg_mode = { 0x0060, 8, 0, 0, 0x1d7 }, ++ .cp_det = { 0x0118, 19, 19, 0, 1 }, ++ .dcp_det = { 0x0118, 18, 18, 0, 1 }, ++ .dp_det = { 0x0118, 20, 20, 0, 1 }, ++ .idm_sink_en = { 0x006c, 1, 1, 0, 1 }, ++ .idp_sink_en = { 0x006c, 0, 0, 0, 1 }, ++ .idp_src_en = { 0x006c, 2, 2, 0, 1 }, ++ .rdm_pdwn_en = { 0x006c, 3, 3, 0, 1 }, ++ .vdm_src_en = { 0x006c, 5, 5, 0, 1 }, ++ .vdp_src_en = { 0x006c, 4, 4, 0, 1 }, ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ + static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8a0000, +@@ -2426,6 +2540,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { + { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, + { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, + { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, ++ { .compatible = "rockchip,rk3506-usb2phy", .data = &rk3506_phy_cfgs }, + { .compatible = "rockchip,rk3562-usb2phy", .data = &rk3562_phy_cfgs }, + { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, + { .compatible = "rockchip,rk3576-usb2phy", .data = &rk3576_phy_cfgs }, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0038-phy-rockchip-inno-usb2-Add-PHY-tuning-for-rk3566-rk3.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0038-phy-rockchip-inno-usb2-Add-PHY-tuning-for-rk3566-rk3.patch new file mode 100644 index 000000000..59c0bf5c6 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0038-phy-rockchip-inno-usb2-Add-PHY-tuning-for-rk3566-rk3.patch @@ -0,0 +1,99 @@ +From 60b2f6a212c936c221b23c6051d89c23a8799d01 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 19 Jul 2026 16:21:40 +0200 +Subject: [PATCH 38/84] phy: rockchip-inno-usb2: Add PHY tuning for + rk3566/rk3568 + +Add a phy_tuning callback for the rk3566/rk3568 USB2 PHY and map its +analog register block so the tuning can reach it. + +The tuning enables HS transmitter pre-emphasis on the otg and host +ports, raises the otg port HS eye height to 437.5mV and sets the +bvalid/id detection filter times. Without it the otg port's HS +transmitter runs at reset defaults that are out of spec, so while host +mode works, peripheral mode fails to enumerate on the connected host. +This is visible on the PineTab2 v2.0 bottom USB-C port. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 47213f62f6eda5aca1f18251f9d4e3edd2f2712a) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 35 ++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index ffc0a7112b6e..d9d14cc8cf79 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -31,6 +31,9 @@ + #define SCHEDULE_DELAY (60 * HZ) + #define OTG_SCHEDULE_DELAY (2 * HZ) + ++/* Filter time of 10ms based on the usb2 phy grf pclk of 100MHz. */ ++#define FILTER_COUNTER 0xf4240 ++ + struct rockchip_usb2phy; + + enum rockchip_usb2phy_port_id { +@@ -1408,7 +1411,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) + } + } + +- if (of_device_is_compatible(np, "rockchip,rv1106-usb2phy")) { ++ if (of_device_is_compatible(np, "rockchip,rv1106-usb2phy") || ++ of_device_is_compatible(np, "rockchip,rk3568-usb2phy")) { + rphy->phy_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(rphy->phy_base)) + return dev_err_probe(dev, PTR_ERR(rphy->phy_base), +@@ -1623,6 +1627,33 @@ static int rk3506_usb2phy_tuning(struct rockchip_usb2phy *rphy) + return ret; + } + ++static int rk3568_usb2phy_tuning(struct rockchip_usb2phy *rphy) ++{ ++ int ret = 0; ++ ++ /* Turn off differential receiver by default to save power */ ++ phy_clear_bits(rphy->phy_base + 0x30, BIT(2)); ++ ++ /* Enable otg port pre-emphasis during non-chirp phase */ ++ phy_update_bits(rphy->phy_base, GENMASK(2, 0), 0x04); ++ ++ /* Enable host port pre-emphasis during non-chirp phase */ ++ phy_update_bits(rphy->phy_base + 0x0400, GENMASK(2, 0), 0x04); ++ ++ if (rphy->phy_cfg->reg == 0xfe8a0000) { ++ /* Set otg port HS eye height to 437.5mv (default is 400mv) */ ++ phy_update_bits(rphy->phy_base + 0x30, GENMASK(6, 4), (0x06 << 4)); ++ ++ /* Set the bvalid filter time to 10ms */ ++ ret |= regmap_write(rphy->grf, 0x0048, FILTER_COUNTER); ++ ++ /* Set the id filter time to 10ms */ ++ ret |= regmap_write(rphy->grf, 0x004c, FILTER_COUNTER); ++ } ++ ++ return ret; ++} ++ + static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy) + { + int ret; +@@ -2177,6 +2208,7 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8a0000, + .num_ports = 2, ++ .phy_tuning = rk3568_usb2phy_tuning, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { +@@ -2220,6 +2252,7 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8b0000, + .num_ports = 2, ++ .phy_tuning = rk3568_usb2phy_tuning, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0039-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0039-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch new file mode 100644 index 000000000..e12c80a97 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0039-drm-rockchip-cdn-dp-Disable-CDN-DP-on-disconnect.patch @@ -0,0 +1,28 @@ +From 6eb3bfc132cc355363c40a8f7d17ad9469c03307 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sat, 20 Nov 2021 14:35:52 +0100 +Subject: [PATCH 39/84] drm/rockchip: cdn-dp: Disable CDN DP on disconnect + +Why not? + +Signed-off-by: Ondrej Jirman +(cherry picked from commit bf7cc5c78bcfcf8017573bbd21d69f955e47276e) +--- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 177e30445ee8..13b83fe8feed 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -898,6 +898,7 @@ static void cdn_dp_pd_event_work(struct work_struct *work) + DRM_DEV_INFO(dp->dev, "Not connected; disabling cdn\n"); + dp->connected = false; + ++ cdn_dp_disable(dp); + /* Connected but not enabled, enable the block */ + } else if (!dp->active) { + DRM_DEV_INFO(dp->dev, "Connected, not enabled; enabling cdn\n"); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0040-phy-rockchip-inno-usb2-Decrease-delay-between-port-i.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0040-phy-rockchip-inno-usb2-Decrease-delay-between-port-i.patch new file mode 100644 index 000000000..5277cb47e --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0040-phy-rockchip-inno-usb2-Decrease-delay-between-port-i.patch @@ -0,0 +1,41 @@ +From 6b34febb9343481e8b855a6f540cb9353a8ab2f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Sun, 30 Jan 2022 23:44:42 +0100 +Subject: [PATCH 40/84] phy: rockchip-inno-usb2: Decrease delay between port + init and charger detection + +When used on systems with TCPM, the charger detection via USB2.0 PHY +will take too much time during boot, so the fusb302 driver will not +be able to get the result, and will set incorrect current_max on +its power supply device. + +This will lead to failure to charge when the device is powered up +while connected to non-PD charger. + +Decreasing delay of otg_sm_work from 6s to 500ms ensures that +port type detection via the PHY will finish well in time for +TCPM to be able to use its result when Rp=default is detected +via CC pins. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 41ad83ccb484296caec829ca3bc871e181a589cb) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index d9d14cc8cf79..1a01169817de 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -541,7 +541,7 @@ static int rockchip_usb2phy_init(struct phy *phy) + goto out; + + schedule_delayed_work(&rport->otg_sm_work, +- OTG_SCHEDULE_DELAY * 3); ++ msecs_to_jiffies(500)); + } else { + /* If OTG works in host only mode, do nothing. */ + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0041-phy-rockchip-inno-usb2-More-robust-charger-detection.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0041-phy-rockchip-inno-usb2-More-robust-charger-detection.patch new file mode 100644 index 000000000..c678556b5 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0041-phy-rockchip-inno-usb2-More-robust-charger-detection.patch @@ -0,0 +1,115 @@ +From e42f271b4064adc4f2bd77d27842da5ce29712eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +Date: Fri, 4 Feb 2022 02:25:34 +0100 +Subject: [PATCH 41/84] phy: rockchip-inno-usb2: More robust charger detection + extcon updates + +Make sure we always clear the charger detection results. Allow to +disable updates of EXTCON_USB state via extcon,ignore-usb DT property. +The previous method was smart, but assumed !vbus_branch would +always be called. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 3b27a37fbd058db8a780e2ad364728a4d4e5fd1a) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 39 +++++++++++-------- + 1 file changed, 23 insertions(+), 16 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 1a01169817de..ef9f7a45c122 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -661,21 +661,27 @@ static const struct phy_ops rockchip_usb2phy_ops = { + .owner = THIS_MODULE, + }; + ++const int rockchip_usb2phy_cable_types[] = { ++ EXTCON_CHG_USB_SDP, ++ EXTCON_CHG_USB_DCP, ++ EXTCON_CHG_USB_CDP, ++ EXTCON_CHG_USB_ACA, ++}; ++ + static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + { + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, + otg_sm_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- static unsigned int cable; ++ unsigned int cable = 0, i; + unsigned long delay; +- bool vbus_attach, sch_work, notify_charger; ++ bool vbus_attach, sch_work; + + vbus_attach = property_enabled(rphy->grf, + &rport->port_cfg->utmi_bvalid); + + sch_work = false; +- notify_charger = false; + delay = OTG_SCHEDULE_DELAY; + dev_dbg(&rport->phy->dev, "%s otg sm work\n", + usb_otg_state_string(rport->state)); +@@ -704,14 +710,12 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; +- notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_SDP; + break; + case POWER_SUPPLY_TYPE_USB_DCP: + dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); + rockchip_usb2phy_power_off(rport->phy); +- notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_DCP; + break; +@@ -719,7 +723,6 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; +- notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_CDP; + break; +@@ -731,22 +734,26 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + break; + } + } else { +- notify_charger = true; + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + } + +- if (rport->vbus_attached != vbus_attach) { +- rport->vbus_attached = vbus_attach; ++ if (rphy->edev && extcon_get_state(rphy->edev, cable) != vbus_attach) { ++ for (i = 0; i < ARRAY_SIZE(rockchip_usb2phy_cable_types); i++) { ++ int type = rockchip_usb2phy_cable_types[i]; + +- if (notify_charger && rphy->edev) { +- extcon_set_state_sync(rphy->edev, +- cable, vbus_attach); +- if (cable == EXTCON_CHG_USB_SDP) +- extcon_set_state_sync(rphy->edev, +- EXTCON_USB, +- vbus_attach); ++ if (extcon_get_state(rphy->edev, type)) ++ extcon_set_state_sync(rphy->edev, type, 0); + } ++ ++ if (vbus_attach) ++ extcon_set_state_sync(rphy->edev, cable, vbus_attach); ++ ++ if ((cable == EXTCON_CHG_USB_SDP || cable == POWER_SUPPLY_TYPE_USB_CDP) ++ && extcon_get_state(rphy->edev, EXTCON_USB) != vbus_attach ++ && !of_property_read_bool(rphy->dev->of_node, "extcon,ignore-usb")) ++ extcon_set_state_sync(rphy->edev, ++ EXTCON_USB, vbus_attach); + } + break; + case OTG_STATE_B_PERIPHERAL: +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0042-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0042-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch new file mode 100644 index 000000000..54c78cc95 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0042-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch @@ -0,0 +1,73 @@ +From ce3115635dc842403bad5d6287a765802741ae74 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 17:46:22 +0200 +Subject: [PATCH 42/84] usb: dwc3: Track the power state of usb3_generic_phy + +The USB3 PHYs will need to be powered off and on again from within the +set_mode work, independently of the rest of the controller. There is no +way to recover from a failure there, so track whether the PHYs are +actually powered to keep the phy_power_on()/phy_power_off() calls +balanced. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit bc1f2513d49d2a23ac923380f72a5ca2729122e8) +--- + drivers/usb/dwc3/core.c | 10 ++++++++-- + drivers/usb/dwc3/core.h | 6 ++++++ + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 9d4293f02c9a..9c6bd173798c 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -919,6 +919,8 @@ static int dwc3_phy_power_on(struct dwc3 *dwc) + goto err_power_off_usb3_phy; + } + ++ dwc->usb3_phy_powered = true; ++ + return 0; + + err_power_off_usb3_phy: +@@ -939,8 +941,12 @@ static void dwc3_phy_power_off(struct dwc3 *dwc) + { + int i; + +- for (i = 0; i < dwc->num_usb3_ports; i++) +- phy_power_off(dwc->usb3_generic_phy[i]); ++ if (dwc->usb3_phy_powered) { ++ for (i = 0; i < dwc->num_usb3_ports; i++) ++ phy_power_off(dwc->usb3_generic_phy[i]); ++ ++ dwc->usb3_phy_powered = false; ++ } + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_power_off(dwc->usb2_generic_phy[i]); +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +index e0dee9d28740..19a93c3dd668 100644 +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1067,6 +1067,10 @@ struct dwc3_glue_ops { + * @usb3_generic_phy: pointer to array of USB3 PHYs + * @num_usb2_ports: number of USB2 ports + * @num_usb3_ports: number of USB3 ports ++ * @usb3_phy_powered: flag to indicate that the USB3 PHYs are powered on. The ++ * USB3 PHYs may be powered off independently of the rest of ++ * the controller, so their state has to be tracked to keep ++ * the phy_power_on()/phy_power_off() calls balanced. + * @phys_ready: flag to indicate that PHYs are ready + * @ulpi: pointer to ulpi interface + * @ulpi_ready: flag to indicate that ULPI is initialized +@@ -1233,6 +1237,8 @@ struct dwc3 { + u8 num_usb2_ports; + u8 num_usb3_ports; + ++ bool usb3_phy_powered; ++ + bool phys_ready; + + struct ulpi *ulpi; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0043-usb-dwc3-Always-disable-SUSPHY-when-entering-a-role.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0043-usb-dwc3-Always-disable-SUSPHY-when-entering-a-role.patch new file mode 100644 index 000000000..70e3f2860 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0043-usb-dwc3-Always-disable-SUSPHY-when-entering-a-role.patch @@ -0,0 +1,47 @@ +From c11a7d364f9ae037e9c0f9e13ec9eefcd9f526cf Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 22:09:48 +0200 +Subject: [PATCH 43/84] usb: dwc3: Always disable SUSPHY when entering a role + +__dwc3_set_mode() leaves the old role, which enables SUSPHY, and then +re-initializes the new one, which requires SUSPHY to be disabled. The +only thing disabling it again is dwc3_set_prtcap(), and only when the +value already in GCTL.PrtCapDir differs from the one being set. + +That is not the same question as whether a role transition took place. +At probe, current_dr_role is 0 while GCTL.PrtCapDir may already hold the +role being switched to, so the transition runs with SUSPHY left enabled. + +Disable it in __dwc3_set_mode() directly, so entering a role does not +depend on what the register happened to hold. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 01e90addf2c0380dc29c550fa565e71127217c18) +--- + drivers/usb/dwc3/core.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 9c6bd173798c..86ddf91ae454 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -235,7 +235,15 @@ static void __dwc3_set_mode(struct work_struct *work) + + spin_lock_irqsave(&dwc->lock, flags); + +- dwc3_set_prtcap(dwc, desired_dr_role, false); ++ /* ++ * Leaving the old role enabled SUSPHY, and re-initializing the new one ++ * requires it to be disabled. dwc3_set_prtcap() only does that when the ++ * value already in GCTL.PrtCapDir differs from the one being set, which ++ * is not the same question, so do it here and tell dwc3_set_prtcap() to ++ * keep its hands off. ++ */ ++ dwc3_enable_susphy(dwc, false); ++ dwc3_set_prtcap(dwc, desired_dr_role, true); + + spin_unlock_irqrestore(&dwc->lock, flags); + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0044-usb-dwc3-Track-cable-connection-state-separately-fro.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0044-usb-dwc3-Track-cable-connection-state-separately-fro.patch new file mode 100644 index 000000000..88aacabcd --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0044-usb-dwc3-Track-cable-connection-state-separately-fro.patch @@ -0,0 +1,143 @@ +From 23e9a265cd549dbd99cf1109ad706f8a25337e42 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 17:48:45 +0200 +Subject: [PATCH 44/84] usb: dwc3: Track cable connection state separately from + dr_role + +A Type-C port can be unplugged and replugged into the same kind of port +without the DRD role ever changing. __dwc3_set_mode() bails out early +when the desired role equals the current one, so such a transition is +invisible to the driver, even though it may need to act on it. + +Track connectedness in its own pair of flags rather than encoding it in +current_dr_role. current_dr_role holds a GCTL.PrtCapDir register value +and is compared against DWC3_GCTL_PRTCAP_* throughout the driver, in the +glue drivers, in debugfs and in tracing, so giving it an out-of-range +value would mean auditing every one of those users. + +dwc3_set_mode() keeps its meaning and implies "connected", so existing +callers are unaffected. Only callers that can actually distinguish a +cable from no cable use dwc3_set_mode_connected(). + +No functional change. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit e862414522584a466af62c055fe7e17a504c9bcd) +--- + drivers/usb/dwc3/core.c | 20 ++++++++++++++++++-- + drivers/usb/dwc3/core.h | 9 +++++++++ + 2 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 86ddf91ae454..a4416759fac8 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -169,11 +169,13 @@ static void __dwc3_set_mode(struct work_struct *work) + int ret; + u32 reg; + u32 desired_dr_role; ++ bool desired_dr_connected; + int i; + + mutex_lock(&dwc->mutex); + spin_lock_irqsave(&dwc->lock, flags); + desired_dr_role = dwc->desired_dr_role; ++ desired_dr_connected = dwc->desired_dr_connected; + spin_unlock_irqrestore(&dwc->lock, flags); + + pm_runtime_get_sync(dwc->dev); +@@ -184,7 +186,8 @@ static void __dwc3_set_mode(struct work_struct *work) + if (!desired_dr_role) + goto out; + +- if (desired_dr_role == dwc->current_dr_role) ++ if (desired_dr_role == dwc->current_dr_role && ++ desired_dr_connected == dwc->current_dr_connected) + goto out; + + if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev) +@@ -244,6 +247,7 @@ static void __dwc3_set_mode(struct work_struct *work) + */ + dwc3_enable_susphy(dwc, false); + dwc3_set_prtcap(dwc, desired_dr_role, true); ++ dwc->current_dr_connected = desired_dr_connected; + + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -295,7 +299,7 @@ static void __dwc3_set_mode(struct work_struct *work) + mutex_unlock(&dwc->mutex); + } + +-void dwc3_set_mode(struct dwc3 *dwc, u32 mode) ++void dwc3_set_mode_connected(struct dwc3 *dwc, u32 mode, bool connected) + { + unsigned long flags; + +@@ -304,11 +308,17 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) + + spin_lock_irqsave(&dwc->lock, flags); + dwc->desired_dr_role = mode; ++ dwc->desired_dr_connected = connected; + spin_unlock_irqrestore(&dwc->lock, flags); + + queue_work(system_freezable_wq, &dwc->drd_work); + } + ++void dwc3_set_mode(struct dwc3 *dwc, u32 mode) ++{ ++ dwc3_set_mode_connected(dwc, mode, true); ++} ++ + u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) + { + struct dwc3 *dwc = dep->dwc; +@@ -1669,6 +1679,12 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) + break; + case USB_DR_MODE_OTG: + INIT_WORK(&dwc->drd_work, __dwc3_set_mode); ++ /* ++ * Default to "connected", which is what dwc3_set_mode() implies. ++ * Only users that can actually tell a cable apart from no cable ++ * ever clear this, via dwc3_set_mode_connected(). ++ */ ++ dwc->current_dr_connected = true; + ret = dwc3_drd_init(dwc); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize dual-role\n"); +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +index 19a93c3dd668..03fd08d0dca1 100644 +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1050,6 +1050,12 @@ struct dwc3_glue_ops { + * @dr_mode: requested mode of operation + * @current_dr_role: current role of operation when in dual-role mode + * @desired_dr_role: desired role of operation when in dual-role mode ++ * @current_dr_connected: set if a cable is currently known to be connected ++ * @desired_dr_connected: set if a cable is desired to be considered connected. ++ * Tracked separately from @desired_dr_role because the ++ * role does not change when a cable is unplugged from ++ * and replugged into the same kind of port, yet the ++ * transition still has to be acted upon. + * @edev: extcon handle + * @edev_nb: extcon notifier + * @hsphy_mode: UTMI phy mode, one of following: +@@ -1250,6 +1256,8 @@ struct dwc3 { + enum usb_dr_mode dr_mode; + u32 current_dr_role; + u32 desired_dr_role; ++ bool current_dr_connected; ++ bool desired_dr_connected; + struct extcon_dev *edev; + struct notifier_block edev_nb; + enum usb_phy_interface hsphy_mode; +@@ -1596,6 +1604,7 @@ struct dwc3_gadget_ep_cmd_params { + /* prototypes */ + void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy); + void dwc3_set_mode(struct dwc3 *dwc, u32 mode); ++void dwc3_set_mode_connected(struct dwc3 *dwc, u32 mode, bool connected); + u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); + + #define DWC3_IP_IS(_ip) \ +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0045-usb-dwc3-Power-cycle-USB3-PHY-on-role-connection-cha.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0045-usb-dwc3-Power-cycle-USB3-PHY-on-role-connection-cha.patch new file mode 100644 index 000000000..6a8e67f6b --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0045-usb-dwc3-Power-cycle-USB3-PHY-on-role-connection-cha.patch @@ -0,0 +1,311 @@ +From e69fe48a25b340f8f46e8144a76a04e80fb1d4fc Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 17:52:42 +0200 +Subject: [PATCH 45/84] usb: dwc3: Power cycle USB3 PHY on role/connection + change + +The RK3399 Type-C PHY latches the plug orientation while it is being +powered on, and per the TRM this may only happen while the USB3 OTG +controller is held in reset, to keep the pipe power state at P2. The PHY +driver is passive: it has no extcon notifier and only reprograms the +orientation on power_on after a preceding power_off. So it can not +reconfigure itself and needs the controller driver to power cycle it. + +Do the power cycle in __dwc3_set_mode(), between asserting and +deasserting GCTL.CoreSoftReset, which is exactly the window the PHY +needs. Leave the PHY powered off while no cable is connected, both to +save power and so that the orientation is picked up at plug-in. + +The connection state is what makes this work for the plugout/plugin +cycle into the same kind of port, where the role never changes. + +For extcon, EXTCON_USB has to be watched besides EXTCON_USB_HOST to tell +a disconnect apart from device mode. The notifier for all cable types is +not told which cable changed, so subscribe to all of them, re-read the +state, and drop the updates that change neither the role nor the +connection state. Boards that do not enable the quirk never look at +EXTCON_USB and so keep behaving exactly as before. + +For usb_role_switch, USB_ROLE_NONE keeps falling back to +role_switch_default_mode as before, but is now remembered as +disconnected, and is reported back as USB_ROLE_NONE. + +While no cable is connected the USB3 PHY is powered off, so there is +nothing to run on top of it. Skip the role init in that case, and skip +the matching teardown for a role that was entered while disconnected. +Otherwise the gadget is brought up against a powered off PHY, which +fails to enable ep0out and then takes seconds to tear down again, all +of it under dwc->mutex. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 1579c94e43edea5ade9beb012a224d7a68e10774) +--- + drivers/usb/dwc3/core.c | 51 ++++++++++++++++++++++++-- + drivers/usb/dwc3/core.h | 7 ++++ + drivers/usb/dwc3/drd.c | 79 +++++++++++++++++++++++++++++++---------- + 3 files changed, 116 insertions(+), 21 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index a4416759fac8..21554e8d3ffa 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -193,7 +193,11 @@ static void __dwc3_set_mode(struct work_struct *work) + if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev) + goto out; + +- switch (dwc->current_dr_role) { ++ /* ++ * Nothing was brought up for a role entered while no cable was ++ * connected, so there is nothing to tear down either. ++ */ ++ switch (dwc->current_dr_connected ? dwc->current_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; +@@ -217,12 +221,45 @@ static void __dwc3_set_mode(struct work_struct *work) + * Only perform GCTL.CoreSoftReset when there's DRD role switching. + */ + if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) || +- DWC3_VER_IS_PRIOR(DWC31, 190A)) && ++ DWC3_VER_IS_PRIOR(DWC31, 190A) || ++ dwc->usb3_phy_power_cycle) && + desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) { + reg = dwc3_readl(dwc, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc, DWC3_GCTL, reg); + ++ /* ++ * The USB3 PHY may only be reconfigured while the controller is ++ * held in reset, so do it here, between asserting and ++ * deasserting GCTL.CoreSoftReset. ++ * ++ * The PHY is left powered off while no cable is connected, both ++ * to save power and so that it picks up the current Type-C plug ++ * orientation when it is powered on again at plug-in. ++ */ ++ if (dwc->usb3_phy_power_cycle) { ++ if (dwc->usb3_phy_powered) { ++ for (i = 0; i < dwc->num_usb3_ports; i++) ++ phy_power_off(dwc->usb3_generic_phy[i]); ++ ++ dwc->usb3_phy_powered = false; ++ } ++ ++ if (desired_dr_connected) { ++ for (i = 0; i < dwc->num_usb3_ports; i++) { ++ ret = phy_power_on(dwc->usb3_generic_phy[i]); ++ if (ret < 0) { ++ dev_err(dwc->dev, "failed to power on usb3 phy %d\n", i); ++ while (--i >= 0) ++ phy_power_off(dwc->usb3_generic_phy[i]); ++ break; ++ } ++ } ++ ++ dwc->usb3_phy_powered = i == dwc->num_usb3_ports; ++ } ++ } ++ + /* + * Wait for internal clocks to synchronized. DWC_usb31 and + * DWC_usb32 may need at least 50ms (less for DWC_usb3). To +@@ -251,7 +288,12 @@ static void __dwc3_set_mode(struct work_struct *work) + + spin_unlock_irqrestore(&dwc->lock, flags); + +- switch (desired_dr_role) { ++ /* ++ * With no cable connected the USB3 PHY is powered off, so there is ++ * nothing to bring up on top of it. Leave the controller idle until a ++ * cable shows up. ++ */ ++ switch (desired_dr_connected ? desired_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: + ret = dwc3_host_init(dwc); + if (ret) { +@@ -1908,6 +1950,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) + dwc->dis_split_quirk = device_property_read_bool(dev, + "snps,dis-split-quirk"); + ++ dwc->usb3_phy_power_cycle = device_property_read_bool(dev, ++ "usb3-phy-power-cycle-on-role-change"); ++ + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +index 03fd08d0dca1..b0cae898d7fe 100644 +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1174,6 +1174,12 @@ struct dwc3_glue_ops { + * 3 - Reserved + * @dis_metastability_quirk: set to disable metastability quirk. + * @dis_split_quirk: set to disable split boundary. ++ * @usb3_phy_power_cycle: set if the USB3 PHY has to be power cycled whenever ++ * the role or the cable connection state changes, and ++ * left powered off while no cable is connected. Needed ++ * on platforms whose Type-C PHY can only pick up the ++ * plug orientation while it is being powered on, and ++ * only while the controller is held in reset. + * @enable_usb2_transceiver_delay: Set to insert a delay before the + * assertion of the TxValid signal during a HS Chirp. + * @sys_wakeup: set if the device may do system wakeup. +@@ -1423,6 +1429,7 @@ struct dwc3 { + unsigned dis_metastability_quirk:1; + + unsigned dis_split_quirk:1; ++ unsigned usb3_phy_power_cycle:1; + unsigned enable_usb2_transceiver_delay:1; + unsigned async_callbacks:1; + unsigned sys_wakeup:1; +diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c +index f3e37d383627..fa622d9abfcd 100644 +--- a/drivers/usb/dwc3/drd.c ++++ b/drivers/usb/dwc3/drd.c +@@ -419,28 +419,56 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) + + static void dwc3_drd_update(struct dwc3 *dwc) + { ++ unsigned long flags; ++ bool connected = true; ++ bool changed; ++ u32 mode; + int id; + +- if (dwc->edev) { +- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); +- if (id < 0) +- id = 0; +- dwc3_set_mode(dwc, id ? +- DWC3_GCTL_PRTCAP_HOST : +- DWC3_GCTL_PRTCAP_DEVICE); +- } ++ if (!dwc->edev) ++ return; ++ ++ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); ++ if (id < 0) ++ id = 0; ++ ++ mode = id ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE; ++ ++ /* ++ * EXTCON_USB is what tells a device mode cable apart from no cable at ++ * all. Boards that do not need the distinction ignore it and keep ++ * assuming a cable is always connected, as before. ++ */ ++ if (dwc->usb3_phy_power_cycle) ++ connected = id || extcon_get_state(dwc->edev, EXTCON_USB) > 0; ++ ++ /* ++ * All cable types are subscribed to, but only the ones read above make ++ * any difference here. Drop the rest rather than queueing work that ++ * would only find that there is nothing to do. ++ */ ++ spin_lock_irqsave(&dwc->lock, flags); ++ changed = mode != dwc->desired_dr_role || ++ connected != dwc->desired_dr_connected; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ if (!changed) ++ return; ++ ++ dwc3_pre_set_role(dwc, id ? USB_ROLE_HOST : USB_ROLE_DEVICE); ++ dwc3_set_mode_connected(dwc, mode, connected); + } + + static int dwc3_drd_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) + { + struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); +- u32 mode = event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE; +- enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ? +- USB_ROLE_HOST : USB_ROLE_DEVICE; + +- dwc3_pre_set_role(dwc, role); +- dwc3_set_mode(dwc, mode); ++ /* ++ * The notifier for all cable types is not told which one changed, so ++ * the state has to be re-read rather than derived from @event. ++ */ ++ dwc3_drd_update(dwc); + + return NOTIFY_DONE; + } +@@ -451,6 +479,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, + enum usb_role role) + { + struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); ++ bool connected = true; + u32 mode; + + switch (role) { +@@ -461,6 +490,11 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, + mode = DWC3_GCTL_PRTCAP_DEVICE; + break; + default: ++ /* ++ * Keep falling back to role_switch_default_mode, but remember ++ * that nothing is actually connected. ++ */ ++ connected = !dwc->usb3_phy_power_cycle; + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) + mode = DWC3_GCTL_PRTCAP_HOST; + else +@@ -469,7 +503,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, + } + + dwc3_pre_set_role(dwc, role); +- dwc3_set_mode(dwc, mode); ++ dwc3_set_mode_connected(dwc, mode, connected); + return 0; + } + +@@ -480,6 +514,11 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) + enum usb_role role; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (!dwc->current_dr_connected) { ++ role = USB_ROLE_NONE; ++ goto unlock; ++ } ++ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + role = USB_ROLE_HOST; +@@ -497,6 +536,7 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) + role = USB_ROLE_DEVICE; + break; + } ++unlock: + spin_unlock_irqrestore(&dwc->lock, flags); + return role; + } +@@ -553,8 +593,12 @@ int dwc3_drd_init(struct dwc3 *dwc) + + if (dwc->edev) { + dwc->edev_nb.notifier_call = dwc3_drd_notifier; +- ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, +- &dwc->edev_nb); ++ /* ++ * EXTCON_USB has to be watched besides EXTCON_USB_HOST to tell ++ * a disconnect apart from device mode, so subscribe to all ++ * cable types and let dwc3_drd_update() ignore the rest. ++ */ ++ ret = extcon_register_notifier_all(dwc->edev, &dwc->edev_nb); + if (ret < 0) { + dev_err(dwc->dev, "couldn't register cable notifier\n"); + return ret; +@@ -601,8 +645,7 @@ void dwc3_drd_exit(struct dwc3 *dwc) + usb_role_switch_unregister(dwc->role_sw); + + if (dwc->edev) +- extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, +- &dwc->edev_nb); ++ extcon_unregister_notifier_all(dwc->edev, &dwc->edev_nb); + + cancel_work_sync(&dwc->drd_work); + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0046-usb-dwc3-Register-the-role-even-when-no-cable-is-con.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0046-usb-dwc3-Register-the-role-even-when-no-cable-is-con.patch new file mode 100644 index 000000000..22a7bc892 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0046-usb-dwc3-Register-the-role-even-when-no-cable-is-con.patch @@ -0,0 +1,69 @@ +From 364a17c0e659ea1a6a2727bdbf9936218b7d0583 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 22:42:25 +0200 +Subject: [PATCH 46/84] usb: dwc3: Register the role even when no cable is + connected + +Gating the role init on the connection state went too far. Entering a +role is what registers the gadget or the host, and for the gadget that +means registering the UDC. With nothing plugged in at boot the UDC was +never registered at all, so userspace could not bind a gadget driver to +it, and by the time a cable showed up and the UDC appeared there was +nothing left pending to bind. + +Only skip the role init for a connection change within a role that is +already entered. That still keeps the gadget from being brought up on +top of a powered off USB3 PHY, which is what the gating was for. + +Fixup for "usb: dwc3: Power cycle USB3 PHY on role/connection change". + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 790a415b4c5d14ae379946381f8ff40ba7c23a1f) +--- + drivers/usb/dwc3/core.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 21554e8d3ffa..0e20e70b9163 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -170,6 +170,7 @@ static void __dwc3_set_mode(struct work_struct *work) + u32 reg; + u32 desired_dr_role; + bool desired_dr_connected; ++ bool role_changed; + int i; + + mutex_lock(&dwc->mutex); +@@ -193,6 +194,8 @@ static void __dwc3_set_mode(struct work_struct *work) + if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev) + goto out; + ++ role_changed = desired_dr_role != dwc->current_dr_role; ++ + /* + * Nothing was brought up for a role entered while no cable was + * connected, so there is nothing to tear down either. +@@ -289,11 +292,15 @@ static void __dwc3_set_mode(struct work_struct *work) + spin_unlock_irqrestore(&dwc->lock, flags); + + /* +- * With no cable connected the USB3 PHY is powered off, so there is +- * nothing to bring up on top of it. Leave the controller idle until a +- * cable shows up. ++ * Entering a role registers the gadget or the host, which has to happen ++ * even with no cable connected so that userspace can bind a gadget ++ * driver to the UDC before anything is plugged in. ++ * ++ * A connection change within the same role is different: the USB3 PHY ++ * has just been powered off, so there is nothing to bring up on top of ++ * it, and the role is already entered anyway. + */ +- switch (desired_dr_connected ? desired_dr_role : 0) { ++ switch (desired_dr_connected || role_changed ? desired_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: + ret = dwc3_host_init(dwc); + if (ret) { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0047-phy-rockchip-inno-usb2-Set-up-charger-detection-mode.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0047-phy-rockchip-inno-usb2-Set-up-charger-detection-mode.patch new file mode 100644 index 000000000..6c788cfdb --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0047-phy-rockchip-inno-usb2-Set-up-charger-detection-mode.patch @@ -0,0 +1,189 @@ +From 45981a4220fe965b0dabd39870943e3da097e26c Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 22:59:13 +0200 +Subject: [PATCH 47/84] phy: rockchip-inno-usb2: Set up charger detection mode + on RK3399 + +BC1.2 detection has to run with the transceiver held off the bus, or the +data contact detection stage cannot see the host's D+ pull-down. On a +PinePhone Pro with a gadget driver bound, DCD reads dp_det = 0 for all +six polls, detection times out and reports a floating charger, and +rockchip_usb2phy_otg_sm_work() powers the USB2 PHY off in response. +USB 2.0 then does not enumerate at all: the host gets -32 on the +descriptor read, then -71, then gives up. Unbind the gadget and the same +cable is correctly detected as SDP on the first poll. + +The driver models 0xe454 as two overlapping narrow fields, phy_sus over +bits 1:0 and chg_det.opmode over bits 3:0, and only ever writes opmode +while VBUS is absent. Charger detection only runs while VBUS is present, +so on RK3399 opmode is never written at all and non-driving mode is +never entered. + +Rockchip's own driver describes the same register as one nine bit field +and writes 0x1d7 to it for the duration of detection, which per its +comment selects normal mode, non-driving opmode, full speed xcvrselect +and termselect, and the DP/DM pull-downs. Do the same, and save and +restore exactly the bits being overwritten so nothing has to be assumed +about what the rest of them mean. + +SoCs without a chg_mode field keep poking opmode as before, including +the VBUS condition from commit 7d8f725b79e3 ("phy: rockchip: inno-usb2: +fix communication disruption in gadget mode"), so they are unaffected. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit b92d132cafcf20c5cf7413f14bae79bd44e3ca84) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 81 +++++++++++++++++-- + 1 file changed, 73 insertions(+), 8 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index ef9f7a45c122..a138417f0b2e 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -100,6 +100,8 @@ struct usb2phy_reg { + * @vdm_src_en: open dm voltage source. + * @vdp_src_en: open dp voltage source. + * @opmode: utmi operational mode. ++ * @chg_mode: charger detection mode, covering everything that has to be set ++ * up for BC1.2 at once. Takes precedence over @opmode. + */ + struct rockchip_chg_det_reg { + struct usb2phy_reg cp_det; +@@ -112,6 +114,7 @@ struct rockchip_chg_det_reg { + struct usb2phy_reg vdm_src_en; + struct usb2phy_reg vdp_src_en; + struct usb2phy_reg opmode; ++ struct usb2phy_reg chg_mode; + }; + + /** +@@ -246,6 +249,8 @@ struct rockchip_usb2phy_port { + * @irq: muxed interrupt for single irq configuration + * @phy_cfg: phy register configuration, assigned by driver data. + * @ports: phy port instance. ++ * @chg_mode_cfg: saved contents of the charger detection mode field, so that ++ * it can be restored once detection is done. + */ + struct rockchip_usb2phy { + struct device *dev; +@@ -260,6 +265,7 @@ struct rockchip_usb2phy { + enum usb_chg_state chg_state; + enum power_supply_type chg_type; + u8 dcd_retries; ++ unsigned int chg_mode_cfg; + struct extcon_dev *edev; + int irq; + const struct rockchip_usb2phy_cfg *phy_cfg; +@@ -823,6 +829,68 @@ static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, + property_enable(base, &rphy->phy_cfg->chg_det.idp_sink_en, en); + } + ++/* ++ * BC1.2 detection needs the transceiver held off the bus: utmi_opmode in ++ * non-driving mode, xcvrselect and termselect at full speed, and the DP/DM ++ * pull-down resistors enabled. On SoCs where that is described as a single ++ * chg_mode field, set it up in one go and put the register back afterwards. ++ * ++ * Otherwise only utmi_opmode can be poked, and only while nothing is ++ * connected: doing it with a cable attached kills an active gadget, see ++ * commit 7d8f725b79e3 ("phy: rockchip: inno-usb2: fix communication ++ * disruption in gadget mode"). That leaves detection running with the ++ * transceiver driving, which is unreliable, but it is not a regression. ++ */ ++static bool rockchip_chg_has_chg_mode(struct rockchip_usb2phy *rphy) ++{ ++ /* An unpopulated usb2phy_reg leaves nothing to write. */ ++ return rphy->phy_cfg->chg_det.chg_mode.enable != 0; ++} ++ ++static void rockchip_chg_enter_mode(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport, ++ bool vbus_attach) ++{ ++ const struct usb2phy_reg *chg_mode = &rphy->phy_cfg->chg_det.chg_mode; ++ struct regmap *base = get_reg_base(rphy); ++ ++ if (!rockchip_chg_has_chg_mode(rphy)) { ++ if (!vbus_attach) ++ property_enable(base, &rphy->phy_cfg->chg_det.opmode, ++ false); ++ return; ++ } ++ ++ /* ++ * Save exactly the bits that are about to be overwritten, so they can ++ * be put back afterwards without having to know what they mean. ++ */ ++ regmap_read(base, chg_mode->offset, &rphy->chg_mode_cfg); ++ rphy->chg_mode_cfg &= GENMASK(chg_mode->bitend, chg_mode->bitstart); ++ ++ property_enable(base, chg_mode, true); ++} ++ ++static void rockchip_chg_leave_mode(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport, ++ bool vbus_attach) ++{ ++ const struct usb2phy_reg *chg_mode = &rphy->phy_cfg->chg_det.chg_mode; ++ struct regmap *base = get_reg_base(rphy); ++ unsigned int mask; ++ ++ if (!rockchip_chg_has_chg_mode(rphy)) { ++ if (!vbus_attach) ++ property_enable(base, &rphy->phy_cfg->chg_det.opmode, ++ true); ++ return; ++ } ++ ++ mask = GENMASK(chg_mode->bitend, chg_mode->bitstart); ++ regmap_write(base, chg_mode->offset, ++ rphy->chg_mode_cfg | (mask << BIT_WRITEABLE_SHIFT)); ++} ++ + #define CHG_DCD_POLL_TIME (100 * HZ / 1000) + #define CHG_DCD_MAX_RETRIES 6 + #define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) +@@ -832,7 +900,6 @@ static void rockchip_chg_detect_work(struct work_struct *work) + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, chg_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- struct regmap *base = get_reg_base(rphy); + bool is_dcd, tmout, vout, vbus_attach; + unsigned long delay; + +@@ -844,9 +911,8 @@ static void rockchip_chg_detect_work(struct work_struct *work) + case USB_CHG_STATE_UNDEFINED: + if (!rport->suspended && !vbus_attach) + rockchip_usb2phy_power_off(rport->phy); +- /* put the controller in non-driving mode */ +- if (!vbus_attach) +- property_enable(base, &rphy->phy_cfg->chg_det.opmode, false); ++ /* put the controller in charger detection mode */ ++ rockchip_chg_enter_mode(rphy, rport, vbus_attach); + /* Start DCD processing stage 1 */ + rockchip_chg_enable_dcd(rphy, true); + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; +@@ -908,9 +974,8 @@ static void rockchip_chg_detect_work(struct work_struct *work) + rphy->chg_state = USB_CHG_STATE_DETECTED; + fallthrough; + case USB_CHG_STATE_DETECTED: +- /* put the controller in normal mode */ +- if (!vbus_attach) +- property_enable(base, &rphy->phy_cfg->chg_det.opmode, true); ++ /* put the controller back in normal mode */ ++ rockchip_chg_leave_mode(rphy, rport, vbus_attach); + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + dev_dbg(&rport->phy->dev, "charger = %s\n", + chg_to_string(rphy->chg_type)); +@@ -2029,7 +2094,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { + } + }, + .chg_det = { +- .opmode = { 0xe454, 3, 0, 5, 1 }, ++ .chg_mode = { 0xe454, 8, 0, 0, 0x1d7 }, + .cp_det = { 0xe2ac, 2, 2, 0, 1 }, + .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, + .dp_det = { 0xe2ac, 0, 0, 0, 1 }, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0048-usb-dwc3-Do-not-re-register-the-gadget-on-cable-even.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0048-usb-dwc3-Do-not-re-register-the-gadget-on-cable-even.patch new file mode 100644 index 000000000..6bf86173c --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0048-usb-dwc3-Do-not-re-register-the-gadget-on-cable-even.patch @@ -0,0 +1,106 @@ +From 9d5233641333d58478d65d40101e36fdf2c8bbac Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 23:15:39 +0200 +Subject: [PATCH 48/84] usb: dwc3: Do not re-register the gadget on cable + events + +The role init and exit ended up with different conditions: init ran for +a role change or a connected cable, exit only for a role that was +entered while connected. After a boot with nothing plugged in the role +is entered but marked disconnected, so the next plug took the init path +again without ever having taken the exit path, and the gadget was added +twice: + + kobject: kobject_add_internal failed for fe800000.usb with -EEXIST + dwc3 fe800000.usb: failed to add gadget + +Entering and leaving a role is what registers and unregisters the gadget +or the host, so drive it purely from the role, as upstream does. A cable +coming or going does not change the role, so park the controller instead +the way the runtime PM path already does: soft disconnect and drop the +event buffers before the reset window, then soft reset, restore the +event buffers and soft connect after it. + +The UDC is now registered exactly once, at probe, and stays registered. +Only its pullup follows the cable. That also stops usb_del_gadget() and +usb_add_gadget() from running on every plug and unplug, which was taking +seconds at a time under dwc->mutex. + +Fixup for "usb: dwc3: Register the role even when no cable is +connected". + +Signed-off-by: Ondrej Jirman +(cherry picked from commit d0b96cd58562bb50012a08ab1bd44389abfd8c28) +--- + drivers/usb/dwc3/core.c | 40 +++++++++++++++++++++++++++------------- + 1 file changed, 27 insertions(+), 13 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 0e20e70b9163..18d50856d3b6 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -197,10 +197,19 @@ static void __dwc3_set_mode(struct work_struct *work) + role_changed = desired_dr_role != dwc->current_dr_role; + + /* +- * Nothing was brought up for a role entered while no cable was +- * connected, so there is nothing to tear down either. ++ * Entering and leaving a role registers and unregisters the gadget or ++ * the host, so it has to follow the role and nothing else. A cable ++ * coming or going does not change the role, and must not take the UDC ++ * away from userspace: the controller is parked below instead, the way ++ * the runtime PM path does it. + */ +- switch (dwc->current_dr_connected ? dwc->current_dr_role : 0) { ++ if (!role_changed && dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE && ++ dwc->current_dr_connected) { ++ dwc3_gadget_suspend(dwc); ++ dwc3_event_buffers_cleanup(dwc); ++ } ++ ++ switch (role_changed ? dwc->current_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; +@@ -291,16 +300,7 @@ static void __dwc3_set_mode(struct work_struct *work) + + spin_unlock_irqrestore(&dwc->lock, flags); + +- /* +- * Entering a role registers the gadget or the host, which has to happen +- * even with no cable connected so that userspace can bind a gadget +- * driver to the UDC before anything is plugged in. +- * +- * A connection change within the same role is different: the USB3 PHY +- * has just been powered off, so there is nothing to bring up on top of +- * it, and the role is already entered anyway. +- */ +- switch (desired_dr_connected || role_changed ? desired_dr_role : 0) { ++ switch (role_changed ? desired_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: + ret = dwc3_host_init(dwc); + if (ret) { +@@ -343,6 +343,20 @@ static void __dwc3_set_mode(struct work_struct *work) + break; + } + ++ /* ++ * GCTL.CoreSoftReset above wiped the controller state, so put it back ++ * for a cable that just showed up. The gadget stays registered ++ * throughout, only its pullup follows the cable. ++ */ ++ if (!role_changed && desired_dr_role == DWC3_GCTL_PRTCAP_DEVICE && ++ desired_dr_connected) { ++ dwc3_core_soft_reset(dwc); ++ dwc3_event_buffers_setup(dwc); ++ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); ++ phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ++ dwc3_gadget_resume(dwc); ++ } ++ + out: + pm_runtime_put_autosuspend(dwc->dev); + mutex_unlock(&dwc->mutex); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0049-phy-rockchip-typec-Support-a-Type-C-orientation-swit.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0049-phy-rockchip-typec-Support-a-Type-C-orientation-swit.patch new file mode 100644 index 000000000..2218099eb --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0049-phy-rockchip-typec-Support-a-Type-C-orientation-swit.patch @@ -0,0 +1,150 @@ +From bedb89fb5c6876434dad80435ede0a71f55f446a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 23:39:05 +0200 +Subject: [PATCH 49/84] phy: rockchip-typec: Support a Type-C orientation + switch + +Register a typec_switch when the DT node is marked as one, and take the +plug orientation from it rather than the extcon polarity property. + +The property can only be read at power_on() time, and only once the +extcon provider has published it; a switch lets the port manager hand the +orientation over whenever it knows it. This is the API RK3588's +phy-rockchip-usbdp.c already uses. Boards that do not mark the node keep +reading the extcon property. + +Keep the last orientation on TYPEC_ORIENTATION_NONE, which only says the +cable is gone: the PHY is powered off through phy_power_off() then, so +there is no reason to clear it to a value nothing should read. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit ba3d9cfa99e2ba9a5543aaef821faa82187c1ac8) +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 80 +++++++++++++++++++++-- + 1 file changed, 73 insertions(+), 7 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index d9701b6106d5..717015f33364 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -49,11 +49,13 @@ + #include + #include + #include ++#include + #include + #include + + #include + #include ++#include + + #define CMN_SSM_BANDGAP (0x21 << 2) + #define CMN_SSM_BIAS (0x22 << 2) +@@ -384,6 +386,14 @@ struct rockchip_typec_phy { + + bool flip; + u8 mode; ++ ++ /* ++ * Optional Type-C orientation switch. When one is registered the plug ++ * orientation is taken from it instead of from the extcon polarity ++ * property, so that it no longer has to be re-read on every power_on. ++ */ ++ struct typec_switch_dev *sw; ++ bool sw_flip; + }; + + struct phy_reg { +@@ -838,18 +848,68 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) + mode |= MODE_DFP_USB; + } + +- ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY, +- &property); +- if (ret) { +- dev_err(tcphy->dev, "get polarity property failed\n"); +- return ret; +- } ++ if (tcphy->sw) { ++ tcphy->flip = tcphy->sw_flip; ++ } else { ++ ret = extcon_get_property(edev, id, ++ EXTCON_PROP_USB_TYPEC_POLARITY, ++ &property); ++ if (ret) { ++ dev_err(tcphy->dev, "get polarity property failed\n"); ++ return ret; ++ } + +- tcphy->flip = property.intval ? 1 : 0; ++ tcphy->flip = property.intval ? 1 : 0; ++ } + + return mode; + } + ++static int tcphy_orien_sw_set(struct typec_switch_dev *sw, ++ enum typec_orientation orien) ++{ ++ struct rockchip_typec_phy *tcphy = typec_switch_get_drvdata(sw); ++ ++ mutex_lock(&tcphy->lock); ++ ++ /* ++ * TYPEC_ORIENTATION_NONE only means the cable is gone. The PHY is ++ * powered off through phy_power_off() in that case, and the last ++ * orientation is kept so that nothing reads a stale value from a ++ * half-updated state. ++ */ ++ if (orien != TYPEC_ORIENTATION_NONE) ++ tcphy->sw_flip = orien == TYPEC_ORIENTATION_REVERSE; ++ ++ mutex_unlock(&tcphy->lock); ++ ++ return 0; ++} ++ ++static void tcphy_orien_switch_unregister(void *data) ++{ ++ struct rockchip_typec_phy *tcphy = data; ++ ++ typec_switch_unregister(tcphy->sw); ++} ++ ++static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy) ++{ ++ struct typec_switch_desc sw_desc = { }; ++ ++ sw_desc.drvdata = tcphy; ++ sw_desc.fwnode = dev_fwnode(tcphy->dev); ++ sw_desc.set = tcphy_orien_sw_set; ++ ++ tcphy->sw = typec_switch_register(tcphy->dev, &sw_desc); ++ if (IS_ERR(tcphy->sw)) ++ return dev_err_probe(tcphy->dev, PTR_ERR(tcphy->sw), ++ "failed to register orientation switch\n"); ++ ++ return devm_add_action_or_reset(tcphy->dev, ++ tcphy_orien_switch_unregister, tcphy); ++} ++ + static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy, + bool value) + { +@@ -1158,6 +1218,12 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) + } + } + ++ if (device_property_present(dev, "orientation-switch")) { ++ ret = tcphy_setup_orien_switch(tcphy); ++ if (ret) ++ return ret; ++ } ++ + pm_runtime_enable(dev); + + for_each_available_child_of_node(np, child_np) { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0050-phy-rockchip-typec-Support-a-Type-C-mode-mux.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0050-phy-rockchip-typec-Support-a-Type-C-mode-mux.patch new file mode 100644 index 000000000..4a7973caf --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0050-phy-rockchip-typec-Support-a-Type-C-mode-mux.patch @@ -0,0 +1,225 @@ +From 0690ea7581e1b3d7a07d92f291874d423d7b5e5b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 23:50:24 +0200 +Subject: [PATCH 50/84] phy: rockchip-typec: Support a Type-C mode mux + +Register a typec_mux when the DT node is marked as one, and take what the +lanes carry from it. Map TYPEC_STATE_USB, TYPEC_DP_STATE_C/E and +TYPEC_DP_STATE_D onto the existing MODE_DFP_USB, MODE_DFP_DP and the +combination of both. + +This was read from the extcon cables at power_on() time, too late for a +port manager that has already negotiated an alternate mode, and a mux is +in any case what alternate mode switching goes through. Boards with no mux +keep reading the extcon cables. + +The mux does not carry the USB data role, and this driver does not need +it: MODE_UFP_USB and MODE_DFP_USB are only ever tested together, so +report USB as MODE_DFP_USB throughout. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit f7dfd3a3fec221817aa35d3ec00130fe5dd5b998) +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 145 +++++++++++++++++----- + 1 file changed, 117 insertions(+), 28 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index 717015f33364..25e89ce7856e 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -55,6 +55,7 @@ + + #include + #include ++#include + #include + + #define CMN_SSM_BANDGAP (0x21 << 2) +@@ -394,6 +395,15 @@ struct rockchip_typec_phy { + */ + struct typec_switch_dev *sw; + bool sw_flip; ++ ++ /* ++ * Optional Type-C mode mux. When one is registered it says what the ++ * lanes carry, so the extcon cables no longer have to be read for it. ++ * Starts out as MODE_DFP_USB, which is what the extcon path falls back ++ * to when it has been told nothing yet. ++ */ ++ struct typec_mux_dev *mux; ++ u8 mux_mode; + }; + + struct phy_reg { +@@ -817,40 +827,44 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) + { + struct extcon_dev *edev = tcphy->extcon; + union extcon_property_value property; +- unsigned int id; +- u8 mode; +- int ret, ufp, dp; +- +- if (!edev) +- return MODE_DFP_USB; +- +- ufp = extcon_get_state(edev, EXTCON_USB); +- dp = extcon_get_state(edev, EXTCON_DISP_DP); ++ unsigned int id = EXTCON_USB_HOST; ++ u8 mode = MODE_DFP_USB; ++ int ret; + +- mode = MODE_DFP_USB; +- id = EXTCON_USB_HOST; +- +- if (ufp > 0) { +- mode = MODE_UFP_USB; +- id = EXTCON_USB; +- } else if (dp > 0) { +- mode = MODE_DFP_DP; +- id = EXTCON_DISP_DP; +- +- ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, +- &property); +- if (ret) { +- dev_err(tcphy->dev, "get superspeed property failed\n"); +- return ret; ++ /* ++ * A Type-C mode mux and orientation switch each replace one half of ++ * what would otherwise be read from extcon, so a board can be wired ++ * either way, or both ways during a conversion. ++ */ ++ if (tcphy->mux) { ++ mode = tcphy->mux_mode; ++ id = mode & MODE_DFP_DP ? EXTCON_DISP_DP : EXTCON_USB_HOST; ++ } else if (edev) { ++ int ufp = extcon_get_state(edev, EXTCON_USB); ++ int dp = extcon_get_state(edev, EXTCON_DISP_DP); ++ ++ if (ufp > 0) { ++ mode = MODE_UFP_USB; ++ id = EXTCON_USB; ++ } else if (dp > 0) { ++ mode = MODE_DFP_DP; ++ id = EXTCON_DISP_DP; ++ ++ ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, ++ &property); ++ if (ret) { ++ dev_err(tcphy->dev, "get superspeed property failed\n"); ++ return ret; ++ } ++ ++ if (property.intval) ++ mode |= MODE_DFP_USB; + } +- +- if (property.intval) +- mode |= MODE_DFP_USB; + } + + if (tcphy->sw) { + tcphy->flip = tcphy->sw_flip; +- } else { ++ } else if (edev) { + ret = extcon_get_property(edev, id, + EXTCON_PROP_USB_TYPEC_POLARITY, + &property); +@@ -910,6 +924,74 @@ static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy) + tcphy_orien_switch_unregister, tcphy); + } + ++static int tcphy_mux_set(struct typec_mux_dev *mux, ++ struct typec_mux_state *state) ++{ ++ struct rockchip_typec_phy *tcphy = typec_mux_get_drvdata(mux); ++ bool is_dp = state->alt && state->alt->svid == USB_TYPEC_DP_SID; ++ u8 mode; ++ ++ /* ++ * The mux does not carry the data role, and this driver does not care ++ * about it: MODE_UFP_USB and MODE_DFP_USB are only ever tested ++ * together. Report USB as MODE_DFP_USB throughout. ++ */ ++ switch (state->mode) { ++ case TYPEC_STATE_SAFE: ++ mode = MODE_DISCONNECT; ++ break; ++ case TYPEC_STATE_USB: ++ mode = MODE_DFP_USB; ++ break; ++ case TYPEC_DP_STATE_C: ++ case TYPEC_DP_STATE_E: ++ if (!is_dp) ++ return 0; ++ /* four lanes of DP, no USB SuperSpeed */ ++ mode = MODE_DFP_DP; ++ break; ++ case TYPEC_DP_STATE_D: ++ if (!is_dp) ++ return 0; ++ /* two lanes of DP alongside USB SuperSpeed */ ++ mode = MODE_DFP_DP | MODE_DFP_USB; ++ break; ++ default: ++ dev_warn(tcphy->dev, "unhandled mux mode %lu\n", state->mode); ++ return 0; ++ } ++ ++ mutex_lock(&tcphy->lock); ++ tcphy->mux_mode = mode; ++ mutex_unlock(&tcphy->lock); ++ ++ return 0; ++} ++ ++static void tcphy_mux_unregister(void *data) ++{ ++ struct rockchip_typec_phy *tcphy = data; ++ ++ typec_mux_unregister(tcphy->mux); ++} ++ ++static int tcphy_setup_mux(struct rockchip_typec_phy *tcphy) ++{ ++ struct typec_mux_desc mux_desc = { }; ++ ++ mux_desc.drvdata = tcphy; ++ mux_desc.fwnode = dev_fwnode(tcphy->dev); ++ mux_desc.set = tcphy_mux_set; ++ ++ tcphy->mux = typec_mux_register(tcphy->dev, &mux_desc); ++ if (IS_ERR(tcphy->mux)) ++ return dev_err_probe(tcphy->dev, PTR_ERR(tcphy->mux), ++ "failed to register mode mux\n"); ++ ++ return devm_add_action_or_reset(tcphy->dev, tcphy_mux_unregister, ++ tcphy); ++} ++ + static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy, + bool value) + { +@@ -1153,6 +1235,7 @@ static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) + property_enable(tcphy, &cfg->usb3tousb2_en, 0); + + tcphy->mode = MODE_DISCONNECT; ++ tcphy->mux_mode = MODE_DFP_USB; + } + + static int rockchip_typec_phy_probe(struct platform_device *pdev) +@@ -1224,6 +1307,12 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) + return ret; + } + ++ if (device_property_present(dev, "mode-switch")) { ++ ret = tcphy_setup_mux(tcphy); ++ if (ret) ++ return ret; ++ } ++ + pm_runtime_enable(dev); + + for_each_available_child_of_node(np, child_np) { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0051-phy-rockchip-typec-Publish-the-DP-lane-count.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0051-phy-rockchip-typec-Publish-the-DP-lane-count.patch new file mode 100644 index 000000000..4819948b2 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0051-phy-rockchip-typec-Publish-the-DP-lane-count.patch @@ -0,0 +1,106 @@ +From c5d1fecc501008af68641d44c93b8bb81cceca5f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 23:15:20 +0200 +Subject: [PATCH 51/84] phy: rockchip-typec: Publish the DP lane count + +Publish the negotiated lane count from the mode mux with +phy_set_bus_width(): four lanes for DP alone, two when it shares the +connector with USB SuperSpeed, and zero whenever the lanes carry USB or +no sink has raised HPD. + +The DP controller sits behind this PHY and is the one the mux configures, +so it has no other way to learn how many lanes it was left; it reads the +count back over the phandle it already holds. Zero doubles as "nothing to +drive", sparing it from telling "no alternate mode" from "alternate mode +without a sink". + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 23febe23e8959ed5219186aa6892d8b42268fd18) +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 28 ++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index 25e89ce7856e..989274f456b9 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -404,6 +404,13 @@ struct rockchip_typec_phy { + */ + struct typec_mux_dev *mux; + u8 mux_mode; ++ ++ /* ++ * The DP PHY, kept so that the lane count the alternate mode settled ++ * on can be published on it. The DP controller sits behind this PHY ++ * and has no other way of learning how many lanes it was left. ++ */ ++ struct phy *dp_phy; + }; + + struct phy_reg { +@@ -929,6 +936,8 @@ static int tcphy_mux_set(struct typec_mux_dev *mux, + { + struct rockchip_typec_phy *tcphy = typec_mux_get_drvdata(mux); + bool is_dp = state->alt && state->alt->svid == USB_TYPEC_DP_SID; ++ struct typec_displayport_data *data; ++ u8 lanes = 0; + u8 mode; + + /* +@@ -949,12 +958,18 @@ static int tcphy_mux_set(struct typec_mux_dev *mux, + return 0; + /* four lanes of DP, no USB SuperSpeed */ + mode = MODE_DFP_DP; ++ data = state->data; ++ if (data->status & DP_STATUS_HPD_STATE) ++ lanes = 4; + break; + case TYPEC_DP_STATE_D: + if (!is_dp) + return 0; + /* two lanes of DP alongside USB SuperSpeed */ + mode = MODE_DFP_DP | MODE_DFP_USB; ++ data = state->data; ++ if (data->status & DP_STATUS_HPD_STATE) ++ lanes = 2; + break; + default: + dev_warn(tcphy->dev, "unhandled mux mode %lu\n", state->mode); +@@ -965,6 +980,13 @@ static int tcphy_mux_set(struct typec_mux_dev *mux, + tcphy->mux_mode = mode; + mutex_unlock(&tcphy->lock); + ++ /* ++ * Publish the lane count on the DP PHY. Zero means the DP controller ++ * has nothing to drive, either because the lanes carry USB or because ++ * no sink has raised HPD yet. ++ */ ++ phy_set_bus_width(tcphy->dp_phy, lanes); ++ + return 0; + } + +@@ -1317,8 +1339,9 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) + + for_each_available_child_of_node(np, child_np) { + struct phy *phy; ++ bool is_dp = of_node_name_eq(child_np, "dp-port"); + +- if (of_node_name_eq(child_np, "dp-port")) ++ if (is_dp) + phy = devm_phy_create(dev, child_np, + &rockchip_dp_phy_ops); + else if (of_node_name_eq(child_np, "usb3-port")) +@@ -1336,6 +1359,9 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) + } + + phy_set_drvdata(phy, tcphy); ++ ++ if (is_dp) ++ tcphy->dp_phy = phy; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0052-drm-rockchip-cdn-dp-Support-ports-driven-by-a-Type-C.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0052-drm-rockchip-cdn-dp-Support-ports-driven-by-a-Type-C.patch new file mode 100644 index 000000000..9bb437d43 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0052-drm-rockchip-cdn-dp-Support-ports-driven-by-a-Type-C.patch @@ -0,0 +1,169 @@ +From dde8b3728ec4e6844e479e989e6492cbda1074c1 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 23:17:37 +0200 +Subject: [PATCH 52/84] drm/rockchip: cdn-dp: Support ports driven by a Type-C + PHY + +Support a Type-C port whose lanes reach the connector through a Type-C +PHY. Take the lane count from the PHY with phy_get_bus_width(), where zero +means nothing to drive, and take the alternate mode's HPD out of band +through the bridge's hpd_notify, which drm_connector_oob_hotplug_event() +feeds from the DP altmode driver. Orientation needs no handling: the PHY +reverses the lanes itself, so the controller sees them in order. + +Nothing publishes extcon cables on such a port, which is all the driver +otherwise reads a Type-C port from, so probe skipped it for want of an +extcon and a board with no other port failed to bind with "missing extcon +or phy". + +A port now needs a PHY and nothing else. Ports that have an extcon keep +reading all of it from there. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit a0dfa262516e05183118110165ce9744e07ab95f) +--- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 69 ++++++++++++++++++++++---- + 1 file changed, 60 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 13b83fe8feed..08257a3d1dbd 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -157,6 +157,15 @@ static int cdn_dp_get_port_lanes(struct cdn_dp_port *port) + int dptx; + u8 lanes; + ++ /* ++ * A port that has no extcon is driven by a Type-C PHY, which publishes ++ * how many lanes the alternate mode left for DP. Zero of them means ++ * there is nothing to drive, either because the lanes carry USB or ++ * because no sink has raised HPD. ++ */ ++ if (!edev) ++ return phy_get_bus_width(port->phy); ++ + dptx = extcon_get_state(edev, EXTCON_DISP_DP); + if (dptx > 0) { + extcon_get_property(edev, EXTCON_DISP_DP, +@@ -220,7 +229,7 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) + * some docks need more time to power up. + */ + while (time_before(jiffies, timeout)) { +- if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) ++ if (!cdn_dp_get_port_lanes(port)) + return false; + + if (!cdn_dp_get_sink_count(dp, &sink_count)) +@@ -360,6 +369,7 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) + static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) + { + union extcon_property_value property; ++ bool flip; + int ret; + + if (!port->phy_enabled) { +@@ -386,15 +396,26 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) + goto err_power_on; + } + +- ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, +- EXTCON_PROP_USB_TYPEC_POLARITY, &property); +- if (ret) { +- DRM_DEV_ERROR(dp->dev, "get property failed\n"); +- goto err_power_on; ++ if (port->extcon) { ++ ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, ++ EXTCON_PROP_USB_TYPEC_POLARITY, ++ &property); ++ if (ret) { ++ DRM_DEV_ERROR(dp->dev, "get property failed\n"); ++ goto err_power_on; ++ } ++ ++ flip = property.intval; ++ } else { ++ /* ++ * A Type-C PHY reverses the lanes itself when the plug is ++ * upside down, so the controller always sees them in order. ++ */ ++ flip = false; + } + + port->lanes = cdn_dp_get_port_lanes(port); +- ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval); ++ ret = cdn_dp_set_host_cap(dp, port->lanes, flip); + if (ret) { + DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n", + ret); +@@ -822,11 +843,34 @@ static int cdn_dp_audio_mute_stream(struct drm_bridge *bridge, + return ret; + } + ++/* ++ * A port driven by a Type-C PHY has no extcon to notify us, so the alternate ++ * mode's HPD arrives out of band instead: the DP alt mode driver resolves the ++ * connector's displayport phandle to this bridge and reports it here. ++ * ++ * cdn_dp_pd_event_work() finishes by reporting the state it settled on, which ++ * comes back through this same path. Only act on a status that disagrees with ++ * what we already concluded, or the two would keep waking each other. ++ */ ++static void cdn_dp_bridge_hpd_notify(struct drm_bridge *bridge, ++ struct drm_connector *connector, ++ enum drm_connector_status status) ++{ ++ struct cdn_dp_device *dp = bridge_to_dp(bridge); ++ bool connected = status == connector_status_connected; ++ ++ if (dp->connected == connected) ++ return; ++ ++ schedule_work(&dp->event_work); ++} ++ + static const struct drm_bridge_funcs cdn_dp_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .detect = cdn_dp_bridge_detect, ++ .hpd_notify = cdn_dp_bridge_hpd_notify, + .edid_read = cdn_dp_bridge_edid_read, + .atomic_enable = cdn_dp_bridge_atomic_enable, + .atomic_disable = cdn_dp_bridge_atomic_disable, +@@ -1030,6 +1074,9 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) + for (i = 0; i < dp->ports; i++) { + port = dp->port[i]; + ++ if (!port->extcon) ++ continue; ++ + port->event_nb.notifier_call = cdn_dp_pd_event; + ret = devm_extcon_register_notifier(dp->dev, port->extcon, + EXTCON_DISP_DP, +@@ -1122,9 +1169,13 @@ static int cdn_dp_probe(struct platform_device *pdev) + PTR_ERR(phy) == -EPROBE_DEFER) + return -EPROBE_DEFER; + +- if (IS_ERR(extcon) || IS_ERR(phy)) ++ if (IS_ERR(phy)) + continue; + ++ /* Type-C PHY driven ports have no extcon. */ ++ if (IS_ERR(extcon)) ++ extcon = NULL; ++ + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; +@@ -1137,7 +1188,7 @@ static int cdn_dp_probe(struct platform_device *pdev) + } + + if (!dp->ports) { +- DRM_DEV_ERROR(dev, "missing extcon or phy\n"); ++ DRM_DEV_ERROR(dev, "missing phy\n"); + return -EINVAL; + } + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0053-phy-rockchip-inno-usb2-Learn-the-host-role-from-phy_.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0053-phy-rockchip-inno-usb2-Learn-the-host-role-from-phy_.patch new file mode 100644 index 000000000..c2b005a55 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0053-phy-rockchip-inno-usb2-Learn-the-host-role-from-phy_.patch @@ -0,0 +1,141 @@ +From 642e715b3a43fe3a73dfb1f4a01e28f57916e72f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 00:20:41 +0200 +Subject: [PATCH 53/84] phy: rockchip-inno-usb2: Learn the host role from + phy_set_mode() + +The OTG state machine finds out whether the port is acting as a host by +reading EXTCON_USB_HOST. That only works when something else supplies +the extcon device and writes to it; when this driver registers its own, +the only writer is its own ID pin handling, which nothing outside can +see, so a board with no ID pin has no way to reach OTG_STATE_A_HOST. + +Take the role from phy_set_mode() in that case. The controller already +calls it on every role change, so the information is there for the +asking, and on a Type-C board it comes from the port manager rather than +having to be routed back in through an extcon device. + +Boards with an extcon phandle keep reading EXTCON_USB_HOST and are not +affected. Boards without one cannot regress either: nothing consumed the +EXTCON_USB_HOST this driver published to its own extcon device, so the +ID pin never reached the controller there to begin with. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 9d26be6b0bb1456a25fc39aa91dcaaa072c8bbad) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 59 ++++++++++++++++++- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index a138417f0b2e..63acee6a4310 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -229,6 +229,7 @@ struct rockchip_usb2phy_port { + struct notifier_block event_nb; + enum usb_otg_state state; + enum usb_dr_mode mode; ++ bool host_mode; + }; + + /** +@@ -267,6 +268,8 @@ struct rockchip_usb2phy { + u8 dcd_retries; + unsigned int chg_mode_cfg; + struct extcon_dev *edev; ++ /* the extcon device came from a phandle rather than being ours */ ++ bool edev_external; + int irq; + const struct rockchip_usb2phy_cfg *phy_cfg; + struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; +@@ -456,6 +459,7 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) + if (IS_ERR(edev)) + return dev_err_probe(rphy->dev, PTR_ERR(edev), + "invalid or missing extcon\n"); ++ rphy->edev_external = true; + } else { + /* Initialize extcon device */ + edev = devm_extcon_dev_allocate(rphy->dev, +@@ -659,11 +663,62 @@ static int rockchip_usb2phy_exit(struct phy *phy) + return 0; + } + ++/* ++ * Whether the OTG port is acting as a host. ++ * ++ * With an extcon device supplied by someone else, EXTCON_USB_HOST is what ++ * says so, either driven by a Type-C port manager or looped back from this ++ * driver's own ID pin handling. Otherwise nothing external writes to our ++ * extcon device and the controller tells us instead, through phy_set_mode(). ++ */ ++static bool rockchip_usb2phy_host_mode(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport) ++{ ++ if (!rphy->edev_external) ++ return rport->host_mode; ++ ++ return extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0; ++} ++ ++static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode, ++ int submode) ++{ ++ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ bool host_mode; ++ ++ if (rport->port_id != USB2PHY_PORT_OTG || rphy->edev_external) ++ return 0; ++ ++ switch (mode) { ++ case PHY_MODE_USB_HOST: ++ host_mode = true; ++ break; ++ case PHY_MODE_USB_DEVICE: ++ host_mode = false; ++ break; ++ default: ++ return 0; ++ } ++ ++ if (rport->host_mode == host_mode) ++ return 0; ++ ++ dev_err(&rport->phy->dev, "U2PHY set_mode: host=%d\n", host_mode); ++ rport->host_mode = host_mode; ++ ++ /* let the state machine act on the new role */ ++ mod_delayed_work(system_wq, &rport->otg_sm_work, 0); ++ ++ return 0; ++} ++ + static const struct phy_ops rockchip_usb2phy_ops = { + .init = rockchip_usb2phy_init, + .exit = rockchip_usb2phy_exit, + .power_on = rockchip_usb2phy_power_on, + .power_off = rockchip_usb2phy_power_off, ++ .set_mode = rockchip_usb2phy_set_mode, + .owner = THIS_MODULE, + }; + +@@ -699,7 +754,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + rockchip_usb2phy_power_off(rport->phy); + fallthrough; + case OTG_STATE_B_IDLE: +- if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) { ++ if (rockchip_usb2phy_host_mode(rphy, rport)) { + dev_dbg(&rport->phy->dev, "usb otg host connect\n"); + rport->state = OTG_STATE_A_HOST; + rockchip_usb2phy_power_on(rport->phy); +@@ -774,7 +829,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + sch_work = true; + break; + case OTG_STATE_A_HOST: +- if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { ++ if (!rockchip_usb2phy_host_mode(rphy, rport)) { + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); + rport->state = OTG_STATE_B_IDLE; + rockchip_usb2phy_power_off(rport->phy); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0054-usb-dwc3-Tell-the-UDC-core-when-there-is-no-cable.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0054-usb-dwc3-Tell-the-UDC-core-when-there-is-no-cable.patch new file mode 100644 index 000000000..11a8a75e9 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0054-usb-dwc3-Tell-the-UDC-core-when-there-is-no-cable.patch @@ -0,0 +1,102 @@ +From 8a289a732202204e0c19c6f38da071d3b4e96f7a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 07:22:48 +0200 +Subject: [PATCH 54/84] usb: dwc3: Tell the UDC core when there is no cable + +The USB3 PHY is powered off while no cable is connected, so anything +that touches the endpoints in that state fails. Binding a gadget driver +does exactly that: the UDC core pulls up as soon as a driver binds, +because udc->vbus starts out true and nothing ever says otherwise, and +the pullup runs __dwc3_gadget_start() against the powered off PHY: + + dwc3 fe800000.usb: failed to enable ep0out + +Report the connection state with usb_udc_vbus_handler() so the core only +pulls up when there is a cable. That also replaces the +dwc3_gadget_suspend()/dwc3_gadget_resume() pair that was approximating +it: the pullup is what has to follow the cable, and the core already +knows how to do that, including for a driver that binds later while +still disconnected. + +usb_udc_vbus_handler() only records the new state and schedules the +pullup change to a workqueue, though, while __dwc3_set_mode() goes +straight on with the teardown: event buffers freed, GCTL.CORESOFTRESET +asserted for 100ms and the USB3 PHY powered off. A soft disconnect +scheduled at unplug races into that window and runs against a core held +in reset with its PHY clocks gone: the endpoint end transfer commands +read back fake completions, the DCTL/DSTS/DEVTEN accesses are lost, and +the controller is left in a state even the DCTL.CSFTRST on the next +plug-in does not recover from. The next session then comes up with a +mute ep0 (host: 'Device not responding to setup address') and a stuck +event interrupt that ends in a softirq lockup and 'irq: nobody cared'. + +So change the pullup synchronously with the cable state on top of the +bookkeeping: usb_gadget_disconnect() before touching the event buffers, +the core reset or the PHY power, and usb_gadget_connect() after the +core is set back up. The scheduled work then finds the pullup already +in the requested state and does nothing. Both directions serialize on +the UDC connect lock, so a quick unplug also waits for an in-flight +connect instead of resetting the core under it. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 406dea5dc6471bc2d8809df426f32445ed1d39a1) +--- + drivers/usb/dwc3/core.c | 35 ++++++++++++++++++++++++++++++++--- + 1 file changed, 32 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 18d50856d3b6..2c1cada5d637 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -204,8 +204,20 @@ static void __dwc3_set_mode(struct work_struct *work) + * the runtime PM path does it. + */ + if (!role_changed && dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE && +- dwc->current_dr_connected) { +- dwc3_gadget_suspend(dwc); ++ dwc->current_dr_connected && dwc->gadget) { ++ /* ++ * usb_udc_vbus_handler() only records the state and schedules ++ * the pullup change for later, but the controller reset and ++ * PHY power off below must not run while the gadget is still ++ * live: the soft disconnect (endpoint end transfer commands, ++ * DCTL/DEVTEN writes) would then hit a core that is held in ++ * reset with its PHY clocks gone, be silently lost, and leave ++ * the controller in a state the next plug-in cannot recover ++ * from. Stop the gadget synchronously first; the scheduled ++ * work then finds nothing left to do. ++ */ ++ usb_udc_vbus_handler(dwc->gadget, false); ++ usb_gadget_disconnect(dwc->gadget); + dwc3_event_buffers_cleanup(dwc); + } + +@@ -354,7 +366,24 @@ static void __dwc3_set_mode(struct work_struct *work) + dwc3_event_buffers_setup(dwc); + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); +- dwc3_gadget_resume(dwc); ++ } ++ ++ /* ++ * Tell the UDC core whether there is a cable, so that it only pulls up ++ * when there is one. Without this it pulls up as soon as a gadget ++ * driver binds, which starts the endpoints on a USB3 PHY that is ++ * powered off while disconnected, and ep0 fails to come up. ++ * ++ * Perform the connect synchronously as well, instead of leaving it to ++ * the scheduled vbus work: the pullup then happens strictly after the ++ * core and event buffer setup above, and a disconnect that follows a ++ * quick unplug serializes against it on the UDC connect lock instead ++ * of tearing the controller down under a half-finished connect. ++ */ ++ if (desired_dr_role == DWC3_GCTL_PRTCAP_DEVICE && dwc->gadget) { ++ usb_udc_vbus_handler(dwc->gadget, desired_dr_connected); ++ if (desired_dr_connected) ++ usb_gadget_connect(dwc->gadget); + } + + out: +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0055-phy-rockchip-inno-usb2-Do-not-run-charger-detection-.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0055-phy-rockchip-inno-usb2-Do-not-run-charger-detection-.patch new file mode 100644 index 000000000..ba98f6018 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0055-phy-rockchip-inno-usb2-Do-not-run-charger-detection-.patch @@ -0,0 +1,114 @@ +From b715f77b5a03b5ff3f5ec9d315424478e933a445 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 07:35:49 +0200 +Subject: [PATCH 55/84] phy: rockchip-inno-usb2: Do not run charger detection + as a host + +VBUS comes up before anything says which way round the port is: the +Type-C port manager turns it on at SRC_ATTACHED, the PHY's bvalid +interrupt fires, and the OTG state machine starts BC1.2 while the role +is still unknown. By the time dwc3 calls phy_set_mode(PHY_MODE_USB_HOST) +detection is already several stages in. + +That used to be harmless because chg_det.opmode was never written, so +detection did not really park the transceiver. Now that it does, the +consequences show up on the bus: + + 16.578 OTGSM b_idle vbus=1 host=0 detection starts + 16.583 mode[0xe454]=0x1d7 transceiver parked, FS, pull-downs + 16.994 U2PHY set_mode: host=1 role finally known + 17.177 usb 8-1: new full-speed USB device number 2 + 17.377 dcd timed out -> DCP no charger, we are sourcing VBUS + +A high speed device enumerated at full speed, because its chirp happened +while the transceiver was parked. Then the bogus DCP outlived the +connection: OTG_STATE_A_HOST never cleared it, so the next time a real +host was plugged in the state machine still saw DCP and powered the USB2 +PHY off, leaving the gadget dead. + +Cancel detection when the role changes, undoing whichever stage it had +reached, and clear the result when leaving host mode. There is still a +window between VBUS and the role being known, but nothing acts on +detection until it finishes, and it no longer finishes. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit b4a35830d0c92b801c7878f1f2f1984516d53a5e) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 40 +++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 63acee6a4310..49994c2390b7 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -680,6 +680,9 @@ static bool rockchip_usb2phy_host_mode(struct rockchip_usb2phy *rphy, + return extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0; + } + ++static void rockchip_chg_detect_cancel(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport); ++ + static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) + { +@@ -707,6 +710,14 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode, + dev_err(&rport->phy->dev, "U2PHY set_mode: host=%d\n", host_mode); + rport->host_mode = host_mode; + ++ /* ++ * VBUS comes up before anyone says which way round the port is, so ++ * charger detection may already be running against a bus that is about ++ * to be used the other way. Whatever it has decided belongs to the old ++ * role either way. ++ */ ++ rockchip_chg_detect_cancel(rphy, rport); ++ + /* let the state machine act on the new role */ + mod_delayed_work(system_wq, &rport->otg_sm_work, 0); + +@@ -832,6 +843,12 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + if (!rockchip_usb2phy_host_mode(rphy, rport)) { + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); + rport->state = OTG_STATE_B_IDLE; ++ /* ++ * Nothing was ever detected here, we were sourcing ++ * VBUS. Do not let that reach the next connection. ++ */ ++ rphy->chg_state = USB_CHG_STATE_UNDEFINED; ++ rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rockchip_usb2phy_power_off(rport->phy); + } + break; +@@ -946,6 +963,29 @@ static void rockchip_chg_leave_mode(struct rockchip_usb2phy *rphy, + rphy->chg_mode_cfg | (mask << BIT_WRITEABLE_SHIFT)); + } + ++/* ++ * Charger detection parks the transceiver and drives DP/DM, so it must not be ++ * left running once the port turns out to be a host: there is no charger to ++ * find, VBUS is ours, and the bus is about to be enumerated. Undo whichever ++ * stage it had reached and drop the result. ++ */ ++static void rockchip_chg_detect_cancel(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport) ++{ ++ cancel_delayed_work_sync(&rport->chg_work); ++ ++ if (rphy->chg_state != USB_CHG_STATE_UNDEFINED && ++ rphy->chg_state != USB_CHG_STATE_DETECTED) { ++ rockchip_chg_enable_dcd(rphy, false); ++ rockchip_chg_enable_primary_det(rphy, false); ++ rockchip_chg_enable_secondary_det(rphy, false); ++ rockchip_chg_leave_mode(rphy, rport, false); ++ } ++ ++ rphy->chg_state = USB_CHG_STATE_UNDEFINED; ++ rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; ++} ++ + #define CHG_DCD_POLL_TIME (100 * HZ / 1000) + #define CHG_DCD_MAX_RETRIES 6 + #define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0056-usb-dwc3-Set-the-PHY-mode-before-starting-the-host.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0056-usb-dwc3-Set-the-PHY-mode-before-starting-the-host.patch new file mode 100644 index 000000000..88f325a8d --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0056-usb-dwc3-Set-the-PHY-mode-before-starting-the-host.patch @@ -0,0 +1,73 @@ +From 7ac552f6eeb21d5d1782a07530f0a87d7d7b4022 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 07:43:34 +0200 +Subject: [PATCH 56/84] usb: dwc3: Set the PHY mode before starting the host + +The device case tells the PHYs the role before dwc3_gadget_init(), but +the host case tells them only after dwc3_host_init() has returned. +dwc3_host_init() runs the xHCI probe, which uses the PHYs and can take a +long time, so a PHY that acts on the mode does not find out until the +controller has already been using it the other way round. + +On a PinePhone Pro the USB2 PHY uses the mode to know it must not run +BC1.2 charger detection, because in host mode VBUS is ours and the bus +is in use. It was told 15.7 s after VBUS came up, by which point +detection had run to completion, decided the port was a floating +charger, and powered the USB2 PHY off underneath the probe: + + 46.498 FUSB vbus := on + 46.774 xhci-hcd: new USB bus registered, assigned bus number 8 + 48.514 charger = USB_DCP_CHARGER + 48.534 U2PHY power_off + 62.238 xhci-hcd: Host halt failed, -110 + 62.254 U2PHY set_mode: host=1 + +Move the mode setting ahead of dwc3_host_init(), which also makes the +two cases consistent. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 82f8bbb2eadcff8cf5840c016745165130b1761f) +--- + drivers/usb/dwc3/core.c | 22 ++++++++++++++-------- + 1 file changed, 14 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 2c1cada5d637..fa961b84d2b5 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -314,18 +314,24 @@ static void __dwc3_set_mode(struct work_struct *work) + + switch (role_changed ? desired_dr_role : 0) { + case DWC3_GCTL_PRTCAP_HOST: ++ /* ++ * Tell the PHYs the role before starting the host, the way the ++ * device case does. dwc3_host_init() runs the xHCI probe, which ++ * uses the PHYs and can take a long time, so a PHY that acts on ++ * the mode must not be told about it only afterwards. ++ */ ++ if (dwc->usb2_phy) ++ otg_set_vbus(dwc->usb2_phy->otg, true); ++ ++ for (i = 0; i < dwc->num_usb2_ports; i++) ++ phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); ++ for (i = 0; i < dwc->num_usb3_ports; i++) ++ phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); ++ + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dwc->dev, "failed to initialize host\n"); + } else { +- if (dwc->usb2_phy) +- otg_set_vbus(dwc->usb2_phy->otg, true); +- +- for (i = 0; i < dwc->num_usb2_ports; i++) +- phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); +- for (i = 0; i < dwc->num_usb3_ports; i++) +- phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); +- + if (dwc->dis_split_quirk) { + reg = dwc3_readl(dwc, DWC3_GUCTL3); + reg |= DWC3_GUCTL3_SPLITDISABLE; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0057-usb-dwc3-Do-not-connect-the-gadget-while-no-cable-is.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0057-usb-dwc3-Do-not-connect-the-gadget-while-no-cable-is.patch new file mode 100644 index 000000000..ee67b9e73 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0057-usb-dwc3-Do-not-connect-the-gadget-while-no-cable-is.patch @@ -0,0 +1,60 @@ +From df015e8b9599ef975a1a90429f64bf1990dba73d Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 07:43:49 +0200 +Subject: [PATCH 57/84] usb: dwc3: Do not connect the gadget while no cable is + present + +usb_add_gadget() starts out assuming VBUS is present and binds any +gadget driver that was waiting, so the connect happens inside +dwc3_gadget_init(), before dwc3 has any way to say there is no cable. +With the USB3 PHY powered down while disconnected, that runs the +endpoint setup against a PHY that is off: + + dwc3 fe800000.usb: request 000000002e1b5f5e was not queued to ep0out + udc fe800000.usb: failed to start adbd: -524 + configfs-gadget.adbd gadget.2: probe with driver configfs-gadget.adbd + failed with error -524 + +The gadget driver is then unbound for good, so the port stays dead until +userspace sets it up again. It shows up after a host to device role +change with nothing plugged in, which is where the gadget is registered +while disconnected. + +Remember the request instead and leave the pullup to the connection +state, which __dwc3_set_mode() already reports through +usb_udc_vbus_handler(). + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 2ef635fb2667eb4e12fe0260152c261a25c414d6) +--- + drivers/usb/dwc3/gadget.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 519a1dd23a30..4fbcad5bb2b2 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2797,6 +2797,20 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + + dwc->softconnect = is_on; + ++ /* ++ * The UDC core connects as soon as a gadget driver binds, because it ++ * assumes VBUS is present until told otherwise, and it can only be ++ * told once the gadget is registered. On a port that powers its PHYs ++ * down while nothing is plugged in, that would run the endpoint setup ++ * against a PHY that is off and fail the bind. ++ * ++ * Record the request and let the connection state drive it instead. ++ * __dwc3_set_mode() reports that state, so the pullup follows the ++ * cable either way. ++ */ ++ if (is_on && dwc->dr_mode == USB_DR_MODE_OTG && !dwc->current_dr_connected) ++ return 0; ++ + /* + * Avoid issuing a runtime resume if the device is already in the + * suspended state during gadget disconnect. DWC3 gadget was already +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0058-phy-rockchip-inno-usb2-Power-the-PHY-up-before-the-h.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0058-phy-rockchip-inno-usb2-Power-the-PHY-up-before-the-h.patch new file mode 100644 index 000000000..f6aa7c84b --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0058-phy-rockchip-inno-usb2-Power-the-PHY-up-before-the-h.patch @@ -0,0 +1,61 @@ +From bc44b71cbbaec180d07ac4961c99e7b82e5f086a Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 07:52:19 +0200 +Subject: [PATCH 58/84] phy: rockchip-inno-usb2: Power the PHY up before the + host starts + +Switching to host mode only queued the OTG state machine, so the PHY was +powered up whenever that work happened to run. dwc3 sets the PHY mode +and then immediately starts the host controller, which put the power up +in the middle of the xHCI probe: + + 8.009 U2PHY set_mode: host=1 + 8.011 xhci-hcd: xHCI Host Controller + 8.044 xhci-hcd: new USB bus registered, assigned bus number 8 + 8.054 U2PHY power_on + 8.066 xhci-hcd: new USB bus registered, assigned bus number 9 + +A device that is already attached has to be visible by the time the root +hub is registered, and here it was not: an attached storage dongle never +enumerated at all. It used to work by accident, because the mode was set +after the host was up and the power up landed after the whole probe. + +Take the host transition immediately instead. Device mode still goes +through the state machine, which has to work out what the port is facing +before it can do anything anyway. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 04db52e4ec332802c34c202252c37f9e37d414c1) +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 49994c2390b7..22ef0e8dd117 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -718,7 +718,20 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode, + */ + rockchip_chg_detect_cancel(rphy, rport); + +- /* let the state machine act on the new role */ ++ if (host_mode) { ++ /* ++ * The controller starts the host right after this returns, and ++ * the device is already attached, so its connect has to be ++ * visible by then. Leaving the power up to the state machine ++ * lands it somewhere in the middle of the host controller ++ * probe, and the connect is missed. ++ */ ++ rport->state = OTG_STATE_A_HOST; ++ rockchip_usb2phy_power_on(phy); ++ return 0; ++ } ++ ++ /* let the state machine work out what a device mode port is facing */ + mod_delayed_work(system_wq, &rport->otg_sm_work, 0); + + return 0; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0059-usb-dwc3-Require-MPS-aligned-OUT-buffers.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0059-usb-dwc3-Require-MPS-aligned-OUT-buffers.patch new file mode 100644 index 000000000..56b843c77 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0059-usb-dwc3-Require-MPS-aligned-OUT-buffers.patch @@ -0,0 +1,44 @@ +From 2575cfc3439b3a876b35644780657e87cb5739cd Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 10:22:14 +0200 +Subject: [PATCH 59/84] usb: dwc3: Require MPS-aligned OUT buffers + +An OUT request whose length is not a multiple of the maximum packet size +gets an extra TRB pointing at a shared bounce buffer, so that a final +full packet from the host has somewhere to land. Bytes the host sends +past the end of the request land in that buffer and are dropped without +the function driver ever hearing about it, silently desynchronising a +byte stream. + +Set quirk_ep_out_aligned_size so the framework rounds OUT buffers up to +the maximum packet size and the overflow cannot happen. + +(cherry picked from commit 7b6bedf1b7096d2ce9dd3d9d0006508e1852e816) +--- + drivers/usb/dwc3/gadget.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 4fbcad5bb2b2..c5f5efda2185 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -4782,6 +4782,16 @@ int dwc3_gadget_init(struct dwc3 *dwc) + dwc->revision); + + dwc->gadget->max_speed = dwc->maximum_speed; ++ ++ /* ++ * An OUT request that is not a multiple of the maximum packet size gets ++ * an extra TRB pointing at a shared bounce buffer, so that a final full ++ * packet from the host has somewhere to land. Anything the host sends ++ * past the end of the request ends up in that buffer and is discarded - ++ * see dwc3_gadget_ep_reclaim_completed_trb(). Tell the gadget framework ++ * to round OUT buffers up instead, so the situation cannot arise. ++ */ ++ dwc->gadget->quirk_ep_out_aligned_size = true; + dwc->gadget->max_ssp_rate = dwc->max_ssp_rate; + + /* +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0060-usb-typec-displayport-Update-the-mux-before-signalli.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0060-usb-typec-displayport-Update-the-mux-before-signalli.patch new file mode 100644 index 000000000..36e3ebf54 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0060-usb-typec-displayport-Update-the-mux-before-signalli.patch @@ -0,0 +1,106 @@ +From c0b9ffa7d04334b0ba1a136f4cda14ac725eb929 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Wed, 22 Jul 2026 14:03:03 +0200 +Subject: [PATCH 60/84] usb: typec: displayport: Update the mux before + signalling hotplug + +On HPD changes the DP alt mode driver signalled the DRM side first and +only updated the mux afterwards. The DRM driver handling the event may +depend on what the mux publishes for the new status: on RK3399 the +Type-C PHY reports through phy_get_bus_width() how many lanes the +alternate mode left for DP, and cdn-dp treats zero as 'nothing to +drive'. + +That ordering breaks both directions of hotplug: + +On plug-in, when the dock raised HPD via Attention, cdn-dp's event work +ran off the hotplug event, read the lane count before tcphy_mux_set() +had stored it, concluded 'Not connected; disabling cdn' and never +looked again, so the external display stayed dark. + +On unplug, dp_altmode_remove() raised the hotplug event with the mux +still publishing the old lane count. The event blocks on DRM locks held +by a commit that is still driving the vanished sink, and as long as the +lanes look present cdn-dp keeps re-enabling the output against a dead +AUX channel, each attempt a 5 second DPCD timeout under those same +locks. The disconnect handshake sat blocked behind that for over ten +seconds before the pipeline finally tore down. + +Notify the mux first everywhere hotplug is signalled: the status update +path, the deferred HPD signalling after configuration, and the alt mode +removal. With the lane count cleared first, an in-flight enable aborts +at the next lane check instead of timing out, and the teardown is +immediate. The extra notification dp_altmode_attention() does +afterwards is left in place; repeating the same state to the mux is +harmless. + +Signed-off-by: Ondrej Jirman +--- + drivers/usb/typec/altmodes/displayport.c | 28 +++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c +index 722030137015..380bd4215404 100644 +--- a/drivers/usb/typec/altmodes/displayport.c ++++ b/drivers/usb/typec/altmodes/displayport.c +@@ -205,6 +205,14 @@ static int dp_altmode_status_update(struct dp_altmode *dp) + dp->pending_irq_hpd = true; + } + } else { ++ /* ++ * Update the mux before signalling the hotplug event: the DRM ++ * driver handling the event may look at what the mux published ++ * for the new status (e.g. how many lanes the alternate mode ++ * left for DP), and with the event sent first it would race ++ * with the mux update and read stale state. ++ */ ++ ret = dp_altmode_notify(dp); + drm_connector_oob_hotplug_event(dp->connector_fwnode, + hpd ? connector_status_connected : + connector_status_disconnected); +@@ -221,8 +229,14 @@ static int dp_altmode_status_update(struct dp_altmode *dp) + + static int dp_altmode_configured(struct dp_altmode *dp) + { ++ int ret; ++ + sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); + sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); ++ ++ /* Update the mux before signalling the hotplug event, see above. */ ++ ret = dp_altmode_notify(dp); ++ + /* + * If the DFP_D/UFP_D sends a change in HPD when first notifying the + * DisplayPort driver that it is connected, then we wait until +@@ -240,7 +254,7 @@ static int dp_altmode_configured(struct dp_altmode *dp) + } + } + +- return dp_altmode_notify(dp); ++ return ret; + } + + static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) +@@ -869,6 +883,18 @@ void dp_altmode_remove(struct typec_altmode *alt) + cancel_work_sync(&dp->work); + typec_altmode_put_plug(dp->plug_prime); + ++ /* ++ * Same ordering as on status updates: tell the mux the lanes are gone ++ * before signalling the disconnect. The hotplug event can block on DRM ++ * locks held by a commit that is still driving the vanished sink, and ++ * until the mux is updated the DRM driver keeps finding lanes to ++ * drive and re-enables the output against a dead AUX channel instead ++ * of tearing it down. ++ */ ++ dp->data.status = 0; ++ dp->data.conf = 0; ++ dp_altmode_notify(dp); ++ + if (dp->connector_fwnode) { + drm_connector_oob_hotplug_event(dp->connector_fwnode, + connector_status_disconnected); +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0061-arm64-dts-rockchip-rk3399-Disable-debug-nodes.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0061-arm64-dts-rockchip-rk3399-Disable-debug-nodes.patch new file mode 100644 index 000000000..ff1a50ba1 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0061-arm64-dts-rockchip-rk3399-Disable-debug-nodes.patch @@ -0,0 +1,36 @@ +From c82f7cbc063dbc0867e635ac66b0a3b255c03340 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 4 Sep 2022 22:34:56 +0200 +Subject: [PATCH 61/84] arm64: dts: rockchip: rk3399: Disable debug nodes + +They hang boot with older levinboot, due to levinboot not enabling their PD. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 809a83d59f3cf8a98ff00640c0f2fdca5bde4d54) +--- + arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +index 49c68fe65de6..4b81ac05fe44 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +@@ -479,6 +479,7 @@ usb_host1_ohci: usb@fe3e0000 { + status = "disabled"; + }; + ++ /* + debug@fe430000 { + compatible = "arm,coresight-cpu-debug", "arm,primecell"; + reg = <0 0xfe430000 0 0x1000>; +@@ -526,6 +527,7 @@ debug@fe710000 { + clock-names = "apb_pclk"; + cpu = <&cpu_b1>; + }; ++ */ + + usbdrd3_0: usb@fe800000 { + compatible = "rockchip,rk3399-dwc3"; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0062-arm64-dts-rockchip-rk3399-Add-reboot-mode-driver.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0062-arm64-dts-rockchip-rk3399-Add-reboot-mode-driver.patch new file mode 100644 index 000000000..8f4b85be6 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0062-arm64-dts-rockchip-rk3399-Add-reboot-mode-driver.patch @@ -0,0 +1,36 @@ +From d8e9516106835ed6c44c8feae5ea65e2f51f8803 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 4 Sep 2022 22:49:46 +0200 +Subject: [PATCH 62/84] arm64: dts: rockchip: rk3399: Add reboot mode driver + +U-Boot uses value 0xef08a53c to jump back to USB loader maskrom +mode instead of normal boot. Add support for reboot to maskrom +from the kernel. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 5c4b81ba4f37d81edd20000581625a76e724a205) +--- + arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +index 4b81ac05fe44..d1d124501759 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +@@ -1312,6 +1312,13 @@ pmu_io_domains: io-domains { + compatible = "rockchip,rk3399-pmu-io-voltage-domain"; + status = "disabled"; + }; ++ ++ reboot-mode { ++ compatible = "syscon-reboot-mode"; ++ offset = <0x300>; ++ mode-normal = <0>; ++ mode-maskrom = <0xef08a53c>; ++ }; + }; + + spi3: spi@ff350000 { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0063-arm64-dts-rockchip-rk3399-Power-cycle-the-USB3-PHY-o.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0063-arm64-dts-rockchip-rk3399-Power-cycle-the-USB3-PHY-o.patch new file mode 100644 index 000000000..5973199f1 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0063-arm64-dts-rockchip-rk3399-Power-cycle-the-USB3-PHY-o.patch @@ -0,0 +1,43 @@ +From 7eb7d7f09537f33afa30863269dc72460f20420f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Mon, 20 Jul 2026 17:53:56 +0200 +Subject: [PATCH 63/84] arm64: dts: rockchip: rk3399: Power cycle the USB3 PHY + on role change + +The Type-C PHY on RK3399 latches the plug orientation while it is being +powered on, and only while the USB3 OTG controller is held in reset, so +the controller driver has to power cycle it. + +The property is inert unless the port is actually in dual-role mode with +either an extcon or a usb-role-switch to report the cable state, so it +can be set at the SoC level. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit f1061493a904add60e12c268d8efa2fefd57a421) +--- + arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +index d1d124501759..c9ee3a20943b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +@@ -555,6 +555,7 @@ usbdrd_dwc3_0: usb@fe800000 { + phys = <&u2phy0_otg>, <&tcphy0_usb3>; + phy-names = "usb2-phy", "usb3-phy"; + phy_type = "utmi_wide"; ++ usb3-phy-power-cycle-on-role-change; + snps,dis_enblslpm_quirk; + snps,dis-u2-freeclk-exists-quirk; + snps,dis_u2_susphy_quirk; +@@ -591,6 +592,7 @@ usbdrd_dwc3_1: usb@fe900000 { + phys = <&u2phy1_otg>, <&tcphy1_usb3>; + phy-names = "usb2-phy", "usb3-phy"; + phy_type = "utmi_wide"; ++ usb3-phy-power-cycle-on-role-change; + snps,dis_enblslpm_quirk; + snps,dis-u2-freeclk-exists-quirk; + snps,dis_u2_susphy_quirk; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0064-arm64-dts-rockchip-rk3399-Add-dmc_opp_table.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0064-arm64-dts-rockchip-rk3399-Add-dmc_opp_table.patch new file mode 100644 index 000000000..8013c59c5 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0064-arm64-dts-rockchip-rk3399-Add-dmc_opp_table.patch @@ -0,0 +1,63 @@ +From 38b6000236d688997fa789a32006dee308870488 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 22 Nov 2024 23:41:48 +0100 +Subject: [PATCH 64/84] arm64: dts: rockchip: rk3399: Add dmc_opp_table + +This was removed in v6.12. Re-add it with original values. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit df46950af61c404d9b0251f674015e35f03471bd) +--- + arch/arm64/boot/dts/rockchip/rk3399.dtsi | 29 ++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +index 6bc1249d99e6..bb9205bebf9f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi +@@ -104,6 +104,31 @@ opp05 { + opp-microvolt = <1100000 1100000 1150000>; + }; + }; ++ ++ dmc_opp_table: opp-table-3 { ++ compatible = "operating-points-v2"; ++ ++ opp00 { ++ opp-hz = /bits/ 64 <328000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp01 { ++ opp-hz = /bits/ 64 <416000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp02 { ++ opp-hz = /bits/ 64 <666000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp03 { ++ opp-hz = /bits/ 64 <856000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp04 { ++ opp-hz = /bits/ 64 <928000000>; ++ opp-microvolt = <925000>; ++ }; ++ }; + }; + + &cpu_l0 { +@@ -130,6 +155,10 @@ &cpu_b1 { + operating-points-v2 = <&cluster1_opp>; + }; + ++&dmc { ++ operating-points-v2 = <&dmc_opp_table>; ++}; ++ + &gpu { + operating-points-v2 = <&gpu_opp_table>; + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0065-arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0065-arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch new file mode 100644 index 000000000..6a67a7bac --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0065-arm64-dts-rockchip-rk3399-s-Add-DMC-table.patch @@ -0,0 +1,63 @@ +From 92232266830ad5fddeb4cc74cef6eb9df7997df8 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 28 Nov 2024 11:16:31 +0100 +Subject: [PATCH 65/84] arm64: dts: rockchip: rk3399-s: Add DMC table + +This is used by Pinephone Pro. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 456dec0546a630cdf298b9931912c03a33bce84c) +--- + arch/arm64/boot/dts/rockchip/rk3399-s.dtsi | 29 ++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi +index e54f451af9f3..82b941720294 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-s.dtsi +@@ -92,6 +92,31 @@ opp05 { + opp-microvolt = <1100000 1100000 1150000>; + }; + }; ++ ++ dmc_opp_table: opp-table-3 { ++ compatible = "operating-points-v2"; ++ ++ opp00 { ++ opp-hz = /bits/ 64 <328000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp01 { ++ opp-hz = /bits/ 64 <416000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp02 { ++ opp-hz = /bits/ 64 <666000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp03 { ++ opp-hz = /bits/ 64 <856000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp04 { ++ opp-hz = /bits/ 64 <928000000>; ++ opp-microvolt = <925000>; ++ }; ++ }; + }; + + &cpu_l0 { +@@ -118,6 +143,10 @@ &cpu_b1 { + operating-points-v2 = <&cluster1_opp>; + }; + ++&dmc { ++ operating-points-v2 = <&dmc_opp_table>; ++}; ++ + &gpu { + operating-points-v2 = <&gpu_opp_table>; + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0066-arm64-dts-rockchip-rk3399-Number-the-CDN-DP-ports.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0066-arm64-dts-rockchip-rk3399-Number-the-CDN-DP-ports.patch new file mode 100644 index 000000000..752d07dcb --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0066-arm64-dts-rockchip-rk3399-Number-the-CDN-DP-ports.patch @@ -0,0 +1,54 @@ +From 2aa1ea8136426e790aa954ceacfb02e7fabcc52b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 22:43:47 +0200 +Subject: [PATCH 66/84] arm64: dts: rockchip: rk3399: Number the CDN DP ports + +Number the input port and add the output port. The binding requires both +port@0 (VOP input) and port@1 (output), but the node had only a single +unnamed input port, so every board that enables the DP fails validation: + + ports: 'port@0' is a required property + ports: 'port@1' is a required property + +The output is what a Type-C connector reaches when the DP is behind an +alternate mode, which no board could describe until now. Endpoint +numbering inside the input port is unchanged and nothing looks the ports +up by name, so boards that already enable the DP are unaffected. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit de379c02ca5cbb6763e47fd63b2b9c3e6e9053e3) +--- + arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +index c9ee3a20943b..3cb02ba78a77 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +@@ -622,7 +622,11 @@ cdn_dp: dp@fec00000 { + status = "disabled"; + + ports { +- dp_in: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ dp_in: port@0 { ++ reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + +@@ -636,6 +640,10 @@ dp_in_vopl: endpoint@1 { + remote-endpoint = <&vopl_out_dp>; + }; + }; ++ ++ dp_out: port@1 { ++ reg = <1>; ++ }; + }; + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0067-arm64-dts-rockchip-rk3399-pinephone-pro-Add-Type-C-p.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0067-arm64-dts-rockchip-rk3399-pinephone-pro-Add-Type-C-p.patch new file mode 100644 index 000000000..736cffe1d --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0067-arm64-dts-rockchip-rk3399-pinephone-pro-Add-Type-C-p.patch @@ -0,0 +1,302 @@ +From b1c760223654de5c5a6233382a58c69b484fb003 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 19:20:33 +0200 +Subject: [PATCH 67/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add Type-C + port support + +Pinephone Pro has: + +- FUSB302 Type-C port controller on i2c4, doing CC detection and PD +- dual role data on the port, USB 2.0 and SuperSpeed +- DisplayPort alternate mode out of the same connector +- a 5V boost for sourcing VBUS when the port is a host + +It is also the port the phone charges from. + +Let's enable it. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 4671fc6d1131e0030f6789c6b4c8ca3662d71f93) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 196 ++++++++++++++++++ + 1 file changed, 196 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 8d26bd9b7500..159ccc47d6fd 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include "rk3399-s.dtsi" + + / { +@@ -114,6 +115,50 @@ avdd2v8_dvp: regulator-avdd2v8-dvp { + vin-supply = <&vcc3v3_sys>; + }; + ++ vcc5v0_sys: regulator-vcc5v0-host { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_sys"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&boost_otg>; ++ ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ /* ++ * GPIO0_A6 is used to force enable the VCC_SYS <-> VBAT+ Q7 switch that ++ * helps boost the RK818's power path's current carrying capacity when ++ * operating the phone from the battery in case VBUS_TYPEC has 5V ++ * provided by the phone itself and not by the external power supply. ++ * (this happens in USB host mode) ++ * ++ * GPIO0_A6 low: Q7 is enabled if there's 0V on VBUS_TYPEC (phone is not ++ * powered from external power supply) ++ * GPIO0_A6 high: Q7 is enabled no matter what ++ * ++ * GPIO0_A6 must be high when the phone is providing VBUS_TYPEC power. ++ * This keeps the power path boost enabled for all cases when the phone ++ * runs from the battery. ++ * ++ * In other words, GPIO0_A6 is used to override the automatic disabling ++ * of Q7 when VBUS_TYPEC has 5V in USB host mode. ++ */ ++ vcc5v0_typec: regulator-vcc5v0-typec { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc5v0_typec_en>; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-name = "vcc5v0_typec"; ++ vin-supply = <&boost_otg>; ++ }; ++ + vcc3v3_sys: regulator-vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; +@@ -237,6 +282,17 @@ &cpu_b1 { + cpu-supply = <&vdd_cpu_b>; + }; + ++&cdn_dp { ++ status = "okay"; ++ phys = <&tcphy0_dp>; ++}; ++ ++&dp_out { ++ cdn_dp_out: endpoint { ++ remote-endpoint = <&tcphy0_dp_in>; ++ }; ++}; ++ + &emmc_phy { + status = "okay"; + }; +@@ -272,6 +328,7 @@ rk818: pmic@1c { + vcc7-supply = <&vcc3v3_sys>; + vcc8-supply = <&vcc_sys>; + vcc9-supply = <&vcc3v3_sys>; ++ usb-supply = <&vcc5v0_typec>; + + regulators { + vdd_cpu_l: DCDC_REG1 { +@@ -402,6 +459,21 @@ regulator-state-mem { + regulator-on-in-suspend; + }; + }; ++ ++ boost_otg: DCDC_BOOST { ++ regulator-name = "boost_otg"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ otg_switch: OTG_SWITCH { ++ regulator-name = "otg_switch"; ++ }; + }; + }; + +@@ -570,6 +642,61 @@ af8133j: compass@1c { + }; + }; + ++&i2c4 { ++ fusb0: typec-portc@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio1>; ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&fusb0_int>; ++ extcon = <&u2phy0>; ++ vbus-supply = <&otg_switch>; ++ ++ connector { ++ compatible = "usb-c-connector"; ++ data-role = "dual"; ++ label = "USB-C"; ++ op-sink-microwatt = <2500000>; ++ power-role = "dual"; ++ sink-pdos = ; ++ source-pdos = ; ++ try-power-role = "sink"; ++ displayport = <&cdn_dp>; ++ ++ altmodes { ++ displayport { ++ svid = /bits/ 16 <0xff01>; ++ vdo = <0x0c46>; ++ }; ++ }; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ ++ usbc_hs: endpoint { ++ remote-endpoint = ++ <&dwc3_0_role_switch>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ usbc_ss: endpoint { ++ remote-endpoint = ++ <&tcphy0_typec>; ++ }; ++ }; ++ }; ++ }; ++ }; ++}; ++ + &io_domains { + bt656-supply = <&vcc1v8_dvp>; + audio-supply = <&vcca1v8_codec>; +@@ -688,6 +815,12 @@ compass_rst_l: compass-rst-l { + }; + }; + ++ fusb302x { ++ fusb0_int: fusb0-int { ++ rockchip,pins = <1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ + leds { + red_led_pin: red-led-pin { + rockchip,pins = <4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; +@@ -740,6 +873,12 @@ wifi_host_wake_l: wifi-host-wake-l { + }; + }; + ++ usb-typec { ++ vcc5v0_typec_en: vcc5v0_typec_en { ++ rockchip,pins = <0 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ + wireless-bluetooth { + bt_wake_pin: bt-wake-pin { + rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; +@@ -821,12 +960,45 @@ flash@0 { + }; + }; + ++&tcphy0 { ++ mode-switch; ++ orientation-switch; ++ status = "okay"; ++ ++ port { ++ tcphy0_typec: endpoint { ++ remote-endpoint = <&usbc_ss>; ++ }; ++ }; ++}; ++ ++&tcphy0_dp { ++ port { ++ tcphy0_dp_in: endpoint { ++ remote-endpoint = <&cdn_dp_out>; ++ }; ++ }; ++}; ++ + &tsadc { + rockchip,hw-tshut-mode = <1>; + rockchip,hw-tshut-polarity = <1>; + status = "okay"; + }; + ++&u2phy0 { ++ status = "okay"; ++}; ++ ++&u2phy0_otg { ++ status = "okay"; ++}; ++ ++&u2phy0_host { ++ status = "okay"; ++ phy-supply = <&vcc5v0_sys>; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; +@@ -852,6 +1024,30 @@ &uart2 { + status = "okay"; + }; + ++&usb_host0_ehci { ++ status = "okay"; ++}; ++ ++&usb_host0_ohci { ++ status = "okay"; ++}; ++ ++&usbdrd3_0 { ++ status = "okay"; ++}; ++ ++&usbdrd_dwc3_0 { ++ dr_mode = "otg"; ++ usb-role-switch; ++ status = "okay"; ++ ++ port { ++ dwc3_0_role_switch: endpoint { ++ remote-endpoint = <&usbc_hs>; ++ }; ++ }; ++}; ++ + &vopb { + status = "okay"; + assigned-clocks = <&cru DCLK_VOP0_DIV>, <&cru DCLK_VOP0>, +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0068-arm64-dts-rockchip-rk3399-pinephone-pro-Add-internal.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0068-arm64-dts-rockchip-rk3399-pinephone-pro-Add-internal.patch new file mode 100644 index 000000000..f54bd80e5 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0068-arm64-dts-rockchip-rk3399-pinephone-pro-Add-internal.patch @@ -0,0 +1,71 @@ +From 02ced21a4dee8dcb0ecd639e940c19b32162fffe Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 4 Sep 2022 23:11:02 +0200 +Subject: [PATCH 68/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add + internal display support + +Touch screen improvememtns in suspend. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit f9f2c2082f1c935a147456f2f111b0427b4fd2b3) +--- + .../boot/dts/rockchip/rk3399-pinephone-pro.dts | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 159ccc47d6fd..3f78611bfd1a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -231,6 +231,8 @@ vcc1v8_lcd: regulator-vcc1v8-lcd { + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc3v3_sys>; + gpio = <&gpio3 RK_PA5 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&display_pwren1>; + }; + + /* MIPI DSI panel 2.8v supply */ +@@ -242,6 +244,8 @@ vcc2v8_lcd: regulator-vcc2v8-lcd { + regulator-max-microvolt = <2800000>; + vin-supply = <&vcc3v3_sys>; + gpio = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&display_pwren>; + }; + + vibrator { +@@ -385,6 +389,9 @@ vcc3v0_touch: LDO_REG2 { + regulator-name = "vcc3v0_touch"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; + }; + + vcca1v8_codec: LDO_REG3 { +@@ -591,6 +598,8 @@ touchscreen@14 { + VDDIO-supply = <&vcc3v0_touch>; + touchscreen-size-x = <720>; + touchscreen-size-y = <1440>; ++ use-dt-irq-flags; ++ poweroff-in-suspend; + }; + + light-sensor@48 { +@@ -795,6 +804,12 @@ lcd { + lcd1_rst_pin: lcd1-rst-pin { + rockchip,pins = <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; + }; ++ display_pwren: display-pwren { ++ rockchip,pins = <3 RK_PA1 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ display_pwren1: display-pwren1 { ++ rockchip,pins = <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; + }; + + cameras { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0069-arm64-dts-rockchip-rk3399-pinephone-pro-Add-battery-.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0069-arm64-dts-rockchip-rk3399-pinephone-pro-Add-battery-.patch new file mode 100644 index 000000000..e09bcb1c5 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0069-arm64-dts-rockchip-rk3399-pinephone-pro-Add-battery-.patch @@ -0,0 +1,178 @@ +From 49d86cb04330d0fa36e3d6762022f9dbe9f0fcab Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 21 Jul 2026 19:20:54 +0200 +Subject: [PATCH 69/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add battery + charger support + +Describe Pinephone battery and battery charger. Battery has internal +NTC R25=3kOhm. Table was generated by aproximated beta model constant +as measured in my fridge. :) + +The input current limit comes from the Type-C port controller, so the +charger takes fusb302 as its power supply. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 7bb2b9094fda320593c8f9eecb961b1758fea928) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 138 ++++++++++++++++++ + 1 file changed, 138 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 3f78611bfd1a..e7577f92cb44 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -51,6 +51,44 @@ button-down { + }; + }; + ++ bat: battery { ++ compatible = "simple-battery"; ++ device-chemistry = "lithium-ion-polymer"; ++ voltage-min-design-microvolt = <3400000>; ++ voltage-max-design-microvolt = <4350000>; ++ energy-full-design-microwatt-hours = <10290400>; ++ charge-full-design-microamp-hours = <2708000>; ++ precharge-current-microamp = <120000>; ++ charge-term-current-microamp = <150000>; ++ constant-charge-current-max-microamp = <1500000>; ++ constant-charge-voltage-max-microvolt = <4300000>; ++ factory-internal-resistance-micro-ohms = <150000>; ++ resistance-temp-table = <20 150>; ++ ocv-capacity-celsius = <20>; ++ ocv-capacity-table-0 = ++ <4308000 100>, ++ <4217000 95>, ++ <4161000 90>, ++ <4110000 85>, ++ <4066000 80>, ++ <4007000 75>, ++ <3974000 70>, ++ <3937000 65>, ++ <3896000 60>, ++ <3853000 55>, ++ <3827000 50>, ++ <3807000 45>, ++ <3793000 40>, ++ <3780000 35>, ++ <3768000 30>, ++ <3756000 25>, ++ <3740000 20>, ++ <3716000 15>, ++ <3689000 10>, ++ <3675000 5>, ++ <3400000 0>; ++ }; ++ + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm0 0 50000 0>; +@@ -482,6 +520,106 @@ otg_switch: OTG_SWITCH { + regulator-name = "otg_switch"; + }; + }; ++ ++ charger { ++ compatible = "rockchip,rk818-charger"; ++ power-supplies = <&fusb0>; ++ monitored-battery = <&bat>; ++ ++ /* ++ * Vendor-specific tunables that do not map to the ++ * standard simple-battery binding. ++ */ ++ rockchip,sense-resistor-micro-ohms = <10000>; ++ rockchip,relax-entry-current-microamp = <300000>; ++ rockchip,relax-exit-current-microamp = <300000>; ++ rockchip,max-soc-offset-percent = <60>; ++ rockchip,feedback-temp-celsius = <105>; ++ rockchip,monitor-sec = <5>; ++ rockchip,ntc-table = < ++ (-20) 26459 ++ (-19) 25009 ++ (-18) 23649 ++ (-17) 22372 ++ (-16) 21174 ++ (-15) 20048 ++ (-14) 18990 ++ (-13) 17996 ++ (-12) 17060 ++ (-11) 16180 ++ (-10) 15352 ++ (-9) 14571 ++ (-8) 13836 ++ (-7) 13143 ++ (-6) 12489 ++ (-5) 11873 ++ (-4) 11291 ++ (-3) 10742 ++ (-2) 10223 ++ (-1) 9733 ++ 0 9269 ++ 1 8831 ++ 2 8416 ++ 3 8024 ++ 4 7653 ++ 5 7301 ++ 6 6968 ++ 7 6652 ++ 8 6353 ++ 9 6069 ++ 10 5800 ++ 11 5544 ++ 12 5301 ++ 13 5071 ++ 14 4852 ++ 15 4644 ++ 16 4446 ++ 17 4258 ++ 18 4079 ++ 19 3909 ++ 20 3747 ++ 21 3593 ++ 22 3446 ++ 23 3306 ++ 24 3172 ++ 25 3045 ++ 26 2924 ++ 27 2808 ++ 28 2697 ++ 29 2592 ++ 30 2491 ++ 31 2395 ++ 32 2304 ++ 33 2216 ++ 34 2132 ++ 35 2052 ++ 36 1975 ++ 37 1902 ++ 38 1832 ++ 39 1765 ++ 40 1700 ++ 41 1639 ++ 42 1580 ++ 43 1523 ++ 44 1469 ++ 45 1417 ++ 46 1368 ++ 47 1320 ++ 48 1274 ++ 49 1230 ++ 50 1188 ++ 51 1148 ++ 52 1109 ++ 53 1072 ++ 54 1036 ++ 55 1001 ++ 56 968 ++ 57 937 ++ 58 906 ++ 59 877 ++ 60 848 ++ >; ++ }; + }; + + vdd_cpu_b: regulator@40 { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0070-arm64-dts-rockchip-rk3399-pinephone-pro-Add-sound-su.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0070-arm64-dts-rockchip-rk3399-pinephone-pro-Add-sound-su.patch new file mode 100644 index 000000000..7096f8536 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0070-arm64-dts-rockchip-rk3399-pinephone-pro-Add-sound-su.patch @@ -0,0 +1,194 @@ +From af417cc22fa4dcefc192cb0390b287380e7c0d55 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 4 Sep 2022 23:25:19 +0200 +Subject: [PATCH 70/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add sound + support + +Add support for Pinephone Pro sound card and codec. + +It also adds a jack detection GPIO to the sound card definition, and +creates a new set of ADC keys for handling headset buttons. + +Co-developed-by: Arnaud Ferraris +Signed-off-by: Arnaud Ferraris +Signed-off-by: Ondrej Jirman +(cherry picked from commit 77c8d97a95e0e4c12ea6e32fe82f1d8eab8997d2) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 117 ++++++++++++++++++ + 1 file changed, 117 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index e7577f92cb44..977b3d186a9a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -136,6 +136,70 @@ multi-led { + leds = <&led_red>, <&led_green>, <&led_blue>; + }; + ++ // in1 - digital mic daughhterboard ++ // in2 - headset mic ++ // in3 - modem output (muxed with mono) ++ // spol - earphone ++ // hpo - heaphones ++ // lout - modem input ++ // spaker - amp enabled by SPK_CTL_H ++ // ++ // mclk - GPIO4_A0/I2S_CLK ++ ++ rt5640-sound { ++ compatible = "simple-audio-card"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hp_det>; ++ simple-audio-card,name = "PinePhonePro"; ++ simple-audio-card,aux-devs = <&speaker_amp>; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,hp-det-gpio = <&gpio4 RK_PD4 GPIO_ACTIVE_LOW>; ++ simple-audio-card,pin-switches = "Internal Speaker"; ++ ++ simple-audio-card,widgets = ++ "Microphone", "Headset Microphone", ++ "Microphone", "Internal Microphone", ++ "Headphone", "Headphones", ++ "Speaker", "Internal Earpiece", ++ "Speaker", "Internal Speaker", ++ "Line", "Line In Modem", ++ "Line", "Line Out Modem"; ++ ++ simple-audio-card,routing = ++ "Headphones", "HPOL", ++ "Headphones", "HPOR", ++ "Internal Earpiece", "SPOLP", ++ "Internal Earpiece", "SPOLN", ++ "Internal Speaker", "Speaker Amp OUTL", ++ "Internal Speaker", "Speaker Amp OUTR", ++ "Speaker Amp INL", "HPOL", ++ "Speaker Amp INR", "HPOR", ++ "DMIC1", "Internal Microphone", ++ "Headset Microphone", "MICBIAS1", ++ "IN2P", "Headset Microphone", ++ "Line Out Modem", "LOUTL", ++ "Line Out Modem", "LOUTR", ++ "IN3P", "Line In Modem", ++ "IN3N", "Line In Modem"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s0>; ++ }; ++ ++ simple-audio-card,codec { ++ sound-dai = <&rt5640>; ++ }; ++ }; ++ ++ speaker_amp: audio-amplifier { ++ compatible = "simple-audio-amplifier"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spk_en>; ++ enable-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; ++ sound-name-prefix = "Speaker Amp"; ++ }; ++ + vcc_sys: regulator-vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; +@@ -224,6 +288,8 @@ vcc1v8_codec: regulator-vcc1v8-codec { + pinctrl-names = "default"; + pinctrl-0 = <&vcc1v8_codec_en>; + regulator-name = "vcc1v8_codec"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc3v3_sys>; +@@ -419,8 +485,13 @@ regulator-state-mem { + + vcca3v0_codec: LDO_REG1 { + regulator-name = "vcca3v0_codec"; ++ regulator-always-on; ++ regulator-boot-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; + }; + + vcc3v0_touch: LDO_REG2 { +@@ -438,6 +509,9 @@ vcca1v8_codec: LDO_REG3 { + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; + }; + + rk818_pwr_on: LDO_REG4 { +@@ -789,6 +863,34 @@ af8133j: compass@1c { + }; + }; + ++&i2c1 { ++ status = "okay"; ++ ++ clock-frequency = <400000>; ++ i2c-scl-rising-time-ns = <300>; ++ i2c-scl-falling-time-ns = <15>; ++ // XXX: modem codec supplies: ++ // - vcc1v8_codec ++ // - vcca3v0_codec ++ ++ // supplies: (always on currently) ++ // - vcca3v0_codec ++ // - vcca1v8_codec ++ // - vcc5v0_sys - spk_vcc ++ rt5640: rt5640@1c { ++ compatible = "realtek,rt5640"; ++ reg = <0x1c>; ++ clocks = <&cru SCLK_I2S_8CH_OUT>; ++ clock-names = "mclk"; ++ assigned-clocks = <&cru SCLK_I2S0_8CH>; ++ assigned-clock-rates = <11289599>; ++ realtek,dmic1-data-pin = <1>; ++ realtek,in3-differential; ++ realtek,lout-differential; ++ #sound-dai-cells = <0>; ++ }; ++}; ++ + &i2c4 { + fusb0: typec-portc@22 { + compatible = "fcs,fusb302"; +@@ -844,6 +946,13 @@ usbc_ss: endpoint { + }; + }; + ++&i2s0 { ++ rockchip,playback-channels = <2>; ++ rockchip,capture-channels = <2>; ++ pinctrl-0 = <&i2s0_2ch_bus>; ++ status = "okay"; ++}; ++ + &io_domains { + bt656-supply = <&vcc1v8_dvp>; + audio-supply = <&vcca1v8_codec>; +@@ -1012,6 +1121,14 @@ sound { + vcc1v8_codec_en: vcc1v8-codec-en { + rockchip,pins = <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_down>; + }; ++ ++ hp_det: hp-det { ++ rockchip,pins = <4 RK_PD4 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ spk_en: spk-en { ++ rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; + }; + + stk3311 { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0071-arm64-dts-rockchip-rk3399-pinephone-pro-Add-modem-su.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0071-arm64-dts-rockchip-rk3399-pinephone-pro-Add-modem-su.patch new file mode 100644 index 000000000..2c6c22931 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0071-arm64-dts-rockchip-rk3399-pinephone-pro-Add-modem-su.patch @@ -0,0 +1,151 @@ +From 9e0e6397372096009e4c9c0b10447ab31c54f741 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 9 Sep 2022 16:39:58 +0200 +Subject: [PATCH 71/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add modem + support + +- Host USB for the modem +- Power supplies +- Power controller driver + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 88e10241f189011429c9587ac44eea465244b8c3) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 93 +++++++++++++++++++ + 1 file changed, 93 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 977b3d186a9a..07dfaa31c361 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -281,6 +281,30 @@ vcca1v8_s3: regulator-vcc1v8-s3 { + regulator-boot-on; + }; + ++ vcc_4g_5v: regulator-vcc-4g-5v { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio1 RK_PC7 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc_4g_5v_en>; ++ regulator-name = "vcc_4g_5v"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc5v0_sys>; ++ }; ++ ++ vcc_4g: regulator-vcc-4g { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vcc_4g_en>; ++ regulator-name = "vcc_4g"; ++ regulator-min-microvolt = <3800000>; ++ regulator-max-microvolt = <3800000>; ++ vin-supply = <&vcc_sys>; ++ }; ++ + vcc1v8_codec: regulator-vcc1v8-codec { + compatible = "regulator-fixed"; + enable-active-high; +@@ -1035,6 +1059,35 @@ &mipi_dsi1 { + status = "okay"; + }; + ++&uart3 { ++ status = "okay"; ++ ++ modem { ++ compatible = "quectel,eg25"; ++ char-device-name = "modem-power"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&modem_control_pins>; ++ ++ power-supply = <&vcc_4g>; ++ vbus-supply = <&vcc_4g_5v>; ++ ++ enable-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; // W_DISABLE# ++ reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; ++ status-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; ++ pwrkey-gpios = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>; ++ ++ host-ready-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; // apready ++ wakeup-gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_HIGH>; // ri ++ ++ dtr-gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; ++ cts-gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; ++ rts-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>; ++ ++ quectel,qdai = "3,0,0,4,0,0,1,1"; ++ }; ++}; ++ + &pmu_io_domains { + pmu1830-supply = <&vcc_1v8>; + status = "okay"; +@@ -1097,6 +1150,29 @@ blue_led_pin: blue-led-pin { + }; + }; + ++ modem { ++ vcc_4g_5v_en: vcc-4g-5v-en-pin { ++ rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ vcc_4g_en: vcc-4g-en-pin { ++ rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ modem_control_pins: modem-control-pins { ++ rockchip,pins = ++ <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>, ++ <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>, ++ <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>, ++ <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>, ++ <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>, ++ <0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>, ++ <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>, ++ <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>, ++ <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; +@@ -1269,6 +1345,15 @@ &u2phy0_host { + phy-supply = <&vcc5v0_sys>; + }; + ++&u2phy1 { ++ status = "okay"; ++}; ++ ++&u2phy1_host { ++ status = "okay"; ++ phy-supply = <&vcc5v0_sys>; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; +@@ -1302,6 +1387,14 @@ &usb_host0_ohci { + status = "okay"; + }; + ++&usb_host1_ehci { ++ status = "okay"; ++}; ++ ++&usb_host1_ohci { ++ status = "okay"; ++}; ++ + &usbdrd3_0 { + status = "okay"; + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0072-arm64-dts-rockchip-rk3399-pinephone-pro-Change-modem.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0072-arm64-dts-rockchip-rk3399-pinephone-pro-Change-modem.patch new file mode 100644 index 000000000..7f7266ea1 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0072-arm64-dts-rockchip-rk3399-pinephone-pro-Change-modem.patch @@ -0,0 +1,28 @@ +From bebc6b2cffeb6ab469434cb45aa631d38784ba0e Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 20 Feb 2024 18:31:33 +0100 +Subject: [PATCH 72/84] arm64: dts: rockchip: rk3399-pinephone-pro: Change + modem codec samplerate to 16kHz + +Signed-off-by: Ondrej Jirman +(cherry picked from commit b31904967a902b2dfea20b2b333c579ddd64c026) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 07dfaa31c361..7eed48fa1a7e 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -1084,7 +1084,7 @@ modem { + cts-gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; + rts-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>; + +- quectel,qdai = "3,0,0,4,0,0,1,1"; ++ quectel,qdai = "3,0,0,4,0,1,1,1"; + }; + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0073-arm64-dts-rockchip-rk3399-pinephone-pro-Add-light-pr.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0073-arm64-dts-rockchip-rk3399-pinephone-pro-Add-light-pr.patch new file mode 100644 index 000000000..e50aa7389 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0073-arm64-dts-rockchip-rk3399-pinephone-pro-Add-light-pr.patch @@ -0,0 +1,30 @@ +From 97d822b3e75c5652824bb46d18d2e1f69f795586 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 11 Jun 2023 19:39:10 +0200 +Subject: [PATCH 73/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add + light/proximity sensor support + +Pinephone Pro uses STK3311 + +Signed-off-by: Ondrej Jirman +(cherry picked from commit e7f1d9ee0452df59f4a2f7c201db6798c39b9945) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 7eed48fa1a7e..564cbe307d0c 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -846,6 +846,8 @@ light-sensor@48 { + pinctrl-names = "default"; + pinctrl-0 = <&light_int_l>; + proximity-near-level = <300>; ++ vdd-supply = <&vcc_3v0>; ++ leda-supply = <&vcc_3v0>; + }; + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0074-arm64-dts-rockchip-rk3399-pinephone-pro-Add-I2C-supp.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0074-arm64-dts-rockchip-rk3399-pinephone-pro-Add-I2C-supp.patch new file mode 100644 index 000000000..69e2a4b37 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0074-arm64-dts-rockchip-rk3399-pinephone-pro-Add-I2C-supp.patch @@ -0,0 +1,30 @@ +From e423b662f45bf3be22e5f4b2e4602d01bbc45a66 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 11 Jun 2023 19:39:57 +0200 +Subject: [PATCH 74/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add I2C + supply to stk3311 + +It is needed to communicate with the device, when other users of +vcc3v0_touch shut down the regulator. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 658fe4ad25378e4e8c030623e7bbe11eed74498f) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 564cbe307d0c..2a2568caf975 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -848,6 +848,7 @@ light-sensor@48 { + proximity-near-level = <300>; + vdd-supply = <&vcc_3v0>; + leda-supply = <&vcc_3v0>; ++ i2c-supply = <&vcc3v0_touch>; + }; + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0075-arm64-dts-rockchip-rk3399-pinephone-pro-Add-magnetom.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0075-arm64-dts-rockchip-rk3399-pinephone-pro-Add-magnetom.patch new file mode 100644 index 000000000..fa8b50b3e --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0075-arm64-dts-rockchip-rk3399-pinephone-pro-Add-magnetom.patch @@ -0,0 +1,36 @@ +From 15b7efa2d0b5c2f75542d5d91c2b914c3131cbbe Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 9 Sep 2022 16:42:07 +0200 +Subject: [PATCH 75/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add + magnetometer sensor support + +Pinephone Pro uses AF8133J. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 6aa4fc189489549c065a1474b42f52ab3ca9f034) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 2a2568caf975..927b481bd588 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -971,6 +971,14 @@ usbc_ss: endpoint { + }; + }; + }; ++ ++ af8133j: compass@1c { ++ compatible = "voltafield,af8133j"; ++ reg = <0x1c>; ++ reset-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_LOW>; ++ avdd-supply = <&vcc_3v0>; ++ dvdd-supply = <&vcc_1v8>; ++ }; + }; + + &i2s0 { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0076-arm64-dts-rockchip-rk3399-pinephone-pro-Add-mount-ma.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0076-arm64-dts-rockchip-rk3399-pinephone-pro-Add-mount-ma.patch new file mode 100644 index 000000000..bd0e701c8 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0076-arm64-dts-rockchip-rk3399-pinephone-pro-Add-mount-ma.patch @@ -0,0 +1,38 @@ +From 666714d7bfbf33f54beb6e263d4f40a6bf6c1eee Mon Sep 17 00:00:00 2001 +From: "Leonardo G. Trombetta" +Date: Mon, 13 Mar 2023 17:45:19 +0100 +Subject: [PATCH 76/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add + mount-matrix for magnetometer + +After experimenting with the magnetometer on the Pinephone Pro I found out the +mount-matrix currently reported by the AF8133J driver is just the identity +matrix (no rotation), which is wrong. It would seem to me that there is +currently no `mount-matrix` info and that's why the driver reports a trivial +answer. As far as I can tell from my tests, a 90 degree clockwise rotation in +the XY plane would align it properly. + +Signed-of-by: Leonardo G. Trombetta +(cherry picked from commit 16abfccfc65a9022c7478e75f35d69869eae076f) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 927b481bd588..fd1c04c3868a 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -978,6 +978,11 @@ af8133j: compass@1c { + reset-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_LOW>; + avdd-supply = <&vcc_3v0>; + dvdd-supply = <&vcc_1v8>; ++ ++ mount-matrix = ++ "0", "1", "0", ++ "-1", "0", "0", ++ "0", "0", "1"; + }; + }; + +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0077-arm64-dts-rockchip-rk3399-pinephone-pro-Enable-POGO-.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0077-arm64-dts-rockchip-rk3399-pinephone-pro-Enable-POGO-.patch new file mode 100644 index 000000000..2200282d7 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0077-arm64-dts-rockchip-rk3399-pinephone-pro-Enable-POGO-.patch @@ -0,0 +1,49 @@ +From 58499e1fac35c15529a15c7ebcd10234b50f612f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 9 Sep 2022 16:42:51 +0200 +Subject: [PATCH 77/84] arm64: dts: rockchip: rk3399-pinephone-pro: Enable POGO + pins I2C + +So that userspace apps can use it. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit a91c41c0dbfacd256b8beeafe28c0bfaa3202166) +--- + .../boot/dts/rockchip/rk3399-pinephone-pro.dts | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index fd1c04c3868a..bbc56d8d0c5b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -986,6 +986,14 @@ af8133j: compass@1c { + }; + }; + ++/* This bus is exposed on POGO pins */ ++&i2c5 { ++ i2c-scl-rising-time-ns = <450>; ++ i2c-scl-falling-time-ns = <15>; ++ pinctrl-0 = <&i2c5_xfer &pogo_int_pin>; ++ status = "okay"; ++}; ++ + &i2s0 { + rockchip,playback-channels = <2>; + rockchip,capture-channels = <2>; +@@ -1203,6 +1211,12 @@ vsel2_pin: vsel2-pin { + }; + }; + ++ pogo { ++ pogo_int_pin: pogo-int { ++ rockchip,pins = <3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ + sdio-pwrseq { + wifi_enable_h_pin: wifi-enable-h-pin { + rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0078-arm64-dts-rockchip-rk3399-pinephone-pro-Add-pinephon.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0078-arm64-dts-rockchip-rk3399-pinephone-pro-Add-pinephon.patch new file mode 100644 index 000000000..9bd378af4 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0078-arm64-dts-rockchip-rk3399-pinephone-pro-Add-pinephon.patch @@ -0,0 +1,187 @@ +From 74636887a5f0870ddd82a7e4608e131bab8b85e6 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Fri, 9 Sep 2022 16:43:39 +0200 +Subject: [PATCH 78/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add + pinephone keyboard support + +Keyboard can be connected over I2C. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit a1e59835ded8ecf3ac45f1bfd2f46420ab96d6e9) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 145 ++++++++++++++++++ + 1 file changed, 145 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index bbc56d8d0c5b..e1cd2f607fd3 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -89,6 +89,122 @@ bat: battery { + <3400000 0>; + }; + ++ ppkb_battery: keyboard-battery { ++ compatible = "simple-battery"; ++ voltage-min-design-microvolt = <3000000>; ++ voltage-max-design-microvolt = <4200000>; ++ energy-full-design-microwatt-hours = <21000000>; ++ charge-full-design-microamp-hours = <6000000>; ++ precharge-current-microamp = <100000>; ++ charge-term-current-microamp = <50000>; ++ constant-charge-current-max-microamp = <2000000>; ++ constant-charge-voltage-max-microvolt = <4200000>; ++ factory-internal-resistance-micro-ohms = <120000>; ++ ocv-capacity-celsius = <20>; ++ ocv-capacity-table-0 = ++ <4147328 100>, ++ <4132636 99>, ++ <4121720 98>, ++ <4110905 97>, ++ <4102063 96>, ++ <4092428 95>, ++ <4082371 94>, ++ <4074137 93>, ++ <4064172 92>, ++ <4056387 91>, ++ <4047198 90>, ++ <4038599 89>, ++ <4030933 88>, ++ <4021831 87>, ++ <4012613 86>, ++ <4003863 85>, ++ <3995132 84>, ++ <3986108 83>, ++ <3977434 82>, ++ <3967977 81>, ++ <3960286 80>, ++ <3951737 79>, ++ <3943518 78>, ++ <3935723 77>, ++ <3928692 76>, ++ <3920093 75>, ++ <3912676 74>, ++ <3905481 73>, ++ <3899881 72>, ++ <3892341 71>, ++ <3887140 70>, ++ <3880005 69>, ++ <3876060 68>, ++ <3869195 67>, ++ <3863234 66>, ++ <3857808 65>, ++ <3851464 64>, ++ <3846976 63>, ++ <3840724 62>, ++ <3835254 61>, ++ <3830946 60>, ++ <3826564 59>, ++ <3821275 58>, ++ <3817413 57>, ++ <3811941 56>, ++ <3808947 55>, ++ <3804959 54>, ++ <3800958 53>, ++ <3797447 52>, ++ <3794303 51>, ++ <3790341 50>, ++ <3787022 49>, ++ <3783436 48>, ++ <3779936 47>, ++ <3776176 46>, ++ <3772160 45>, ++ <3769348 44>, ++ <3766705 43>, ++ <3762481 42>, ++ <3760894 41>, ++ <3758396 40>, ++ <3755803 39>, ++ <3753543 38>, ++ <3749691 37>, ++ <3747026 36>, ++ <3743937 35>, ++ <3741872 34>, ++ <3738529 33>, ++ <3737528 32>, ++ <3734059 31>, ++ <3731586 30>, ++ <3728576 29>, ++ <3725939 28>, ++ <3721208 27>, ++ <3718239 26>, ++ <3713032 25>, ++ <3709785 24>, ++ <3704045 23>, ++ <3701203 22>, ++ <3695965 21>, ++ <3691074 20>, ++ <3684562 19>, ++ <3679140 18>, ++ <3670993 17>, ++ <3664243 16>, ++ <3655383 15>, ++ <3647666 14>, ++ <3642747 13>, ++ <3637775 12>, ++ <3632593 11>, ++ <3627120 10>, ++ <3620923 9>, ++ <3611465 8>, ++ <3597536 7>, ++ <3577366 6>, ++ <3545253 5>, ++ <3497377 4>, ++ <3430377 3>, ++ <3337780 2>, ++ <3183953 1>, ++ <3064061 0>; ++ }; ++ + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm0 0 50000 0>; +@@ -381,6 +497,15 @@ vibrator { + enable-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; + vcc-supply = <&vcc3v3_sys>; + }; ++ ++ keyboard-power { ++ compatible = "megi,pinephone-keyboard-power-manager"; ++ phone-battery = "rk818-battery"; ++ phone-usb = "rk818-usb"; ++ kb-battery = "ip5xxx-battery"; ++ kb-boost = "ip5xxx-boost"; ++ kb-usb = "ip5xxx-usb"; ++ }; + }; + + &cpu_alert0 { +@@ -992,6 +1117,26 @@ &i2c5 { + i2c-scl-falling-time-ns = <15>; + pinctrl-0 = <&i2c5_xfer &pogo_int_pin>; + status = "okay"; ++ ++ ppkb: keyboard@15 { ++ compatible = "pine64,pinephone-keyboard"; ++ reg = <0x15>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ vbat-supply = <&vcc5v0_sys>; ++ wakeup-source; ++ ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ charger@75 { ++ compatible = "injoinic,ip5209"; ++ reg = <0x75>; ++ monitored-battery = <&ppkb_battery>; ++ }; ++ }; ++ }; + }; + + &i2s0 { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0079-arm64-dts-rockchip-rk3399-pinephone-pro-Switch-LED-b.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0079-arm64-dts-rockchip-rk3399-pinephone-pro-Switch-LED-b.patch new file mode 100644 index 000000000..5cbf93a4b --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0079-arm64-dts-rockchip-rk3399-pinephone-pro-Switch-LED-b.patch @@ -0,0 +1,42 @@ +From f8b352c3ea9ab947c39843aa8613f37eff53f2d0 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 30 Mar 2023 13:45:08 +0200 +Subject: [PATCH 79/84] arm64: dts: rockchip: rk3399-pinephone-pro: Switch LED + backlight PWM from 1kHz to 20kHz + +This gets rid of measurable backlight flicker, because LED driver's feedback +voltage reference lowpass filter is now driven above the cutoff frequency +of 5 kHz. + +Also adapt the brightness levels curve, to make sure the lowest setting +satisfies requirement to have duty cycle at least 250ns. Backlight LED +driver doesn't react to smaller pulses on the EN/PWM pin. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 42222d8aab5e43e8240985cb4724f503715a1054) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index e1cd2f607fd3..efd478d70d25 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -208,6 +208,14 @@ ppkb_battery: keyboard-battery { + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm0 0 50000 0>; ++ brightness-levels = ++ <0 250 360 470 580 690 810 949 1110 1294 1502 ++ 1737 1998 2289 2610 2964 3351 3774 4233 4731 ++ 5268 5847 6467 7133 7845 8604 9412 10271 11182 ++ 12146 13164 14239 15374 16568 17822 19140 20521 ++ 21969 23483 25068 26722 28447 30247 32121 34071 ++ 36099 38210 40400 42669 45026 47468 50000>; ++ default-brightness-level = <17>; + }; + + gpio-keys { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0080-arm64-dts-rockchip-rk3399-pinephone-pro-Pre-configur.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0080-arm64-dts-rockchip-rk3399-pinephone-pro-Pre-configur.patch new file mode 100644 index 000000000..ea347de40 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0080-arm64-dts-rockchip-rk3399-pinephone-pro-Pre-configur.patch @@ -0,0 +1,111 @@ +From 640b4b539e3e667d3ef50026eaddf090afda0541 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 21 May 2023 01:42:25 +0200 +Subject: [PATCH 80/84] arm64: dts: rockchip: rk3399-pinephone-pro: + Pre-configure DMC + +This enables dynamic DRAM reclocking. The node needs to be okayed +by U-Boot/firmware combo known to support it. Do not enable &dmc +by default. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit fbae8f31862e79cec310206acba287343d372d50) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 33 +++++++++++++++++++ + arch/arm64/boot/dts/rockchip/rk3399-t.dtsi | 29 ++++++++++++++++ + 2 files changed, 62 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index efd478d70d25..41eede2b7c60 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -558,6 +558,39 @@ cdn_dp_out: endpoint { + }; + }; + ++&dfi { ++ status = "okay"; ++}; ++ ++&dmc { ++ // This node can only be enabled by FDT patching from U-Boot that ++ // uses Rockchip TPL/TF-A. DMC only works with the downstream firmware. ++ // If you enabled this on mainline, your phone will lock up on boot. ++ //status = "okay"; ++ ++ center-supply = <&vdd_center>; ++ ++ rockchip,pd-idle-ns = <160>; ++ rockchip,sr-idle-ns = <10240>; ++ rockchip,sr-mc-gate-idle-ns = <40960>; ++ rockchip,srpd-lite-idle-ns = <61440>; ++ rockchip,standby-idle-ns = <81920>; ++ ++ rockchip,lpddr4_odt_dis_freq = <666000000>; ++ ++ rockchip,srpd-lite-idle-dis-freq-hz = <0>; ++ rockchip,standby-idle-dis-freq-hz = <928000000>; ++ rockchip,sr-mc-gate-idle-dis-freq-hz = <800000000>; ++ rockchip,pd-idle-dis-freq-hz = <800000000>; ++ rockchip,sr-idle-dis-freq-hz = <800000000>; ++}; ++ ++&dmc_opp_table { ++ opp00 { ++ opp-suspend; ++ }; ++}; ++ + &emmc_phy { + status = "okay"; + }; +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-t.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-t.dtsi +index 72989f03fcb1..20a9c1ce383d 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-t.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3399-t.dtsi +@@ -85,6 +85,31 @@ opp03 { + opp-microvolt = <975000 975000 1150000>; + }; + }; ++ ++ dmc_opp_table: opp-table-3 { ++ compatible = "operating-points-v2"; ++ ++ opp00 { ++ opp-hz = /bits/ 64 <328000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp01 { ++ opp-hz = /bits/ 64 <416000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp02 { ++ opp-hz = /bits/ 64 <666000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp03 { ++ opp-hz = /bits/ 64 <856000000>; ++ opp-microvolt = <900000>; ++ }; ++ opp04 { ++ opp-hz = /bits/ 64 <928000000>; ++ opp-microvolt = <925000>; ++ }; ++ }; + }; + + &cpu_l0 { +@@ -111,6 +136,10 @@ &cpu_b1 { + operating-points-v2 = <&cluster1_opp>; + }; + ++&dmc { ++ operating-points-v2 = <&dmc_opp_table>; ++}; ++ + &gpu { + operating-points-v2 = <&gpu_opp_table>; + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0081-arm64-dts-rockchip-rk3399-pinephone-pro-Improve-SPI-.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0081-arm64-dts-rockchip-rk3399-pinephone-pro-Improve-SPI-.patch new file mode 100644 index 000000000..e270f4aad --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0081-arm64-dts-rockchip-rk3399-pinephone-pro-Improve-SPI-.patch @@ -0,0 +1,37 @@ +From 22256b60a41f614c7491cda57f62622af230ec4b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Thu, 29 Jun 2023 16:28:08 +0200 +Subject: [PATCH 81/84] arm64: dts: rockchip: rk3399-pinephone-pro: Improve SPI + access speed + +From 300 KiB/s to 2MiB/s. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 9f2f299bd46901a703d16f39fca9b219dcc8b09c) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 41eede2b7c60..ece5473efcd6 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -1513,11 +1513,14 @@ &sdhci { + + &spi1 { + status = "okay"; ++ assigned-clocks = <&cru SCLK_SPI1>; ++ assigned-clock-rates = <100000000>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; +- spi-max-frequency = <10000000>; ++ m25p,fast-read; ++ spi-max-frequency = <50000000>; + vcc-supply = <&vcc_1v8>; + }; + }; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0082-arm64-dts-rockchip-rk3399-pinephone-pro-Assign-power.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0082-arm64-dts-rockchip-rk3399-pinephone-pro-Assign-power.patch new file mode 100644 index 000000000..d24818b34 --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0082-arm64-dts-rockchip-rk3399-pinephone-pro-Assign-power.patch @@ -0,0 +1,30 @@ +From 3e56a1d0de12219b746d8959a84259c65d070d0b Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 9 Sep 2025 08:31:21 +0200 +Subject: [PATCH 82/84] arm64: dts: rockchip: rk3399-pinephone-pro: Assign + power supplies to audio codec + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 4400cddfa923adb0183b157603581172137f6790) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index ece5473efcd6..489e005bf872 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -1077,6 +1077,10 @@ rt5640: rt5640@1c { + clock-names = "mclk"; + assigned-clocks = <&cru SCLK_I2S0_8CH>; + assigned-clock-rates = <11289599>; ++ avdd-supply = <&vcca3v0_codec>; ++ cpvdd-supply = <&vcca3v0_codec>; ++ spkvdd-supply = <&vcc5v0_sys>; ++ dbvdd-supply = <&vcca1v8_codec>; + realtek,dmic1-data-pin = <1>; + realtek,in3-differential; + realtek,lout-differential; +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0083-arm64-dts-rockchip-rk3399-pinephone-pro-Add-camera-f.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0083-arm64-dts-rockchip-rk3399-pinephone-pro-Add-camera-f.patch new file mode 100644 index 000000000..797de3ccd --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0083-arm64-dts-rockchip-rk3399-pinephone-pro-Add-camera-f.patch @@ -0,0 +1,75 @@ +From 6f77254d3715eec8649b422c06758be6965bc907 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Tue, 9 Sep 2025 10:17:32 +0200 +Subject: [PATCH 83/84] arm64: dts: rockchip: rk3399-pinephone-pro: Add camera + flash controller + +This exact controller is not present on the board, and the real control +interface is slightly different. Don't upstream this. + +It's good enough for experiments/basic lighting. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 56488ecd7be24b83bf8775c6ba5f01c9c061b42a) +--- + .../dts/rockchip/rk3399-pinephone-pro.dts | 23 +++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 489e005bf872..67b28f70b6f9 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -260,6 +260,21 @@ multi-led { + leds = <&led_red>, <&led_green>, <&led_blue>; + }; + ++ sgm3140: led-controller { ++ compatible = "sgmicro,sgm3140"; ++ vin-supply = <&vcc3v3_sys>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&flash_pins>; ++ enable-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>; ++ flash-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; ++ ++ sgm3140_flash: led { ++ function = LED_FUNCTION_FLASH; ++ color = ; ++ flash-max-timeout-us = <250000>; ++ }; ++ }; ++ + // in1 - digital mic daughhterboard + // in2 - headset mic + // in3 - modem output (muxed with mono) +@@ -935,6 +950,7 @@ wcam: camera@1a { + compatible = "sony,imx258-pdaf"; + reg = <0x1a>; + clocks = <&cru SCLK_CIF_OUT>; /* MIPI_MCLK0, derived from CIF_CLKO */ ++ flash-leds = <&sgm3140_flash>; + lens-focus = <&wcam_lens>; + orientation = <1>; /* V4L2_CAMERA_ORIENTATION_BACK */ + pinctrl-names = "default"; +@@ -942,6 +958,7 @@ wcam: camera@1a { + reset-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_LOW>; + rotation = <270>; + /* Note: both cameras also depend on vcca1v8_codec to power the I2C bus. */ ++ i2c-supply = <&vcca1v8_codec>; + vif-supply = <&vcc1v8_dvp>; + vana-supply = <&avdd2v8_dvp>; + vdig-supply = <&vcc1v2_dvp>; /* DVDD_DVP is the same as VCC1V2_DVP */ +@@ -1362,6 +1379,12 @@ green_led_pin: green-led-pin { + blue_led_pin: blue-led-pin { + rockchip,pins = <4 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>; + }; ++ ++ flash_pins: flash-pins { ++ rockchip,pins = ++ <1 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>, ++ <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; + }; + + modem { +-- +2.55.0 + diff --git a/device/community/linux-pine64-pinephonepro/megi_patches/0084-arm64-dts-rockchip-rk3399-pinephone-pro-Disable-inte.patch b/device/community/linux-pine64-pinephonepro/megi_patches/0084-arm64-dts-rockchip-rk3399-pinephone-pro-Disable-inte.patch new file mode 100644 index 000000000..80abed38a --- /dev/null +++ b/device/community/linux-pine64-pinephonepro/megi_patches/0084-arm64-dts-rockchip-rk3399-pinephone-pro-Disable-inte.patch @@ -0,0 +1,36 @@ +From 8e72c70536c1349eea588a8ddf1f7f9cd383112f Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 4 Jan 2026 23:54:58 +0100 +Subject: [PATCH 84/84] arm64: dts: rockchip: rk3399-pinephone-pro: Disable + interrupt GPIO for WiFi for the Pinephone Pro + +This breaks at least dev version of Pinephone Pro. Or use of Pinephone +Pro +with firmware configurations that don't use OOB interrupts. + +Signed-off-by: Ondrej Jirman +(cherry picked from commit 99623d100af024386f2ca07a55b9f0148e444bfd) +--- + arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +index 67b28f70b6f9..7a3fa599f541 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +@@ -1501,9 +1501,9 @@ &sdio0 { + brcmf: wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; +- interrupt-parent = <&gpio4>; +- interrupts = ; +- interrupt-names = "host-wake"; ++ //interrupt-parent = <&gpio4>; ++ //interrupts = ; ++ //interrupt-names = "host-wake"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_host_wake_l>; + }; +-- +2.55.0 +