From a3a98e123369e28ec4130e51d98b61c560e506d3 Mon Sep 17 00:00:00 2001 From: coolsnowwolf Date: Fri, 18 Apr 2025 22:37:21 +0800 Subject: [PATCH] rockchip: initial support for RK3576 UFS controller for 6.12 --- ...al-support-for-rk3576-ufs-controller.patch | 138 +++++ ...al-support-for-rk3576-ufs-controller.patch | 31 ++ ...al-support-for-rk3576-ufs-controller.patch | 110 ++++ ...al-support-for-rk3576-ufs-controller.patch | 58 ++ ...al-support-for-rk3576-ufs-controller.patch | 68 +++ ...al-support-for-rk3576-ufs-controller.patch | 515 ++++++++++++++++++ ...al-support-for-rk3576-ufs-controller.patch | 51 ++ 7 files changed, 971 insertions(+) create mode 100644 target/linux/rockchip/patches-6.12/311-01-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-02-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-03-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-04-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-05-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-06-v6.13-initial-support-for-rk3576-ufs-controller.patch create mode 100644 target/linux/rockchip/patches-6.12/311-07-v6.13-initial-support-for-rk3576-ufs-controller.patch diff --git a/target/linux/rockchip/patches-6.12/311-01-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-01-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..3bae9a834 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-01-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,138 @@ +Document Rockchip UFS host controller for RK3576 SoC. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Shawn Lin +--- + +Changes in v5: +- use ufshc for devicetree example suggested by Mani + +Changes in v4: +- properly describe reset-gpios + +Changes in v3: +- rename the file to rockchip,rk3576-ufshc.yaml +- add description for reset-gpios +- use rockchip,rk3576-ufshc as compatible + +Changes in v2: +- rename the file +- add reset-gpios + + .../bindings/ufs/rockchip,rk3576-ufshc.yaml | 105 +++++++++++++++++++++ + 1 file changed, 105 insertions(+) + create mode 100644 Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml + +diff --git a/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml b/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml +new file mode 100644 +index 0000000..7d6c038 +--- /dev/null ++++ b/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml +@@ -0,0 +1,105 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/ufs/rockchip,rk3576-ufshc.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Rockchip UFS Host Controller ++ ++maintainers: ++ - Shawn Lin ++ ++allOf: ++ - $ref: ufs-common.yaml ++ ++properties: ++ compatible: ++ const: rockchip,rk3576-ufshc ++ ++ reg: ++ maxItems: 5 ++ ++ reg-names: ++ items: ++ - const: hci ++ - const: mphy ++ - const: hci_grf ++ - const: mphy_grf ++ - const: hci_apb ++ ++ clocks: ++ maxItems: 4 ++ ++ clock-names: ++ items: ++ - const: core ++ - const: pclk ++ - const: pclk_mphy ++ - const: ref_out ++ ++ power-domains: ++ maxItems: 1 ++ ++ resets: ++ maxItems: 4 ++ ++ reset-names: ++ items: ++ - const: biu ++ - const: sys ++ - const: ufs ++ - const: grf ++ ++ reset-gpios: ++ maxItems: 1 ++ description: | ++ GPIO specifiers for host to reset the whole UFS device including PHY and ++ memory. This gpio is active low and should choose the one whose high output ++ voltage is lower than 1.5V based on the UFS spec. ++ ++required: ++ - compatible ++ - reg ++ - reg-names ++ - clocks ++ - clock-names ++ - interrupts ++ - power-domains ++ - resets ++ - reset-names ++ - reset-gpios ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ ufshc: ufshc@2a2d0000 { ++ compatible = "rockchip,rk3576-ufshc"; ++ reg = <0x0 0x2a2d0000 0x0 0x10000>, ++ <0x0 0x2b040000 0x0 0x10000>, ++ <0x0 0x2601f000 0x0 0x1000>, ++ <0x0 0x2603c000 0x0 0x1000>, ++ <0x0 0x2a2e0000 0x0 0x10000>; ++ reg-names = "hci", "mphy", "hci_grf", "mphy_grf", "hci_apb"; ++ clocks = <&cru ACLK_UFS_SYS>, <&cru PCLK_USB_ROOT>, <&cru PCLK_MPHY>, ++ <&cru CLK_REF_UFS_CLKOUT>; ++ clock-names = "core", "pclk", "pclk_mphy", "ref_out"; ++ interrupts = ; ++ power-domains = <&power RK3576_PD_USB>; ++ resets = <&cru SRST_A_UFS_BIU>, <&cru SRST_A_UFS_SYS>, <&cru SRST_A_UFS>, ++ <&cru SRST_P_UFS_GRF>; ++ reset-names = "biu", "sys", "ufs", "grf"; ++ reset-gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_LOW>; ++ }; ++ }; +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-02-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-02-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..2df0c3220 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-02-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,31 @@ +Add ROCKCHIP_SIP_SUSPEND_MODE to pass down parameters to Trusted Firmware +in order to decide suspend mode. Currently only add ROCKCHIP_SLEEP_PD_CONFIG +which teaches firmware to power down controllers or not. + +Signed-off-by: Shawn Lin +--- + +Changes in v5: None +Changes in v4: None +Changes in v3: None +Changes in v2: None + + include/soc/rockchip/rockchip_sip.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/include/soc/rockchip/rockchip_sip.h b/include/soc/rockchip/rockchip_sip.h +index c46a9ae..501ad1f 100644 +--- a/include/soc/rockchip/rockchip_sip.h ++++ b/include/soc/rockchip/rockchip_sip.h +@@ -6,6 +6,9 @@ + #ifndef __SOC_ROCKCHIP_SIP_H + #define __SOC_ROCKCHIP_SIP_H + ++#define ROCKCHIP_SIP_SUSPEND_MODE 0x82000003 ++#define ROCKCHIP_SLEEP_PD_CONFIG 0xff ++ + #define ROCKCHIP_SIP_DRAM_FREQ 0x82000008 + #define ROCKCHIP_SIP_CONFIG_DRAM_INIT 0x00 + #define ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE 0x01 +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-03-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-03-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..317ac91b0 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-03-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,110 @@ +From: Ulf Hansson + +For some usecases a consumer driver requires its device to remain power-on +from the PM domain perspective during runtime. Using dev PM qos along with +the genpd governors, doesn't work for this case as would potentially +prevent the device from being runtime suspended too. + +To support these usecases, let's introduce dev_pm_genpd_rpm_always_on() to +allow consumers drivers to dynamically control the behaviour in genpd for a +device that is attached to it. + +Signed-off-by: Ulf Hansson +Signed-off-by: Shawn Lin +--- + +Changes in v5: None +Changes in v4: None +Changes in v3: None +Changes in v2: None + + drivers/pmdomain/core.c | 34 ++++++++++++++++++++++++++++++++++ + include/linux/pm_domain.h | 7 +++++++ + 2 files changed, 41 insertions(+) + +diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c +index 5ede0f7..2ccfcb7 100644 +--- a/drivers/pmdomain/core.c ++++ b/drivers/pmdomain/core.c +@@ -692,6 +692,36 @@ bool dev_pm_genpd_get_hwmode(struct device *dev) + } + EXPORT_SYMBOL_GPL(dev_pm_genpd_get_hwmode); + ++/** ++ * dev_pm_genpd_rpm_always_on() - Control if the PM domain can be powered off. ++ * ++ * @dev: Device for which the PM domain may need to stay on for. ++ * @on: Value to set or unset for the condition. ++ * ++ * For some usecases a consumer driver requires its device to remain power-on ++ * from the PM domain perspective during runtime. This function allows the ++ * behaviour to be dynamically controlled for a device attached to a genpd. ++ * ++ * It is assumed that the users guarantee that the genpd wouldn't be detached ++ * while this routine is getting called. ++ * ++ * Return: Returns 0 on success and negative error values on failures. ++ */ ++int dev_pm_genpd_rpm_always_on(struct device *dev, bool on) ++{ ++ struct generic_pm_domain *genpd; ++ ++ genpd = dev_to_genpd_safe(dev); ++ if (!genpd) ++ return -ENODEV; ++ ++ genpd_lock(genpd); ++ dev_gpd_data(dev)->rpm_always_on = on; ++ genpd_unlock(genpd); ++ ++ return 0; ++} ++ + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) + { + unsigned int state_idx = genpd->state_idx; +@@ -863,6 +893,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, + if (!pm_runtime_suspended(pdd->dev) || + irq_safe_dev_in_sleep_domain(pdd->dev, genpd)) + not_suspended++; ++ ++ /* The device may need its PM domain to stay powered on. */ ++ if (to_gpd_data(pdd)->rpm_always_on) ++ return -EBUSY; + } + + if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) +diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h +index b637ec1..30186ad 100644 +--- a/include/linux/pm_domain.h ++++ b/include/linux/pm_domain.h +@@ -245,6 +245,7 @@ struct generic_pm_domain_data { + unsigned int default_pstate; + unsigned int rpm_pstate; + bool hw_mode; ++ bool rpm_always_on; + void *data; + }; + +@@ -277,6 +278,7 @@ ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev); + void dev_pm_genpd_synced_poweroff(struct device *dev); + int dev_pm_genpd_set_hwmode(struct device *dev, bool enable); + bool dev_pm_genpd_get_hwmode(struct device *dev); ++int dev_pm_genpd_rpm_always_on(struct device *dev, bool on); + + extern struct dev_power_governor simple_qos_governor; + extern struct dev_power_governor pm_domain_always_on_gov; +@@ -360,6 +362,11 @@ static inline bool dev_pm_genpd_get_hwmode(struct device *dev) + return false; + } + ++static inline int dev_pm_genpd_rpm_always_on(struct device *dev, bool on) ++{ ++ return -EOPNOTSUPP; ++} ++ + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) + #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) + #endif +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-04-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-04-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..f61678751 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-04-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,58 @@ +Inform firmware to keep the power domain on or off. + +Suggested-by: Ulf Hansson +Signed-off-by: Shawn Lin +--- + +Changes in v5: +- fix a compile warning + +Changes in v4: None +Changes in v3: None +Changes in v2: None + + drivers/pmdomain/rockchip/pm-domains.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c +index cb0f938..49842f1 100644 +--- a/drivers/pmdomain/rockchip/pm-domains.c ++++ b/drivers/pmdomain/rockchip/pm-domains.c +@@ -5,6 +5,7 @@ + * Copyright (c) 2015 ROCKCHIP, Co. Ltd. + */ + ++#include + #include + #include + #include +@@ -20,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -540,6 +542,7 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, + struct generic_pm_domain *genpd = &pd->genpd; + u32 pd_pwr_offset = pd->info->pwr_offset; + bool is_on, is_mem_on = false; ++ struct arm_smccc_res res; + + if (pd->info->pwr_mask == 0) + return; +@@ -567,6 +570,11 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, + genpd->name, is_on); + return; + } ++ ++ /* Inform firmware to keep this pd on or off */ ++ arm_smccc_smc(ROCKCHIP_SIP_SUSPEND_MODE, ROCKCHIP_SLEEP_PD_CONFIG, ++ pmu->info->pwr_offset + pd_pwr_offset, ++ pd->info->pwr_mask, on, 0, 0, 0, &res); + } + + static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-05-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-05-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..6f675eaea --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-05-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,68 @@ +These two APIs will be used by host driver if they need a different +HCE process. + +Signed-off-by: Shawn Lin +--- + +Changes in v5: None +Changes in v4: None +Changes in v3: None +Changes in v2: None + + drivers/ufs/core/ufshcd.c | 6 ++++-- + include/ufs/ufshcd.h | 2 ++ + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 24a32e2..9d1d56d 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -4039,7 +4039,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) + * + * Return: 0 on success, non-zero value on failure. + */ +-static int ufshcd_dme_reset(struct ufs_hba *hba) ++int ufshcd_dme_reset(struct ufs_hba *hba) + { + struct uic_command uic_cmd = { + .command = UIC_CMD_DME_RESET, +@@ -4053,6 +4053,7 @@ static int ufshcd_dme_reset(struct ufs_hba *hba) + + return ret; + } ++EXPORT_SYMBOL_GPL(ufshcd_dme_reset); + + int ufshcd_dme_configure_adapt(struct ufs_hba *hba, + int agreed_gear, +@@ -4078,7 +4079,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_configure_adapt); + * + * Return: 0 on success, non-zero value on failure. + */ +-static int ufshcd_dme_enable(struct ufs_hba *hba) ++int ufshcd_dme_enable(struct ufs_hba *hba) + { + struct uic_command uic_cmd = { + .command = UIC_CMD_DME_ENABLE, +@@ -4092,6 +4093,7 @@ static int ufshcd_dme_enable(struct ufs_hba *hba) + + return ret; + } ++EXPORT_SYMBOL_GPL(ufshcd_dme_enable); + + static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba) + { +diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h +index 3f68ae3e4..b9733dc 100644 +--- a/include/ufs/ufshcd.h ++++ b/include/ufs/ufshcd.h +@@ -1360,6 +1360,8 @@ extern int ufshcd_system_thaw(struct device *dev); + extern int ufshcd_system_restore(struct device *dev); + #endif + ++extern int ufshcd_dme_reset(struct ufs_hba *hba); ++extern int ufshcd_dme_enable(struct ufs_hba *hba); + extern int ufshcd_dme_configure_adapt(struct ufs_hba *hba, + int agreed_gear, + int adapt_val); +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-06-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-06-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..45af73ee3 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-06-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,515 @@ +RK3576 SoC contains a UFS controller, add initial support for it. +The features are: +(1) support UFS 2.0 features +(2) High speed up to HS-G3 +(3) 2RX-2TX lanes +(4) auto H8 entry and exit + +Software limitation: +(1) HCE procedure: enable controller->enable intr->dme_reset->dme_enable +(2) disable unipro timeout values before power mode change + +Signed-off-by: Shawn Lin +--- + +Changes in v5: +- use device_set_awake_path() and disable ref_out_clk in suspend +- remove pd_id from header +- recontruct ufs_rockchip_hce_enable_notify() to workaround hce enable + without using new quirk + +Changes in v4: +- deal with power domain of rpm and spm suggested by Ulf +- Fix typo and disable clks in ufs_rockchip_remove +- remove clk_disable_unprepare(host->ref_out_clk) from + ufs_rockchip_remove + +Changes in v3: +- reword Kconfig description +- elaborate more about controller in commit msg +- use rockchip,rk3576-ufshc for compatible +- remove useless header file +- remove inline for ufshcd_is_device_present +- use usleep_range instead +- remove initialization, reverse Xmas order +- remove useless varibles +- check vops for null +- other small fixes for err path +- remove pm_runtime_set_active +- fix the active and inactive reset-gpios logic +- fix rpm_lvl and spm_lvl to 5 and move to end of probe path +- remove unnecessary system PM callbacks +- use UFSHCI_QUIRK_DME_RESET_ENABLE_AFTER_HCE instead + of UFSHCI_QUIRK_BROKEN_HCE + +Changes in v2: None + + drivers/ufs/host/Kconfig | 12 ++ + drivers/ufs/host/Makefile | 1 + + drivers/ufs/host/ufs-rockchip.c | 368 ++++++++++++++++++++++++++++++++++++++++ + drivers/ufs/host/ufs-rockchip.h | 48 ++++++ + 4 files changed, 429 insertions(+) + create mode 100644 drivers/ufs/host/ufs-rockchip.c + create mode 100644 drivers/ufs/host/ufs-rockchip.h + +diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig +index 580c8d0..191fbd7 100644 +--- a/drivers/ufs/host/Kconfig ++++ b/drivers/ufs/host/Kconfig +@@ -142,3 +142,15 @@ config SCSI_UFS_SPRD + + Select this if you have UFS controller on Unisoc chipset. + If unsure, say N. ++ ++config SCSI_UFS_ROCKCHIP ++ tristate "Rockchip UFS host controller driver" ++ depends on SCSI_UFSHCD_PLATFORM && (ARCH_ROCKCHIP || COMPILE_TEST) ++ help ++ This selects the Rockchip specific additions to UFSHCD platform driver. ++ UFS host on Rockchip needs some vendor specific configuration before ++ accessing the hardware which includes PHY configuration and vendor ++ specific registers. ++ ++ Select this if you have UFS controller on Rockchip chipset. ++ If unsure, say N. +diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile +index 4573aea..2f97feb 100644 +--- a/drivers/ufs/host/Makefile ++++ b/drivers/ufs/host/Makefile +@@ -10,5 +10,6 @@ obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o + obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o + obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o + obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o ++obj-$(CONFIG_SCSI_UFS_ROCKCHIP) += ufs-rockchip.o + obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o + obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o +diff --git a/drivers/ufs/host/ufs-rockchip.c b/drivers/ufs/host/ufs-rockchip.c +new file mode 100644 +index 0000000..b087ce0 +--- /dev/null ++++ b/drivers/ufs/host/ufs-rockchip.c +@@ -0,0 +1,368 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Rockchip UFS Host Controller driver ++ * ++ * Copyright (C) 2024 Rockchip Electronics Co.Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "ufshcd-pltfrm.h" ++#include "ufs-rockchip.h" ++ ++static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba, ++ enum ufs_notify_change_status status) ++{ ++ int err = 0; ++ ++ if (status == POST_CHANGE) { ++ err = ufshcd_dme_reset(hba); ++ if (err) ++ return err; ++ ++ err = ufshcd_dme_enable(hba); ++ if (err) ++ return err; ++ ++ err = ufshcd_vops_phy_initialization(hba); ++ } ++ ++ return err; ++} ++ ++static void ufs_rockchip_set_pm_lvl(struct ufs_hba *hba) ++{ ++ hba->rpm_lvl = UFS_PM_LVL_5; ++ hba->spm_lvl = UFS_PM_LVL_5; ++} ++ ++static int ufs_rockchip_rk3576_phy_init(struct ufs_hba *hba) ++{ ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(PA_LOCAL_TX_LCC_ENABLE, 0x0), 0x0); ++ /* enable the mphy DME_SET cfg */ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x200, 0x0), 0x40); ++ for (int i = 0; i < 2; i++) { ++ /* Configuration M-TX */ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xaa, SEL_TX_LANE0 + i), 0x06); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xa9, SEL_TX_LANE0 + i), 0x02); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xad, SEL_TX_LANE0 + i), 0x44); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xac, SEL_TX_LANE0 + i), 0xe6); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xab, SEL_TX_LANE0 + i), 0x07); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x94, SEL_TX_LANE0 + i), 0x93); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x93, SEL_TX_LANE0 + i), 0xc9); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x7f, SEL_TX_LANE0 + i), 0x00); ++ /* Configuration M-RX */ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x12, SEL_RX_LANE0 + i), 0x06); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x11, SEL_RX_LANE0 + i), 0x00); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x1d, SEL_RX_LANE0 + i), 0x58); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x1c, SEL_RX_LANE0 + i), 0x8c); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x1b, SEL_RX_LANE0 + i), 0x02); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, SEL_RX_LANE0 + i), 0xf6); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, SEL_RX_LANE0 + i), 0x69); ++ } ++ /* disable the mphy DME_SET cfg */ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x200, 0x0), 0x00); ++ ++ ufs_sys_writel(host->mphy_base, 0x80, 0x08C); ++ ufs_sys_writel(host->mphy_base, 0xB5, 0x110); ++ ufs_sys_writel(host->mphy_base, 0xB5, 0x250); ++ ++ ufs_sys_writel(host->mphy_base, 0x03, 0x134); ++ ufs_sys_writel(host->mphy_base, 0x03, 0x274); ++ ++ ufs_sys_writel(host->mphy_base, 0x38, 0x0E0); ++ ufs_sys_writel(host->mphy_base, 0x38, 0x220); ++ ++ ufs_sys_writel(host->mphy_base, 0x50, 0x164); ++ ufs_sys_writel(host->mphy_base, 0x50, 0x2A4); ++ ++ ufs_sys_writel(host->mphy_base, 0x80, 0x178); ++ ufs_sys_writel(host->mphy_base, 0x80, 0x2B8); ++ ++ ufs_sys_writel(host->mphy_base, 0x18, 0x1B0); ++ ufs_sys_writel(host->mphy_base, 0x18, 0x2F0); ++ ++ ufs_sys_writel(host->mphy_base, 0x03, 0x128); ++ ufs_sys_writel(host->mphy_base, 0x03, 0x268); ++ ++ ufs_sys_writel(host->mphy_base, 0x20, 0x12C); ++ ufs_sys_writel(host->mphy_base, 0x20, 0x26C); ++ ++ ufs_sys_writel(host->mphy_base, 0xC0, 0x120); ++ ufs_sys_writel(host->mphy_base, 0xC0, 0x260); ++ ++ ufs_sys_writel(host->mphy_base, 0x03, 0x094); ++ ++ ufs_sys_writel(host->mphy_base, 0x03, 0x1B4); ++ ufs_sys_writel(host->mphy_base, 0x03, 0x2F4); ++ ++ ufs_sys_writel(host->mphy_base, 0xC0, 0x08C); ++ usleep_range(1, 2); ++ ufs_sys_writel(host->mphy_base, 0x00, 0x08C); ++ ++ usleep_range(200, 250); ++ /* start link up */ ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_TX_ENDIAN, 0), 0x0); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_RX_ENDIAN, 0), 0x0); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID_VALID, 0), 0x1); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_PEERDEVICEID, 0), 0x1); ++ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, 0), 0x1); ++ ++ return 0; ++} ++ ++static int ufs_rockchip_common_init(struct ufs_hba *hba) ++{ ++ struct device *dev = hba->dev; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct ufs_rockchip_host *host; ++ int err; ++ ++ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ /* system control register for hci */ ++ host->ufs_sys_ctrl = devm_platform_ioremap_resource_byname(pdev, "hci_grf"); ++ if (IS_ERR(host->ufs_sys_ctrl)) ++ return dev_err_probe(dev, PTR_ERR(host->ufs_sys_ctrl), ++ "cannot ioremap for hci system control register\n"); ++ ++ /* system control register for mphy */ ++ host->ufs_phy_ctrl = devm_platform_ioremap_resource_byname(pdev, "mphy_grf"); ++ if (IS_ERR(host->ufs_phy_ctrl)) ++ return dev_err_probe(dev, PTR_ERR(host->ufs_phy_ctrl), ++ "cannot ioremap for mphy system control register\n"); ++ ++ /* mphy base register */ ++ host->mphy_base = devm_platform_ioremap_resource_byname(pdev, "mphy"); ++ if (IS_ERR(host->mphy_base)) ++ return dev_err_probe(dev, PTR_ERR(host->mphy_base), ++ "cannot ioremap for mphy base register\n"); ++ ++ host->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(host->rst)) ++ return dev_err_probe(dev, PTR_ERR(host->rst), ++ "failed to get reset control\n"); ++ ++ reset_control_assert(host->rst); ++ usleep_range(1, 2); ++ reset_control_deassert(host->rst); ++ ++ host->ref_out_clk = devm_clk_get_enabled(dev, "ref_out"); ++ if (IS_ERR(host->ref_out_clk)) ++ return dev_err_probe(dev, PTR_ERR(host->ref_out_clk), ++ "ref_out unavailable\n"); ++ ++ host->rst_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(host->rst_gpio)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->rst_gpio), ++ "invalid reset-gpios property in node\n"); ++ ++ host->clks[0].id = "core"; ++ host->clks[1].id = "pclk"; ++ host->clks[2].id = "pclk_mphy"; ++ err = devm_clk_bulk_get_optional(dev, UFS_MAX_CLKS, host->clks); ++ if (err) ++ return dev_err_probe(dev, err, "failed to get clocks\n"); ++ ++ err = clk_bulk_prepare_enable(UFS_MAX_CLKS, host->clks); ++ if (err) ++ return dev_err_probe(dev, err, "failed to enable clocks\n"); ++ ++ host->hba = hba; ++ ++ ufshcd_set_variant(hba, host); ++ ++ return 0; ++} ++ ++static int ufs_rockchip_rk3576_init(struct ufs_hba *hba) ++{ ++ struct device *dev = hba->dev; ++ int ret; ++ ++ hba->quirks = UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING; ++ ++ /* Enable BKOPS when suspend */ ++ hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; ++ /* Enable putting device into deep sleep */ ++ hba->caps |= UFSHCD_CAP_DEEPSLEEP; ++ /* Enable devfreq of UFS */ ++ hba->caps |= UFSHCD_CAP_CLK_SCALING; ++ /* Enable WriteBooster */ ++ hba->caps |= UFSHCD_CAP_WB_EN; ++ ++ ret = ufs_rockchip_common_init(hba); ++ if (ret) ++ return dev_err_probe(dev, ret, "ufs common init fail\n"); ++ ++ return 0; ++} ++ ++static int ufs_rockchip_device_reset(struct ufs_hba *hba) ++{ ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ ++ /* Active the reset-gpios */ ++ gpiod_set_value_cansleep(host->rst_gpio, 1); ++ usleep_range(20, 25); ++ ++ /* Inactive the reset-gpios */ ++ gpiod_set_value_cansleep(host->rst_gpio, 0); ++ usleep_range(20, 25); ++ ++ return 0; ++} ++ ++static const struct ufs_hba_variant_ops ufs_hba_rk3576_vops = { ++ .name = "rk3576", ++ .init = ufs_rockchip_rk3576_init, ++ .device_reset = ufs_rockchip_device_reset, ++ .hce_enable_notify = ufs_rockchip_hce_enable_notify, ++ .phy_initialization = ufs_rockchip_rk3576_phy_init, ++}; ++ ++static const struct of_device_id ufs_rockchip_of_match[] = { ++ { .compatible = "rockchip,rk3576-ufshc", .data = &ufs_hba_rk3576_vops }, ++}; ++MODULE_DEVICE_TABLE(of, ufs_rockchip_of_match); ++ ++static int ufs_rockchip_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct ufs_hba_variant_ops *vops; ++ struct ufs_hba *hba; ++ int err; ++ ++ vops = device_get_match_data(dev); ++ if (!vops) ++ return dev_err_probe(dev, -EINVAL, "ufs_hba_variant_ops not defined.\n"); ++ ++ err = ufshcd_pltfrm_init(pdev, vops); ++ if (err) ++ return dev_err_probe(dev, err, "ufshcd_pltfrm_init failed\n"); ++ ++ hba = platform_get_drvdata(pdev); ++ /* Set the default desired pm level in case no users set via sysfs */ ++ ufs_rockchip_set_pm_lvl(hba); ++ ++ return 0; ++} ++ ++static void ufs_rockchip_remove(struct platform_device *pdev) ++{ ++ struct ufs_hba *hba = platform_get_drvdata(pdev); ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ ++ pm_runtime_forbid(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ ufshcd_remove(hba); ++ ufshcd_dealloc_host(hba); ++ clk_bulk_disable_unprepare(UFS_MAX_CLKS, host->clks); ++} ++ ++#ifdef CONFIG_PM ++static int ufs_rockchip_runtime_suspend(struct device *dev) ++{ ++ struct ufs_hba *hba = dev_get_drvdata(dev); ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ ++ clk_disable_unprepare(host->ref_out_clk); ++ ++ /* Shouldn't power down if rpm_lvl is less than level 5. */ ++ dev_pm_genpd_rpm_always_on(dev, hba->rpm_lvl < UFS_PM_LVL_5 ? true : false); ++ ++ return ufshcd_runtime_suspend(dev); ++} ++ ++static int ufs_rockchip_runtime_resume(struct device *dev) ++{ ++ struct ufs_hba *hba = dev_get_drvdata(dev); ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ int err; ++ ++ err = clk_prepare_enable(host->ref_out_clk); ++ if (err) { ++ dev_err(hba->dev, "failed to enable ref out clock %d\n", err); ++ return err; ++ } ++ ++ reset_control_assert(host->rst); ++ usleep_range(1, 2); ++ reset_control_deassert(host->rst); ++ ++ return ufshcd_runtime_resume(dev); ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int ufs_rockchip_system_suspend(struct device *dev) ++{ ++ struct ufs_hba *hba = dev_get_drvdata(dev); ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ int err; ++ ++ if (hba->spm_lvl < UFS_PM_LVL_5) ++ device_set_awake_path(dev); ++ ++ err = ufshcd_system_suspend(dev); ++ if (err) { ++ dev_err(hba->dev, "system susped failed %d\n", err); ++ return err; ++ } ++ ++ clk_disable_unprepare(host->ref_out_clk); ++ ++ return 0; ++} ++ ++static int ufs_rockchip_system_resume(struct device *dev) ++{ ++ struct ufs_hba *hba = dev_get_drvdata(dev); ++ struct ufs_rockchip_host *host = ufshcd_get_variant(hba); ++ int err; ++ ++ err = clk_prepare_enable(host->ref_out_clk); ++ if (err) { ++ dev_err(hba->dev, "failed to enable ref out clock %d\n", err); ++ return err; ++ } ++ ++ return ufshcd_system_resume(dev); ++} ++#endif ++ ++static const struct dev_pm_ops ufs_rockchip_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(ufs_rockchip_system_suspend, ufs_rockchip_system_resume) ++ SET_RUNTIME_PM_OPS(ufs_rockchip_runtime_suspend, ufs_rockchip_runtime_resume, NULL) ++ .prepare = ufshcd_suspend_prepare, ++ .complete = ufshcd_resume_complete, ++}; ++ ++static struct platform_driver ufs_rockchip_pltform = { ++ .probe = ufs_rockchip_probe, ++ .remove = ufs_rockchip_remove, ++ .driver = { ++ .name = "ufshcd-rockchip", ++ .pm = &ufs_rockchip_pm_ops, ++ .of_match_table = ufs_rockchip_of_match, ++ }, ++}; ++module_platform_driver(ufs_rockchip_pltform); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Rockchip UFS Host Driver"); +diff --git a/drivers/ufs/host/ufs-rockchip.h b/drivers/ufs/host/ufs-rockchip.h +new file mode 100644 +index 0000000..768dbe3 +--- /dev/null ++++ b/drivers/ufs/host/ufs-rockchip.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Rockchip UFS Host Controller driver ++ * ++ * Copyright (C) 2024 Rockchip Electronics Co.Ltd. ++ */ ++ ++#ifndef _UFS_ROCKCHIP_H_ ++#define _UFS_ROCKCHIP_H_ ++ ++#define UFS_MAX_CLKS 3 ++ ++#define SEL_TX_LANE0 0x0 ++#define SEL_TX_LANE1 0x1 ++#define SEL_TX_LANE2 0x2 ++#define SEL_TX_LANE3 0x3 ++#define SEL_RX_LANE0 0x4 ++#define SEL_RX_LANE1 0x5 ++#define SEL_RX_LANE2 0x6 ++#define SEL_RX_LANE3 0x7 ++ ++#define MIB_T_DBG_CPORT_TX_ENDIAN 0xc022 ++#define MIB_T_DBG_CPORT_RX_ENDIAN 0xc023 ++ ++struct ufs_rockchip_host { ++ struct ufs_hba *hba; ++ void __iomem *ufs_phy_ctrl; ++ void __iomem *ufs_sys_ctrl; ++ void __iomem *mphy_base; ++ struct gpio_desc *rst_gpio; ++ struct reset_control *rst; ++ struct clk *ref_out_clk; ++ struct clk_bulk_data clks[UFS_MAX_CLKS]; ++ uint64_t caps; ++}; ++ ++#define ufs_sys_writel(base, val, reg) \ ++ writel((val), (base) + (reg)) ++#define ufs_sys_readl(base, reg) readl((base) + (reg)) ++#define ufs_sys_set_bits(base, mask, reg) \ ++ ufs_sys_writel( \ ++ (base), ((mask) | (ufs_sys_readl((base), (reg)))), (reg)) ++#define ufs_sys_ctrl_clr_bits(base, mask, reg) \ ++ ufs_sys_writel((base), \ ++ ((~(mask)) & (ufs_sys_readl((base), (reg)))), \ ++ (reg)) ++ ++#endif /* _UFS_ROCKCHIP_H_ */ +-- +2.7.4 diff --git a/target/linux/rockchip/patches-6.12/311-07-v6.13-initial-support-for-rk3576-ufs-controller.patch b/target/linux/rockchip/patches-6.12/311-07-v6.13-initial-support-for-rk3576-ufs-controller.patch new file mode 100644 index 000000000..cc0bcd4e3 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/311-07-v6.13-initial-support-for-rk3576-ufs-controller.patch @@ -0,0 +1,51 @@ +Add ufshc node to rk3576.dtsi, so the board using UFS could +enable it. + +Signed-off-by: Shawn Lin +--- + +Changes in v5: None +Changes in v4: None +Changes in v3: None +Changes in v2: None + + arch/arm64/boot/dts/rockchip/rk3576.dtsi | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi +index 436232f..32beda2 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi +@@ -1110,6 +1110,30 @@ + }; + }; + ++ ufshc: ufshc@2a2d0000 { ++ compatible = "rockchip,rk3576-ufshc"; ++ reg = <0x0 0x2a2d0000 0 0x10000>, /* 0: HCI standard */ ++ <0x0 0x2b040000 0 0x10000>, /* 1: Mphy */ ++ <0x0 0x2601f000 0 0x1000>, /* 2: HCI Vendor specified */ ++ <0x0 0x2603c000 0 0x1000>, /* 3: Mphy Vendor specified */ ++ <0x0 0x2a2e0000 0 0x10000>; /* 4: HCI apb */ ++ reg-names = "hci", "mphy", "hci_grf", "mphy_grf", "hci_apb"; ++ clocks = <&cru ACLK_UFS_SYS>, <&cru PCLK_USB_ROOT>, <&cru PCLK_MPHY>, ++ <&cru CLK_REF_UFS_CLKOUT>; ++ clock-names = "core", "pclk", "pclk_mphy", "ref_out"; ++ assigned-clocks = <&cru CLK_REF_OSC_MPHY>; ++ assigned-clock-parents = <&cru CLK_REF_MPHY_26M>; ++ interrupts = ; ++ power-domains = <&power RK3576_PD_USB>; ++ pinctrl-0 = <&ufs_refclk>; ++ pinctrl-names = "default"; ++ resets = <&cru SRST_A_UFS_BIU>, <&cru SRST_A_UFS_SYS>, ++ <&cru SRST_A_UFS>, <&cru SRST_P_UFS_GRF>; ++ reset-names = "biu", "sys", "ufs", "grf"; ++ reset-gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_LOW>; ++ status = "disabled"; ++ }; ++ + sdmmc: mmc@2a310000 { + compatible = "rockchip,rk3576-dw-mshc"; + reg = <0x0 0x2a310000 0x0 0x4000>; +-- +2.7.4