diff options
27 files changed, 646 insertions, 277 deletions
diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 9a2432a88074..6a1e3d455897 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -209,6 +209,7 @@ properties: - qcom,gcc-msm8660 - qcom,kpss-acc-v1 - qcom,kpss-acc-v2 + - qcom,msm8226-smp - renesas,apmu - renesas,r9a06g032-smp - rockchip,rk3036-smp @@ -293,7 +294,7 @@ properties: Specifies the ACC* node associated with this CPU. Required for systems that have an "enable-method" property - value of "qcom,kpss-acc-v1" or "qcom,kpss-acc-v2" + value of "qcom,kpss-acc-v1", "qcom,kpss-acc-v2" or "qcom,msm8226-smp" * arm/msm/qcom,kpss-acc.txt diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index a7333ad938d2..d7e3cda8924e 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -13,8 +13,10 @@ Required properties: * "qcom,scm-ipq806x" * "qcom,scm-ipq8074" * "qcom,scm-mdm9607" + * "qcom,scm-msm8226" * "qcom,scm-msm8660" * "qcom,scm-msm8916" + * "qcom,scm-msm8953" * "qcom,scm-msm8960" * "qcom,scm-msm8974" * "qcom,scm-msm8994" @@ -33,7 +35,7 @@ Required properties: * core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and "qcom,scm-msm8960" * core, iface and bus clocks required for "qcom,scm-apq8084", - "qcom,scm-msm8916" and "qcom,scm-msm8974" + "qcom,scm-msm8916", "qcom,scm-msm8953" and "qcom,scm-msm8974" - clock-names: Must contain "core" for the core clock, "iface" for the interface clock and "bus" for the bus clock per the requirements of the compatible. - qcom,dload-mode: phandle to the TCSR hardware block and offset of the diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 239f37881cae..e810480e3eb7 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -19,6 +19,7 @@ properties: - qcom,mdm9607-rpmpd - qcom,msm8916-rpmpd - qcom,msm8939-rpmpd + - qcom,msm8953-rpmpd - qcom,msm8976-rpmpd - qcom,msm8994-rpmpd - qcom,msm8996-rpmpd @@ -31,6 +32,7 @@ properties: - qcom,sdm845-rpmhpd - qcom,sdx55-rpmhpd - qcom,sm6115-rpmpd + - qcom,sm6350-rpmhpd - qcom,sm8150-rpmhpd - qcom,sm8250-rpmhpd - qcom,sm8350-rpmhpd diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 93e4b737ee1b..1904612fad85 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -30,6 +30,7 @@ properties: - qcom,sc7280-aoss-qmp - qcom,sc8180x-aoss-qmp - qcom,sdm845-aoss-qmp + - qcom,sm6350-aoss-qmp - qcom,sm8150-aoss-qmp - qcom,sm8250-aoss-qmp - qcom,sm8350-aoss-qmp diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index cc3fe5ed7421..b32457c2fc0b 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -34,6 +34,7 @@ properties: - qcom,rpm-ipq6018 - qcom,rpm-msm8226 - qcom,rpm-msm8916 + - qcom,rpm-msm8953 - qcom,rpm-msm8974 - qcom,rpm-msm8976 - qcom,rpm-msm8996 @@ -41,6 +42,7 @@ properties: - qcom,rpm-sdm660 - qcom,rpm-sm6115 - qcom,rpm-sm6125 + - qcom,rpm-qcm2290 - qcom,rpm-qcs404 qcom,smd-channels: @@ -57,6 +59,7 @@ if: - qcom,rpm-apq8084 - qcom,rpm-msm8916 - qcom,rpm-msm8974 + - qcom,rpm-msm8953 then: required: - qcom,smd-channels diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml new file mode 100644 index 000000000000..d68c002527fa --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Subsystem Power Manager binding + +maintainers: + - Andy Gross <[email protected]> + - Bjorn Andersson <[email protected]> + +description: | + This binding describes the Qualcomm Subsystem Power Manager, used to control + the peripheral logic surrounding the application cores in Qualcomm platforms. + +properties: + compatible: + items: + - enum: + - qcom,sdm660-gold-saw2-v4.1-l2 + - qcom,sdm660-silver-saw2-v4.1-l2 + - qcom,msm8998-gold-saw2-v4.1-l2 + - qcom,msm8998-silver-saw2-v4.1-l2 + - qcom,msm8226-saw2-v2.1-cpu + - qcom,msm8974-saw2-v2.1-cpu + - qcom,apq8084-saw2-v2.1-cpu + - qcom,apq8064-saw2-v1.1-cpu + - const: qcom,saw2 + + reg: + description: Base address and size of the SPM register region + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + + /* Example 1: SoC using SAW2 and kpss-acc-v2 CPUIdle */ + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "qcom,kryo"; + device_type = "cpu"; + enable-method = "qcom,kpss-acc-v2"; + qcom,saw = <&saw0>; + reg = <0x0>; + operating-points-v2 = <&cpu_opp_table>; + }; + }; + + saw0: power-manager@f9089000 { + compatible = "qcom,msm8974-saw2-v2.1-cpu", "qcom,saw2"; + reg = <0xf9089000 0x1000>; + }; + + - | + + /* + * Example 2: New-gen multi cluster SoC using SAW only for L2; + * This does not require any cpuidle driver, nor any cpu phandle. + */ + power-manager@17812000 { + compatible = "qcom,msm8998-gold-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x17812000 0x1000>; + }; + + power-manager@17912000 { + compatible = "qcom,msm8998-silver-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x17912000 0x1000>; + }; + +... diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 630a038f4513..60496554c6dd 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -29,6 +29,7 @@ #define COREPOR_RST BIT(5) #define CORE_RST BIT(4) #define L2DT_SLP BIT(3) +#define CORE_MEM_CLAMP BIT(1) #define CLAMP BIT(0) #define APC_PWR_GATE_CTL 0x14 @@ -75,6 +76,62 @@ static int scss_release_secondary(unsigned int cpu) return 0; } +static int cortex_a7_release_secondary(unsigned int cpu) +{ + int ret = 0; + void __iomem *reg; + struct device_node *cpu_node, *acc_node; + u32 reg_val; + + cpu_node = of_get_cpu_node(cpu, NULL); + if (!cpu_node) + return -ENODEV; + + acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); + if (!acc_node) { + ret = -ENODEV; + goto out_acc; + } + + reg = of_iomap(acc_node, 0); + if (!reg) { + ret = -ENOMEM; + goto out_acc_map; + } + + /* Put the CPU into reset. */ + reg_val = CORE_RST | COREPOR_RST | CLAMP | CORE_MEM_CLAMP; + writel(reg_val, reg + APCS_CPU_PWR_CTL); + + /* Turn on the BHS and set the BHS_CNT to 16 XO clock cycles */ + writel(BHS_EN | (0x10 << BHS_CNT_SHIFT), reg + APC_PWR_GATE_CTL); + /* Wait for the BHS to settle */ + udelay(2); + + reg_val &= ~CORE_MEM_CLAMP; + writel(reg_val, reg + APCS_CPU_PWR_CTL); + reg_val |= L2DT_SLP; + writel(reg_val, reg + APCS_CPU_PWR_CTL); + udelay(2); + + reg_val = (reg_val | BIT(17)) & ~CLAMP; + writel(reg_val, reg + APCS_CPU_PWR_CTL); + udelay(2); + + /* Release CPU out of reset and bring it to life. */ + reg_val &= ~(CORE_RST | COREPOR_RST); + writel(reg_val, reg + APCS_CPU_PWR_CTL); + reg_val |= CORE_PWRD_UP; + writel(reg_val, reg + APCS_CPU_PWR_CTL); + + iounmap(reg); +out_acc_map: + of_node_put(acc_node); +out_acc: + of_node_put(cpu_node); + return ret; +} + static int kpssv1_release_secondary(unsigned int cpu) { int ret = 0; @@ -281,6 +338,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) return qcom_boot_secondary(cpu, scss_release_secondary); } +static int cortex_a7_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + return qcom_boot_secondary(cpu, cortex_a7_release_secondary); +} + static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) { return qcom_boot_secondary(cpu, kpssv1_release_secondary); @@ -315,6 +377,15 @@ static const struct smp_operations smp_msm8660_ops __initconst = { }; CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); +static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = { + .smp_prepare_cpus = qcom_smp_prepare_cpus, + .smp_boot_secondary = cortex_a7_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = qcom_cpu_die, +#endif +}; +CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops); + static const struct smp_operations qcom_smp_kpssv1_ops __initconst = { .smp_prepare_cpus = qcom_smp_prepare_cpus, .smp_boot_secondary = kpssv1_boot_secondary, diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 334f83e56120..8a02213c8391 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -112,6 +112,7 @@ config ARM_QCOM_SPM_CPUIDLE select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES select QCOM_SCM + select QCOM_SPM help Select this to enable cpuidle for Qualcomm processors. The Subsystem Power Manager (SPM) controls low power modes for the diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c index c0e7971da2da..01e77913a414 100644 --- a/drivers/cpuidle/cpuidle-qcom-spm.c +++ b/drivers/cpuidle/cpuidle-qcom-spm.c @@ -18,158 +18,18 @@ #include <linux/cpuidle.h> #include <linux/cpu_pm.h> #include <linux/qcom_scm.h> +#include <soc/qcom/spm.h> #include <asm/proc-fns.h> #include <asm/suspend.h> #include "dt_idle_states.h" -#define MAX_PMIC_DATA 2 -#define MAX_SEQ_DATA 64 -#define SPM_CTL_INDEX 0x7f -#define SPM_CTL_INDEX_SHIFT 4 -#define SPM_CTL_EN BIT(0) - -enum pm_sleep_mode { - PM_SLEEP_MODE_STBY, - PM_SLEEP_MODE_RET, - PM_SLEEP_MODE_SPC, - PM_SLEEP_MODE_PC, - PM_SLEEP_MODE_NR, -}; - -enum spm_reg { - SPM_REG_CFG, - SPM_REG_SPM_CTL, - SPM_REG_DLY, - SPM_REG_PMIC_DLY, - SPM_REG_PMIC_DATA_0, - SPM_REG_PMIC_DATA_1, - SPM_REG_VCTL, - SPM_REG_SEQ_ENTRY, - SPM_REG_SPM_STS, - SPM_REG_PMIC_STS, - SPM_REG_NR, -}; - -struct spm_reg_data { - const u8 *reg_offset; - u32 spm_cfg; - u32 spm_dly; - u32 pmic_dly; - u32 pmic_data[MAX_PMIC_DATA]; - u8 seq[MAX_SEQ_DATA]; - u8 start_index[PM_SLEEP_MODE_NR]; -}; - -struct spm_driver_data { +struct cpuidle_qcom_spm_data { struct cpuidle_driver cpuidle_driver; - void __iomem *reg_base; - const struct spm_reg_data *reg_data; -}; - -static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { - [SPM_REG_CFG] = 0x08, - [SPM_REG_SPM_CTL] = 0x30, - [SPM_REG_DLY] = 0x34, - [SPM_REG_SEQ_ENTRY] = 0x80, -}; - -/* SPM register data for 8974, 8084 */ -static const struct spm_reg_data spm_reg_8974_8084_cpu = { - .reg_offset = spm_reg_offset_v2_1, - .spm_cfg = 0x1, - .spm_dly = 0x3C102800, - .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, - 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, - 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 3, + struct spm_driver_data *spm; }; -/* SPM register data for 8226 */ -static const struct spm_reg_data spm_reg_8226_cpu = { - .reg_offset = spm_reg_offset_v2_1, - .spm_cfg = 0x0, - .spm_dly = 0x3C102800, - .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, - 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, - 0x80, 0x10, 0x26, 0x30, 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 5, -}; - -static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { - [SPM_REG_CFG] = 0x08, - [SPM_REG_SPM_CTL] = 0x20, - [SPM_REG_PMIC_DLY] = 0x24, - [SPM_REG_PMIC_DATA_0] = 0x28, - [SPM_REG_PMIC_DATA_1] = 0x2C, - [SPM_REG_SEQ_ENTRY] = 0x80, -}; - -/* SPM register data for 8064 */ -static const struct spm_reg_data spm_reg_8064_cpu = { - .reg_offset = spm_reg_offset_v1_1, - .spm_cfg = 0x1F, - .pmic_dly = 0x02020004, - .pmic_data[0] = 0x0084009C, - .pmic_data[1] = 0x00A4001C, - .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, - 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 2, -}; - -static inline void spm_register_write(struct spm_driver_data *drv, - enum spm_reg reg, u32 val) -{ - if (drv->reg_data->reg_offset[reg]) - writel_relaxed(val, drv->reg_base + - drv->reg_data->reg_offset[reg]); -} - -/* Ensure a guaranteed write, before return */ -static inline void spm_register_write_sync(struct spm_driver_data *drv, - enum spm_reg reg, u32 val) -{ - u32 ret; - - if (!drv->reg_data->reg_offset[reg]) - return; - - do { - writel_relaxed(val, drv->reg_base + - drv->reg_data->reg_offset[reg]); - ret = readl_relaxed(drv->reg_base + - drv->reg_data->reg_offset[reg]); - if (ret == val) - break; - cpu_relax(); - } while (1); -} - -static inline u32 spm_register_read(struct spm_driver_data *drv, - enum spm_reg reg) -{ - return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); -} - -static void spm_set_low_power_mode(struct spm_driver_data *drv, - enum pm_sleep_mode mode) -{ - u32 start_index; - u32 ctl_val; - - start_index = drv->reg_data->start_index[mode]; - - ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); - ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); - ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; - ctl_val |= SPM_CTL_EN; - spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); -} - static int qcom_pm_collapse(unsigned long int unused) { qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON); @@ -201,10 +61,10 @@ static int qcom_cpu_spc(struct spm_driver_data *drv) static int spm_enter_idle_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx) { - struct spm_driver_data *data = container_of(drv, struct spm_driver_data, - cpuidle_driver); + struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data, + cpuidle_driver); - return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data); + return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm); } static struct cpuidle_driver qcom_spm_idle_driver = { @@ -225,134 +85,92 @@ static const struct of_device_id qcom_idle_state_match[] = { { }, }; -static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu) +static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu) { + struct platform_device *pdev = NULL; + struct device_node *cpu_node, *saw_node; + struct cpuidle_qcom_spm_data *data = NULL; int ret; - memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv)); - drv->cpumask = (struct cpumask *)cpumask_of(cpu); + cpu_node = of_cpu_device_node_get(cpu); + if (!cpu_node) + return -ENODEV; - /* Parse idle states from device tree */ - ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1); - if (ret <= 0) - return ret ? : -ENODEV; + saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); + if (!saw_node) + return -ENODEV; - /* We have atleast one power down mode */ - return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask); -} + pdev = of_find_device_by_node(saw_node); + of_node_put(saw_node); + of_node_put(cpu_node); + if (!pdev) + return -ENODEV; -static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, - int *spm_cpu) -{ - struct spm_driver_data *drv = NULL; - struct device_node *cpu_node, *saw_node; - int cpu; - bool found = 0; + data = devm_kzalloc(cpuidle_dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; - for_each_possible_cpu(cpu) { - cpu_node = of_cpu_device_node_get(cpu); - if (!cpu_node) - continue; - saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); - found = (saw_node == pdev->dev.of_node); - of_node_put(saw_node); - of_node_put(cpu_node); - if (found) - break; - } + data->spm = dev_get_drvdata(&pdev->dev); + if (!data->spm) + return -EINVAL; - if (found) { - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); - if (drv) - *spm_cpu = cpu; - } + data->cpuidle_driver = qcom_spm_idle_driver; + data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu); - return drv; -} + ret = dt_init_idle_driver(&data->cpuidle_driver, + qcom_idle_state_match, 1); + if (ret <= 0) + return ret ? : -ENODEV; -static const struct of_device_id spm_match_table[] = { - { .compatible = "qcom,msm8226-saw2-v2.1-cpu", - .data = &spm_reg_8226_cpu }, - { .compatible = "qcom,msm8974-saw2-v2.1-cpu", - .data = &spm_reg_8974_8084_cpu }, - { .compatible = "qcom,apq8084-saw2-v2.1-cpu", - .data = &spm_reg_8974_8084_cpu }, - { .compatible = "qcom,apq8064-saw2-v1.1-cpu", - .data = &spm_reg_8064_cpu }, - { }, -}; + ret = qcom_scm_set_warm_boot_addr(cpu_resume_arm, cpumask_of(cpu)); + if (ret) + return ret; + + return cpuidle_register(&data->cpuidle_driver, NULL); +} -static int spm_dev_probe(struct platform_device *pdev) +static int spm_cpuidle_drv_probe(struct platform_device *pdev) { - struct spm_driver_data *drv; - struct resource *res; - const struct of_device_id *match_id; - void __iomem *addr; int cpu, ret; if (!qcom_scm_is_available()) return -EPROBE_DEFER; - drv = spm_get_drv(pdev, &cpu); - if (!drv) - return -EINVAL; - platform_set_drvdata(pdev, drv); + for_each_possible_cpu(cpu) { + ret = spm_cpuidle_register(&pdev->dev, cpu); + if (ret && ret != -ENODEV) { + dev_err(&pdev->dev, + "Cannot register for CPU%d: %d\n", cpu, ret); + } + } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(drv->reg_base)) - return PTR_ERR(drv->reg_base); + return 0; +} - match_id = of_match_node(spm_match_table, pdev->dev.of_node); - if (!match_id) - return -ENODEV; +static struct platform_driver spm_cpuidle_driver = { + .probe = spm_cpuidle_drv_probe, + .driver = { + .name = "qcom-spm-cpuidle", + .suppress_bind_attrs = true, + }, +}; - drv->reg_data = match_id->data; +static int __init qcom_spm_cpuidle_init(void) +{ + struct platform_device *pdev; + int ret; - ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu); + ret = platform_driver_register(&spm_cpuidle_driver); if (ret) return ret; - /* Write the SPM sequences first.. */ - addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; - __iowrite32_copy(addr, drv->reg_data->seq, - ARRAY_SIZE(drv->reg_data->seq) / 4); - - /* - * ..and then the control registers. - * On some SoC if the control registers are written first and if the - * CPU was held in reset, the reset signal could trigger the SPM state - * machine, before the sequences are completely written. - */ - spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); - spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); - spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); - spm_register_write(drv, SPM_REG_PMIC_DATA_0, - drv->reg_data->pmic_data[0]); - spm_register_write(drv, SPM_REG_PMIC_DATA_1, - drv->reg_data->pmic_data[1]); - - /* Set up Standby as the default low power mode */ - spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); - - return cpuidle_register(&drv->cpuidle_driver, NULL); -} - -static int spm_dev_remove(struct platform_device *pdev) -{ - struct spm_driver_data *drv = platform_get_drvdata(pdev); + pdev = platform_device_register_simple("qcom-spm-cpuidle", + -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&spm_cpuidle_driver); + return PTR_ERR(pdev); + } - cpuidle_unregister(&drv->cpuidle_driver); return 0; } - -static struct platform_driver spm_driver = { - .probe = spm_dev_probe, - .remove = spm_dev_remove, - .driver = { - .name = "saw", - .of_match_table = spm_match_table, - }, -}; - -builtin_platform_driver(spm_driver); +device_initcall(qcom_spm_cpuidle_init); diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 2ee97bab7440..a861033616ee 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1348,6 +1348,10 @@ static const struct of_device_id qcom_scm_dt_match[] = { SCM_HAS_IFACE_CLK | SCM_HAS_BUS_CLK) }, + { .compatible = "qcom,scm-msm8953", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) + }, { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK | SCM_HAS_IFACE_CLK | SCM_HAS_BUS_CLK) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 79b568f82a1c..fe3c486ae32d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -190,6 +190,15 @@ config QCOM_SOCINFO Say yes here to support the Qualcomm socinfo driver, providing information about the SoC to user space. +config QCOM_SPM + tristate "Qualcomm Subsystem Power Manager (SPM)" + depends on ARCH_QCOM + select QCOM_SCM + help + Enable the support for the Qualcomm Subsystem Power Manager, used + to manage cores, L2 low power modes and to configure the internal + Adaptive Voltage Scaler parameters, where supported. + config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ad675a6593d0..24514c722832 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o +obj-$(CONFIG_QCOM_SPM) += spm.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c index 4ce8e816154f..1d818a8ba208 100644 --- a/drivers/soc/qcom/cpr.c +++ b/drivers/soc/qcom/cpr.c @@ -1614,7 +1614,6 @@ static void cpr_debugfs_init(struct cpr_drv *drv) static int cpr_probe(struct platform_device *pdev) { - struct resource *res; struct device *dev = &pdev->dev; struct cpr_drv *drv; int irq, ret; @@ -1648,8 +1647,7 @@ static int cpr_probe(struct platform_device *pdev) if (IS_ERR(drv->tcsr)) return PTR_ERR(drv->tcsr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv->base = devm_ioremap_resource(dev, res); + drv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(drv->base)) return PTR_ERR(drv->base); diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 15a36dcab990..6bf2f1d1f2c5 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -115,7 +115,7 @@ static const struct llcc_slice_config sc7280_data[] = { { LLCC_CMPT, 10, 768, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, { LLCC_GPUHTW, 11, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, { LLCC_GPU, 12, 512, 1, 0, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, - { LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 1, 0}, + { LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 0, 1, 0}, { LLCC_MDMPNG, 21, 768, 0, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, { LLCC_WLHW, 24, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, { LLCC_MODPE, 29, 64, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, @@ -142,6 +142,16 @@ static const struct llcc_slice_config sdm845_data[] = { { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, }; +static const struct llcc_slice_config sm6350_data[] = { + { LLCC_CPUSS, 1, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1 }, + { LLCC_MDM, 8, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 512, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_MDMPNG, 21, 768, 0, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_NPU, 23, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_MODPE, 29, 64, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, +}; + static const struct llcc_slice_config sm8150_data[] = { { LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 }, { LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 }, @@ -203,6 +213,11 @@ static const struct qcom_llcc_config sdm845_cfg = { .need_llcc_cfg = false, }; +static const struct qcom_llcc_config sm6350_cfg = { + .sct_data = sm6350_data, + .size = ARRAY_SIZE(sm6350_data), +}; + static const struct qcom_llcc_config sm8150_cfg = { .sct_data = sm8150_data, .size = ARRAY_SIZE(sm8150_data), @@ -626,6 +641,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, + { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg }, { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg }, { .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg }, { } diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index f1875dc31ae2..d2dacbbaafbd 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -300,7 +300,6 @@ static int ocmem_dev_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; unsigned long reg, region_size; int i, j, ret, num_banks; - struct resource *res; struct ocmem *ocmem; if (!qcom_scm_is_available()) @@ -321,8 +320,7 @@ static int ocmem_dev_probe(struct platform_device *pdev) return ret; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - ocmem->mmio = devm_ioremap_resource(&pdev->dev, res); + ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl"); if (IS_ERR(ocmem->mmio)) { dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n"); return PTR_ERR(ocmem->mmio); diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index 915d5bc3d46e..fc580a3c4336 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -131,7 +131,7 @@ static int pdr_register_listener(struct pdr_handle *pdr, return ret; req.enable = enable; - strcpy(req.service_path, pds->service_path); + strscpy(req.service_path, pds->service_path, sizeof(req.service_path)); ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, &txn, SERVREG_REGISTER_LISTENER_REQ, @@ -257,7 +257,7 @@ static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds, return ret; req.transaction_id = tid; - strcpy(req.service_path, pds->service_path); + strscpy(req.service_path, pds->service_path, sizeof(req.service_path)); ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, &txn, SERVREG_SET_ACK_REQ, @@ -406,7 +406,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) return -ENOMEM; /* Prepare req message */ - strcpy(req.service_name, pds->service_name); + strscpy(req.service_name, pds->service_name, sizeof(req.service_name)); req.domain_offset_valid = true; req.domain_offset = 0; @@ -531,8 +531,8 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, return ERR_PTR(-ENOMEM); pds->service = SERVREG_NOTIFIER_SERVICE; - strcpy(pds->service_name, service_name); - strcpy(pds->service_path, service_path); + strscpy(pds->service_name, service_name, sizeof(pds->service_name)); + strscpy(pds->service_path, service_path, sizeof(pds->service_path)); pds->need_locator_lookup = true; mutex_lock(&pdr->list_lock); @@ -587,7 +587,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds) break; /* Prepare req message */ - strcpy(req.service_path, pds->service_path); + strscpy(req.service_path, pds->service_path, sizeof(req.service_path)); addr = pds->addr; break; } diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 7d649d2cf31e..28a8c0dda66c 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -871,7 +871,6 @@ EXPORT_SYMBOL(geni_icc_disable); static int geni_se_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct geni_wrapper *wrapper; int ret; @@ -880,8 +879,7 @@ static int geni_se_probe(struct platform_device *pdev) return -ENOMEM; wrapper->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wrapper->base = devm_ioremap_resource(dev, res); + wrapper->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(wrapper->base)) return PTR_ERR(wrapper->base); diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index bb336cc8df6f..aac36f0b58e4 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -572,7 +572,6 @@ EXPORT_SYMBOL(qmp_put); static int qmp_probe(struct platform_device *pdev) { - struct resource *res; struct qmp *qmp; int irq; int ret; @@ -585,8 +584,7 @@ static int qmp_probe(struct platform_device *pdev) init_waitqueue_head(&qmp->event); mutex_init(&qmp->tx_lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qmp->msgram = devm_ioremap_resource(&pdev->dev, res); + qmp->msgram = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(qmp->msgram)) return PTR_ERR(qmp->msgram); diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c index 304afc223a58..290bdefbf28a 100644 --- a/drivers/soc/qcom/qcom_gsbi.c +++ b/drivers/soc/qcom/qcom_gsbi.c @@ -127,7 +127,6 @@ static int gsbi_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct device_node *tcsr_node; const struct of_device_id *match; - struct resource *res; void __iomem *base; struct gsbi_info *gsbi; int i, ret; @@ -139,8 +138,7 @@ static int gsbi_probe(struct platform_device *pdev) if (!gsbi) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index e749a2b285d8..3a12a482f6b2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -910,7 +910,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; struct rsc_drv *drv; - struct resource *res; char drv_id[10] = {0}; int ret, irq; u32 solver_config; @@ -941,8 +940,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) drv->name = dev_name(&pdev->dev); snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource_byname(pdev, drv_id); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index fa209b479ab3..e280a8194725 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -147,6 +147,21 @@ static const struct rpmhpd_desc sdx55_desc = { .num_pds = ARRAY_SIZE(sdx55_rpmhpds), }; +/* SM6350 RPMH powerdomains */ +static struct rpmhpd *sm6350_rpmhpds[] = { + [SM6350_CX] = &sdm845_cx, + [SM6350_GFX] = &sdm845_gfx, + [SM6350_LCX] = &sdm845_lcx, + [SM6350_LMX] = &sdm845_lmx, + [SM6350_MSS] = &sdm845_mss, + [SM6350_MX] = &sdm845_mx, +}; + +static const struct rpmhpd_desc sm6350_desc = { + .rpmhpds = sm6350_rpmhpds, + .num_pds = ARRAY_SIZE(sm6350_rpmhpds), +}; + /* SM8150 RPMH powerdomains */ static struct rpmhpd sm8150_mmcx_ao; @@ -297,6 +312,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, + { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index dbf494e92574..4f69fb9b2e0e 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -185,6 +185,29 @@ static const struct rpmpd_desc msm8916_desc = { .max_state = MAX_CORNER_RPMPD_STATE, }; +/* msm8953 RPM Power Domains */ +DEFINE_RPMPD_PAIR(msm8953, vddmd, vddmd_ao, SMPA, LEVEL, 1); +DEFINE_RPMPD_PAIR(msm8953, vddcx, vddcx_ao, SMPA, LEVEL, 2); +DEFINE_RPMPD_PAIR(msm8953, vddmx, vddmx_ao, SMPA, LEVEL, 7); + +DEFINE_RPMPD_VFL(msm8953, vddcx_vfl, SMPA, 2); + +static struct rpmpd *msm8953_rpmpds[] = { + [MSM8953_VDDMD] = &msm8953_vddmd, + [MSM8953_VDDMD_AO] = &msm8953_vddmd_ao, + [MSM8953_VDDCX] = &msm8953_vddcx, + [MSM8953_VDDCX_AO] = &msm8953_vddcx_ao, + [MSM8953_VDDCX_VFL] = &msm8953_vddcx_vfl, + [MSM8953_VDDMX] = &msm8953_vddmx, + [MSM8953_VDDMX_AO] = &msm8953_vddmx_ao, +}; + +static const struct rpmpd_desc msm8953_desc = { + .rpmpds = msm8953_rpmpds, + .num_pds = ARRAY_SIZE(msm8953_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO, +}; + /* msm8976 RPM Power Domains */ DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2); DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6); @@ -377,6 +400,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc }, { .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc }, + { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc }, { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, { .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc }, { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index dfdd4f20f5fd..30dda1af63c8 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -236,6 +236,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-msm8226" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8936" }, + { .compatible = "qcom,rpm-msm8953" }, { .compatible = "qcom,rpm-msm8974" }, { .compatible = "qcom,rpm-msm8976" }, { .compatible = "qcom,rpm-msm8994" }, @@ -244,6 +245,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-sdm660" }, { .compatible = "qcom,rpm-sm6115" }, { .compatible = "qcom,rpm-sm6125" }, + { .compatible = "qcom,rpm-qcm2290" }, { .compatible = "qcom,rpm-qcs404" }, {} }; diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 9faf48302f4b..288897868435 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -281,19 +281,31 @@ static const struct soc_id soc_id[] = { { 319, "APQ8098" }, { 321, "SDM845" }, { 322, "MDM9206" }, + { 323, "IPQ8074" }, { 324, "SDA660" }, { 325, "SDM658" }, { 326, "SDA658" }, { 327, "SDA630" }, { 338, "SDM450" }, { 341, "SDA845" }, + { 342, "IPQ8072" }, + { 343, "IPQ8076" }, + { 344, "IPQ8078" }, { 345, "SDM636" }, { 346, "SDA636" }, { 349, "SDM632" }, { 350, "SDA632" }, { 351, "SDA450" }, { 356, "SM8250" }, + { 375, "IPQ8070" }, + { 376, "IPQ8071" }, + { 389, "IPQ8072A" }, + { 390, "IPQ8074A" }, + { 391, "IPQ8076A" }, + { 392, "IPQ8078A" }, { 394, "SM6125" }, + { 395, "IPQ8070A" }, + { 396, "IPQ8071A" }, { 402, "IPQ6018" }, { 403, "IPQ6028" }, { 421, "IPQ6000" }, diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c new file mode 100644 index 000000000000..2961a89d929c --- /dev/null +++ b/drivers/soc/qcom/spm.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2015, Linaro Ltd. + * + * SAW power controller driver + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <soc/qcom/spm.h> + +#define SPM_CTL_INDEX 0x7f +#define SPM_CTL_INDEX_SHIFT 4 +#define SPM_CTL_EN BIT(0) + +enum spm_reg { + SPM_REG_CFG, + SPM_REG_SPM_CTL, + SPM_REG_DLY, + SPM_REG_PMIC_DLY, + SPM_REG_PMIC_DATA_0, + SPM_REG_PMIC_DATA_1, + SPM_REG_VCTL, + SPM_REG_SEQ_ENTRY, + SPM_REG_SPM_STS, + SPM_REG_PMIC_STS, + SPM_REG_AVS_CTL, + SPM_REG_AVS_LIMIT, + SPM_REG_NR, +}; + +static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { + [SPM_REG_AVS_CTL] = 0x904, + [SPM_REG_AVS_LIMIT] = 0x908, +}; + +static const struct spm_reg_data spm_reg_660_gold_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4580458, +}; + +static const struct spm_reg_data spm_reg_660_silver_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x101c031, + .avs_limit = 0x4580458, +}; + +static const struct spm_reg_data spm_reg_8998_gold_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4700470, +}; + +static const struct spm_reg_data spm_reg_8998_silver_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4200420, +}; + +static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = { + [SPM_REG_CFG] = 0x08, + [SPM_REG_SPM_CTL] = 0x30, + [SPM_REG_DLY] = 0x34, + [SPM_REG_SEQ_ENTRY] = 0x80, +}; + +/* SPM register data for 8974, 8084 */ +static const struct spm_reg_data spm_reg_8974_8084_cpu = { + .reg_offset = spm_reg_offset_v2_1, + .spm_cfg = 0x1, + .spm_dly = 0x3C102800, + .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, + 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, + 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 3, +}; + +/* SPM register data for 8226 */ +static const struct spm_reg_data spm_reg_8226_cpu = { + .reg_offset = spm_reg_offset_v2_1, + .spm_cfg = 0x0, + .spm_dly = 0x3C102800, + .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, + 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, + 0x80, 0x10, 0x26, 0x30, 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 5, +}; + +static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { + [SPM_REG_CFG] = 0x08, + [SPM_REG_SPM_CTL] = 0x20, + [SPM_REG_PMIC_DLY] = 0x24, + [SPM_REG_PMIC_DATA_0] = 0x28, + [SPM_REG_PMIC_DATA_1] = 0x2C, + [SPM_REG_SEQ_ENTRY] = 0x80, +}; + +/* SPM register data for 8064 */ +static const struct spm_reg_data spm_reg_8064_cpu = { + .reg_offset = spm_reg_offset_v1_1, + .spm_cfg = 0x1F, + .pmic_dly = 0x02020004, + .pmic_data[0] = 0x0084009C, + .pmic_data[1] = 0x00A4001C, + .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, + 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 2, +}; + +static inline void spm_register_write(struct spm_driver_data *drv, + enum spm_reg reg, u32 val) +{ + if (drv->reg_data->reg_offset[reg]) + writel_relaxed(val, drv->reg_base + + drv->reg_data->reg_offset[reg]); +} + +/* Ensure a guaranteed write, before return */ +static inline void spm_register_write_sync(struct spm_driver_data *drv, + enum spm_reg reg, u32 val) +{ + u32 ret; + + if (!drv->reg_data->reg_offset[reg]) + return; + + do { + writel_relaxed(val, drv->reg_base + + drv->reg_data->reg_offset[reg]); + ret = readl_relaxed(drv->reg_base + + drv->reg_data->reg_offset[reg]); + if (ret == val) + break; + cpu_relax(); + } while (1); +} + +static inline u32 spm_register_read(struct spm_driver_data *drv, + enum spm_reg reg) +{ + return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); +} + +void spm_set_low_power_mode(struct spm_driver_data *drv, + enum pm_sleep_mode mode) +{ + u32 start_index; + u32 ctl_val; + + start_index = drv->reg_data->start_index[mode]; + + ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); + ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); + ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; + ctl_val |= SPM_CTL_EN; + spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); +} + +static const struct of_device_id spm_match_table[] = { + { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", + .data = &spm_reg_660_gold_l2 }, + { .compatible = "qcom,sdm660-silver-saw2-v4.1-l2", + .data = &spm_reg_660_silver_l2 }, + { .compatible = "qcom,msm8226-saw2-v2.1-cpu", + .data = &spm_reg_8226_cpu }, + { .compatible = "qcom,msm8974-saw2-v2.1-cpu", + .data = &spm_reg_8974_8084_cpu }, + { .compatible = "qcom,msm8998-gold-saw2-v4.1-l2", + .data = &spm_reg_8998_gold_l2 }, + { .compatible = "qcom,msm8998-silver-saw2-v4.1-l2", + .data = &spm_reg_8998_silver_l2 }, + { .compatible = "qcom,apq8084-saw2-v2.1-cpu", + .data = &spm_reg_8974_8084_cpu }, + { .compatible = "qcom,apq8064-saw2-v1.1-cpu", + .data = &spm_reg_8064_cpu }, + { }, +}; +MODULE_DEVICE_TABLE(of, spm_match_table); + +static int spm_dev_probe(struct platform_device *pdev) +{ + const struct of_device_id *match_id; + struct spm_driver_data *drv; + struct resource *res; + void __iomem *addr; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(drv->reg_base)) + return PTR_ERR(drv->reg_base); + + match_id = of_match_node(spm_match_table, pdev->dev.of_node); + if (!match_id) + return -ENODEV; + + drv->reg_data = match_id->data; + platform_set_drvdata(pdev, drv); + + /* Write the SPM sequences first.. */ + addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; + __iowrite32_copy(addr, drv->reg_data->seq, + ARRAY_SIZE(drv->reg_data->seq) / 4); + + /* + * ..and then the control registers. + * On some SoC if the control registers are written first and if the + * CPU was held in reset, the reset signal could trigger the SPM state + * machine, before the sequences are completely written. + */ + spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl); + spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit); + spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); + spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); + spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); + spm_register_write(drv, SPM_REG_PMIC_DATA_0, + drv->reg_data->pmic_data[0]); + spm_register_write(drv, SPM_REG_PMIC_DATA_1, + drv->reg_data->pmic_data[1]); + + /* Set up Standby as the default low power mode */ + if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) + spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); + + return 0; +} + +static struct platform_driver spm_driver = { + .probe = spm_dev_probe, + .driver = { + .name = "qcom_spm", + .of_match_table = spm_match_table, + }, +}; + +static int __init qcom_spm_init(void) +{ + return platform_driver_register(&spm_driver); +} +arch_initcall(qcom_spm_init); + +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 4533dbbf9937..960f7976a807 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -20,6 +20,14 @@ #define SDX55_MX 1 #define SDX55_CX 2 +/* SM6350 Power Domain Indexes */ +#define SM6350_CX 0 +#define SM6350_GFX 1 +#define SM6350_LCX 2 +#define SM6350_LMX 3 +#define SM6350_MSS 4 +#define SM6350_MX 5 + /* SM8150 Power Domain Indexes */ #define SM8150_MSS 0 #define SM8150_EBI 1 @@ -133,6 +141,15 @@ #define MSM8916_VDDMX 3 #define MSM8916_VDDMX_AO 4 +/* MSM8953 Power Domain Indexes */ +#define MSM8953_VDDMD 0 +#define MSM8953_VDDMD_AO 1 +#define MSM8953_VDDCX 2 +#define MSM8953_VDDCX_AO 3 +#define MSM8953_VDDCX_VFL 4 +#define MSM8953_VDDMX 5 +#define MSM8953_VDDMX_AO 6 + /* MSM8976 Power Domain Indexes */ #define MSM8976_VDDCX 0 #define MSM8976_VDDCX_AO 1 diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h new file mode 100644 index 000000000000..4951f9d8b0bd --- /dev/null +++ b/include/soc/qcom/spm.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2015, Linaro Ltd. + */ + +#ifndef __SPM_H__ +#define __SPM_H__ + +#include <linux/cpuidle.h> + +#define MAX_PMIC_DATA 2 +#define MAX_SEQ_DATA 64 + +enum pm_sleep_mode { + PM_SLEEP_MODE_STBY, + PM_SLEEP_MODE_RET, + PM_SLEEP_MODE_SPC, + PM_SLEEP_MODE_PC, + PM_SLEEP_MODE_NR, +}; + +struct spm_reg_data { + const u16 *reg_offset; + u32 spm_cfg; + u32 spm_dly; + u32 pmic_dly; + u32 pmic_data[MAX_PMIC_DATA]; + u32 avs_ctl; + u32 avs_limit; + u8 seq[MAX_SEQ_DATA]; + u8 start_index[PM_SLEEP_MODE_NR]; +}; + +struct spm_driver_data { + void __iomem *reg_base; + const struct spm_reg_data *reg_data; +}; + +void spm_set_low_power_mode(struct spm_driver_data *drv, + enum pm_sleep_mode mode); + +#endif /* __SPM_H__ */ |