};
&pcie0 {
- pinctrl-names = "perst-default", "perst-active", "power-active";
+ pinctrl-names = "perst-default", "perst-active", "power-default", "power-active";
pinctrl-0 = <&pcie0_perst_default>;
pinctrl-1 = <&pcie0_perst_active>;
- pinctrl-2 = <&pcie0_power_active>;
+ pinctrl-2 = <&pcie0_power_default>;
+ pinctrl-3 = <&pcie0_power_active>;
status = "disabled";
};
&pcie1 {
- pinctrl-names = "perst-default", "perst-active", "power-active";
+ pinctrl-names = "perst-default", "perst-active", "power-default", "power-active";
pinctrl-0 = <&pcie1_perst_default>;
pinctrl-1 = <&pcie1_perst_active>;
- pinctrl-2 = <&pcie1_power_active>;
+ pinctrl-2 = <&pcie1_power_default>;
+ pinctrl-3 = <&pcie1_power_active>;
status = "disabled";
};
};
};
+ pcie0_power_default: pcie0_power_default {
+ power-pins {
+ starfive,pins = <PAD_GPIO32>;
+ starfive,pinmux = <PAD_GPIO32_FUNC_SEL 0>;
+ starfive,pin-ioconfig = <IO(GPIO_IE(1))>;
+ starfive,pin-gpio-dout = <GPO_LOW>;
+ starfive,pin-gpio-doen = <OEN_LOW>;
+ };
+ };
+
pcie0_power_active: pcie0_power_active {
power-pins {
starfive,pins = <PAD_GPIO32>;
};
};
+ pcie1_power_default: pcie1_power_default {
+ power-pins {
+ starfive,pins = <PAD_GPIO21>;
+ starfive,pinmux = <PAD_GPIO21_FUNC_SEL 0>;
+ starfive,pin-ioconfig = <IO(GPIO_IE(1))>;
+ starfive,pin-gpio-dout = <GPO_LOW>;
+ starfive,pin-gpio-doen = <OEN_LOW>;
+ };
+ };
+
pcie1_power_active: pcie1_power_active {
power-pins {
starfive,pins = <PAD_GPIO21>;
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <880000>;
};
- opp-625000000 {
- opp-hz = /bits/ 64 <625000000>;
- opp-microvolt = <880000>;
- };
opp-750000000 {
opp-hz = /bits/ 64 <750000000>;
opp-microvolt = <880000>;
};
- opp-875000000 {
- opp-hz = /bits/ 64 <875000000>;
- opp-microvolt = <880000>;
- };
- opp-1000000000 {
- opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <900000>;
- };
- opp-1250000000 {
- opp-hz = /bits/ 64 <1250000000>;
- opp-microvolt = <950000>;
- };
- opp-1375000000 {
- opp-hz = /bits/ 64 <1375000000>;
- opp-microvolt = <1000000>;
- };
opp-1500000000 {
opp-hz = /bits/ 64 <1500000000>;
- opp-microvolt = <1100000>;
- };
- opp-1625000000 {
- opp-hz = /bits/ 64 <1625000000>;
- opp-microvolt = <1100000>;
- };
- opp-1750000000 {
- opp-hz = /bits/ 64 <1750000000>;
- opp-microvolt = <1200000>;
+ opp-microvolt = <1000000>;
};
};
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
- cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0
- &CLUSTER_RET_0 &CLUSTER_NONRET_0>;
+ cpu-idle-states = <&CPU_NONRET_0_0>;
next-level-cache = <&cachectrl>;
riscv,isa = "rv64imac";
tlb-split;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
- cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0
- &CLUSTER_RET_0 &CLUSTER_NONRET_0>;
+ cpu-idle-states = <&CPU_NONRET_0_0>;
next-level-cache = <&cachectrl>;
riscv,isa = "rv64imafdc";
tlb-split;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
- cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0
- &CLUSTER_RET_0 &CLUSTER_NONRET_0>;
+ cpu-idle-states = <&CPU_NONRET_0_0>;
next-level-cache = <&cachectrl>;
riscv,isa = "rv64imafdc";
tlb-split;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
- cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0
- &CLUSTER_RET_0 &CLUSTER_NONRET_0>;
+ cpu-idle-states = <&CPU_NONRET_0_0>;
next-level-cache = <&cachectrl>;
riscv,isa = "rv64imafdc";
tlb-split;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
- cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0
- &CLUSTER_RET_0 &CLUSTER_NONRET_0>;
+ cpu-idle-states = <&CPU_NONRET_0_0>;
next-level-cache = <&cachectrl>;
riscv,isa = "rv64imafdc";
tlb-split;
};
};
- idle-states {
- CPU_RET_0_0: cpu-retentive-0-0 {
- compatible = "starfive,jh7110-idle-state";
- riscv,sbi-suspend-param = <0x10000000>;
- entry-latency-us = <20>;
- exit-latency-us = <40>;
- min-residency-us = <80>;
- };
-
- CPU_NONRET_0_0: cpu-nonretentive-0-0 {
- compatible = "starfive,jh7110-idle-state";
- riscv,sbi-suspend-param = <0x90000000>;
- entry-latency-us = <250>;
- exit-latency-us = <500>;
- min-residency-us = <950>;
- };
-
- CLUSTER_RET_0: cluster-retentive-0 {
- compatible = "starfive,jh7110-idle-state";
- riscv,sbi-suspend-param = <0x11000000>;
- local-timer-stop;
- entry-latency-us = <50>;
- exit-latency-us = <100>;
- min-residency-us = <250>;
- wakeup-latency-us = <130>;
- };
-
- CLUSTER_NONRET_0: cluster-nonretentive-0 {
- compatible = "starfive,jh7110-idle-state";
- riscv,sbi-suspend-param = <0x91000000>;
- local-timer-stop;
- entry-latency-us = <600>;
- exit-latency-us = <1100>;
- min-residency-us = <2700>;
- wakeup-latency-us = <1500>;
- };
+ idle-states {
+ CPU_NONRET_0_0: cpu-nonretentive-0-0 {
+ compatible = "riscv,idle-state";
+ riscv,sbi-suspend-param = <0x80000000>;
+ entry-latency-us = <600>;
+ exit-latency-us = <1100>;
+ min-residency-us = <2700>;
+ wakeup-latency-us = <1500>;
+ };
};
soc: soc {
<&clkisp JH7110_U0_M31DPHY_REFCLK_IN>,
<&clkisp JH7110_U0_M31DPHY_TXCLKESC_LAN0>,
<&clkgen JH7110_ISP_TOP_CLK_ISPCORE_2X>,
- <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>,
- <&clkgen JH7110_NOC_BUS_CLK_ISP_AXI>;
+ <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>;
clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk",
"clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr",
"clk_mipi_rx0_pxl", "clk_pixel_clk_if0",
"clk_pixel_clk_if1", "clk_pixel_clk_if2",
"clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in",
"clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0",
- "clk_ispcore_2x", "clk_isp_axi", "clk_noc_bus_clk_isp_axi";
+ "clk_ispcore_2x", "clk_isp_axi";
resets = <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_P>,
<&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_C>,
<&rstgen RSTN_U0_VIN_N_PCLK>,
compatible = "img-gpu";
reg = <0x0 0x18000000 0x0 0x100000>,
<0x0 0x130C000 0x0 0x10000>;
- clocks = <&clkgen JH7110_GPU_CORE>,
+ clocks = <&clkgen JH7110_GPU_CORE>,
<&clkgen JH7110_GPU_CLK_APB>,
<&clkgen JH7110_GPU_RTC_TOGGLE>,
<&clkgen JH7110_GPU_CORE_CLK>,
0x9 0x40000000 0x0 0x10000000>;
reg-names = "reg", "config";
device_type = "pci";
- starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130>;
+ starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130 0x1b8>;
bus-range = <0x0 0xff>;
ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>,
<0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>;
0x9 0xc0000000 0x0 0x10000000>;
reg-names = "reg", "config";
device_type = "pci";
- starfive,stg-syscon = <&stg_syscon 0x270 0x274 0x2e0>;
+ starfive,stg-syscon = <&stg_syscon 0x270 0x274 0x2e0 0x368>;
bus-range = <0x0 0xff>;
ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>,
<0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>;
starfive_cpufreq: starfive,jh7110-cpufreq {
compatible = "starfive,jh7110-cpufreq";
- clocks = <&clkgen JH7110_PLL0_OUT>,
- <&clkgen JH7110_CPU_ROOT>,
- <&osc>;
- clock-names = "pll0", "cpu_clk", "osc";
+ clocks = <&clkgen JH7110_CPU_CORE>;
+ clock-names = "cpu_clk";
};
};
};
CONFIG_SMP=y
CONFIG_HZ_100=y
CONFIG_PM=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_ADVANCED_DEBUG=y
CONFIG_CPU_IDLE=y
CONFIG_RISCV_SBI_CPUIDLE=y
# CONFIG_SECCOMP is not set
CONFIG_WATCHDOG_SYSFS=y
CONFIG_STARFIVE_WATCHDOG=y
CONFIG_REGULATOR=y
+CONFIG_REGULATOR_AXP15060=y
CONFIG_REGULATOR_STARFIVE_JH7110=y
# CONFIG_MEDIA_CEC_SUPPORT is not set
CONFIG_MEDIA_SUPPORT=y
CONFIG_PWM_STARFIVE_PTC=y
CONFIG_PHY_M31_DPHY_RX0=y
CONFIG_RAS=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_AUTOFS4_FS=y
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 by Rivos Inc.
+ */
+#ifndef __ASM_CPU_OPS_SBI_H
+#define __ASM_CPU_OPS_SBI_H
+
+#ifndef __ASSEMBLY__
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+
+/**
+ * struct sbi_hart_boot_data - Hart specific boot used during booting and
+ * cpu hotplug.
+ * @task_ptr: A pointer to the hart specific tp
+ * @stack_ptr: A pointer to the hart specific sp
+ */
+struct sbi_hart_boot_data {
+ void *task_ptr;
+ void *stack_ptr;
+};
+#endif
+
+#endif /* ifndef __ASM_CPU_OPS_SBI_H */
SBI_EXT_IPI = 0x735049,
SBI_EXT_RFENCE = 0x52464E43,
SBI_EXT_HSM = 0x48534D,
+ SBI_EXT_SRST = 0x53525354,
};
enum sbi_ext_base_fid {
#define SBI_HSM_SUSPEND_NON_RET_LAST (SBI_HSM_SUSP_NON_RET_BIT | \
SBI_HSM_SUSP_BASE_MASK)
+enum sbi_ext_srst_fid {
+ SBI_EXT_SRST_RESET = 0,
+};
+
+enum sbi_srst_reset_type {
+ SBI_SRST_RESET_TYPE_SHUTDOWN = 0,
+ SBI_SRST_RESET_TYPE_COLD_REBOOT,
+ SBI_SRST_RESET_TYPE_WARM_REBOOT,
+};
+
+enum sbi_srst_reset_reason {
+ SBI_SRST_RESET_REASON_NONE = 0,
+ SBI_SRST_RESET_REASON_SYS_FAILURE,
+};
+
#define SBI_SPEC_VERSION_DEFAULT 0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
#include <asm/thread_info.h>
#include <asm/ptrace.h>
#include <asm/suspend.h>
+#include <asm/cpu_ops_sbi.h>
void asm_offsets(void);
DEFINE(PT_SIZE_ON_STACK, ALIGN(sizeof(struct pt_regs), STACK_ALIGN));
OFFSET(KERNEL_MAP_VIRT_ADDR, kernel_mapping, virt_addr);
+ OFFSET(SBI_HART_BOOT_TASK_PTR_OFFSET, sbi_hart_boot_data, task_ptr);
+ OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr);
}
#include <linux/init.h>
#include <linux/mm.h>
+#include <linux/sched/task_stack.h>
#include <asm/cpu_ops.h>
+#include <asm/cpu_ops_sbi.h>
#include <asm/sbi.h>
#include <asm/smp.h>
extern char secondary_start_sbi[];
const struct cpu_operations cpu_ops_sbi;
+/*
+ * Ordered booting via HSM brings one cpu at a time. However, cpu hotplug can
+ * be invoked from multiple threads in parallel. Define a per cpu data
+ * to handle that.
+ */
+DEFINE_PER_CPU(struct sbi_hart_boot_data, boot_data);
+
static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr,
unsigned long priv)
{
static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle)
{
- int rc;
unsigned long boot_addr = __pa_symbol(secondary_start_sbi);
int hartid = cpuid_to_hartid_map(cpuid);
-
- cpu_update_secondary_bootdata(cpuid, tidle);
- rc = sbi_hsm_hart_start(hartid, boot_addr, 0);
-
- return rc;
+ unsigned long hsm_data;
+ struct sbi_hart_boot_data *bdata = &per_cpu(boot_data, cpuid);
+
+ /* Make sure tidle is updated */
+ smp_mb();
+ bdata->task_ptr = tidle;
+ bdata->stack_ptr = task_stack_page(tidle) + THREAD_SIZE;
+ /* Make sure boot data is updated */
+ smp_mb();
+ hsm_data = __pa(bdata);
+ return sbi_hsm_hart_start(hartid, boot_addr, hsm_data);
}
static int sbi_cpu_prepare(unsigned int cpuid)
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/csr.h>
+#include <asm/cpu_ops_sbi.h>
#include <asm/hwcap.h>
#include <asm/image.h>
#include "efi-header.S"
la a3, .Lsecondary_park
csrw CSR_TVEC, a3
- slli a3, a0, LGREG
- la a4, __cpu_up_stack_pointer
- XIP_FIXUP_OFFSET a4
- la a5, __cpu_up_task_pointer
- XIP_FIXUP_OFFSET a5
- add a4, a3, a4
- add a5, a3, a5
- REG_L sp, (a4)
- REG_L tp, (a5)
-
- .global secondary_start_common
-secondary_start_common:
+ /* a0 contains the hartid & a1 contains boot data */
+ li a2, SBI_HART_BOOT_TASK_PTR_OFFSET
+ XIP_FIXUP_OFFSET a2
+ add a2, a2, a1
+ REG_L tp, (a2)
+ li a3, SBI_HART_BOOT_STACK_PTR_OFFSET
+ XIP_FIXUP_OFFSET a3
+ add a3, a3, a1
+ REG_L sp, (a3)
+
+.Lsecondary_start_common:
#ifdef CONFIG_MMU
/* Enable virtual memory and relocate to virtual address */
beqz tp, .Lwait_for_cpu_up
fence
- tail secondary_start_common
+ tail .Lsecondary_start_common
#endif
END(_start_kernel)
#include <linux/init.h>
#include <linux/pm.h>
+#include <linux/reboot.h>
#include <asm/sbi.h>
#include <asm/smp.h>
}
EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid);
+static void sbi_srst_reset(unsigned long type, unsigned long reason)
+{
+ sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason,
+ 0, 0, 0, 0);
+ pr_warn("%s: type=0x%lx reason=0x%lx failed\n",
+ __func__, type, reason);
+}
+
+static int sbi_srst_reboot(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ?
+ SBI_SRST_RESET_TYPE_WARM_REBOOT :
+ SBI_SRST_RESET_TYPE_COLD_REBOOT,
+ SBI_SRST_RESET_REASON_NONE);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sbi_srst_reboot_nb;
+
+static void sbi_srst_power_off(void)
+{
+ sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN,
+ SBI_SRST_RESET_REASON_NONE);
+}
+
/**
* sbi_probe_extension() - Check if an SBI extension ID is supported or not.
* @extid: The extension ID to be probed.
} else {
__sbi_rfence = __sbi_rfence_v01;
}
+ if ((sbi_spec_version >= sbi_mk_version(0, 3)) &&
+ (sbi_probe_extension(SBI_EXT_SRST) > 0)) {
+ pr_info("SBI SRST extension detected\n");
+ pm_power_off = sbi_srst_power_off;
+ sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot;
+ sbi_srst_reboot_nb.priority = 192;
+ register_restart_handler(&sbi_srst_reboot_nb);
+ }
} else {
__sbi_set_timer = __sbi_set_timer_v01;
__sbi_send_ipi = __sbi_send_ipi_v01;
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/reset.h>
-
+#include <linux/pm_runtime.h>
#include "starfive-trng.h"
#define to_trng(p) container_of(p, struct trng, rng)
struct trng *hrng = to_trng(rng);
u32 intr = 0;
+ pm_runtime_get_sync(hrng->dev);
+
trng_cmd(hrng, CCORE_CTRL_EXEC_NOP);
if (hrng->mode == PRNG_256BIT)
trng_wait_till_idle(hrng);
writel(0, hrng->base + CCORE_IE);
+ pm_runtime_put_sync(hrng->dev);
+
return max;
}
goto err_disable_miscahb_clk;
}
+ clk_disable_unprepare(rng->hclk);
+ clk_disable_unprepare(rng->miscahb_clk);
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_enable(&pdev->dev);
+
return 0;
err_disable_miscahb_clk:
return ret;
}
+#ifdef CONFIG_PM
+
+static int starfive_trng_runtime_suspend(struct device *dev)
+{
+ struct trng *trng = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(trng->hclk);
+ clk_disable_unprepare(trng->miscahb_clk);
+
+ dev_dbg(dev, "starfive hrng runtime suspend");
+
+ return 0;
+}
+
+static int starfive_trng_runtime_resume(struct device *dev)
+{
+ struct trng *trng = dev_get_drvdata(dev);
+
+ clk_prepare_enable(trng->hclk);
+ clk_prepare_enable(trng->miscahb_clk);
+
+ dev_dbg(dev, "starfive hrng runtime resume");
+
+ return 0;
+}
+
+static int starfive_trng_runtime_idle(struct device *dev)
+{
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_autosuspend(dev);
+
+ return 0;
+}
+#endif/*CONFIG_PM*/
+
+static const struct dev_pm_ops starfive_trng_pm_ops = {
+ SET_RUNTIME_PM_OPS(starfive_trng_runtime_suspend, starfive_trng_runtime_resume,
+ starfive_trng_runtime_idle)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
static const struct of_device_id trng_dt_ids[] = {
{ .compatible = "starfive,jh7110-trng" },
static struct platform_driver trng_driver = {
.probe = trng_probe,
.driver = {
- .name = "trng",
+ .name = "trng",
+ .pm = &starfive_trng_pm_ops,
.of_match_table = of_match_ptr(trng_dt_ids),
},
};
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <dt-bindings/clock/starfive-jh7110-clkgen.h>
#include "clk-starfive-jh7110.h"
static u32 jh7110_clk_reg_get(struct jh7110_clk *clk)
{
void __iomem *reg = jh7110_clk_reg_addr_get(clk);
+ if (clk->reg_flags == JH7110_CLK_ISP_FLAG) {
+ int ret;
+ struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
+
+ if (pm_runtime_suspended(priv->dev)) {
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ dev_err(priv->dev, "cannot resume device :%d.\n", ret);
+ return 0;
+ }
+ pm_runtime_put(priv->dev);
+ }
+ }
return readl_relaxed(reg);
}
|| clk->idx == JH7110_UART5_CLK_CORE)
&& (value != JH7110_CLK_ENABLE))
value <<= 8;
- value |= readl_relaxed(reg) & ~mask;
+ value |= jh7110_clk_reg_get(clk) & ~mask;
writel_relaxed(value, reg);
spin_unlock_irqrestore(&priv->rmw_lock, flags);
}
#define JH7110_ISP_TOP_CLK_BIST_APB_CLKGEN (JH7110_CLK_ISP_END + 2)
#define JH7110_ISP_TOP_CLK_DVP_CLKGEN (JH7110_CLK_ISP_END + 3)
+struct isp_init_crg {
+ int num_clks;
+ struct clk_bulk_data *clks;
+ struct reset_control *rsts;
+};
+
static const struct jh7110_clk_data jh7110_clk_isp_data[] __initconst = {
//syscon
JH7110__DIV(JH7110_DOM4_APB_FUNC, "dom4_apb_func",
static struct clk_bulk_data isp_top_clks[] = {
{ .id = "u0_dom_isp_top_clk_dom_isp_top_clk_ispcore_2x" },
{ .id = "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi" },
- { .id = "u0_sft7110_noc_bus_clk_isp_axi" },
+};
+
+static int jh7110_isp_crg_get(struct device *dev, struct isp_init_crg *crg)
+{
+ int ret;
+
+ crg->rsts = devm_reset_control_array_get_shared(dev);
+ if (IS_ERR(crg->rsts)) {
+ dev_err(dev, "rst get failed\n");
+ return PTR_ERR(crg->rsts);
+ }
+
+ crg->clks = isp_top_clks;
+ crg->num_clks = ARRAY_SIZE(isp_top_clks);
+ ret = clk_bulk_get(dev, crg->num_clks, crg->clks);
+ if (ret) {
+ dev_err(dev, "clks get failed: %d\n", ret);
+ goto clks_get_failed;
+ }
+
+ return 0;
+
+clks_get_failed:
+ reset_control_assert(crg->rsts);
+ reset_control_put(crg->rsts);
+
+ return ret;
+}
+
+static int jh7110_isp_crg_enable(struct device *dev, struct isp_init_crg *crg, bool enable)
+{
+ int ret;
+
+ dev_dbg(dev, "starfive jh7110 isp clk&rst %sable\n", enable ? "en":"dis");
+ if (enable) {
+ ret = reset_control_deassert(crg->rsts);
+ if (ret) {
+ dev_err(dev, "rst deassert failed: %d\n", ret);
+ goto crg_failed;
+ }
+
+ ret = clk_bulk_prepare_enable(crg->num_clks, crg->clks);
+ if (ret) {
+ dev_err(dev, "clks enable failed: %d\n", ret);
+ goto crg_failed;
+ }
+ } else {
+ clk_bulk_disable_unprepare(crg->num_clks, crg->clks);
+ }
+
+ return 0;
+crg_failed:
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int clk_isp_system_suspend(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int clk_isp_system_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int clk_isp_runtime_suspend(struct device *dev)
+{
+ struct isp_init_crg *crg = dev_get_drvdata(dev);
+
+ return jh7110_isp_crg_enable(dev, crg, false);
+}
+
+static int clk_isp_runtime_resume(struct device *dev)
+{
+ struct isp_init_crg *crg = dev_get_drvdata(dev);
+
+ return jh7110_isp_crg_enable(dev, crg, true);
+}
+#endif
+
+static const struct dev_pm_ops clk_isp_pm_ops = {
+ SET_RUNTIME_PM_OPS(clk_isp_runtime_suspend, clk_isp_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(clk_isp_system_suspend, clk_isp_system_resume)
};
static struct clk_hw *jh7110_isp_clk_get(struct of_phandle_args *clkspec,
static int __init clk_starfive_jh7110_isp_probe(struct platform_device *pdev)
{
struct jh7110_clk_priv *priv;
+ struct isp_init_crg *crg;
unsigned int idx;
- struct reset_control *rsts;
- int num_clks;
int ret = 0;
priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg,
if (IS_ERR(priv->isp_base))
return PTR_ERR(priv->isp_base);
+ crg = devm_kzalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL);
+ if (!crg)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, crg);
+
+ ret = jh7110_isp_crg_get(&pdev->dev, crg);
+ if (ret)
+ goto init_failed;
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get pm runtime: %d\n", ret);
- return ret;
- }
-
- rsts = devm_reset_control_array_get_shared(priv->dev);
- if (!IS_ERR(rsts)) {
- ret = reset_control_deassert(rsts);
- if (ret) {
- dev_err(priv->dev, "rst deassert failed: %d\n", ret);
- goto rsts_deassert_failed;
- }
- } else {
- dev_err(priv->dev, "rst get failed\n");
- return PTR_ERR(rsts);
- }
-
- num_clks = ARRAY_SIZE(isp_top_clks);
- ret = clk_bulk_get(priv->dev, num_clks, isp_top_clks);
- if (!ret) {
- ret = clk_bulk_prepare_enable(num_clks, isp_top_clks);
- if (ret) {
- dev_err(priv->dev, "clks enable failed: %d\n", ret);
- goto clks_enable_failed;
- }
- } else {
- dev_err(priv->dev, "clks get failed: %d\n", ret);
- goto clks_get_failed;
+ goto init_failed;
}
priv->pll[PLL_OFI(JH7110_U3_PCLK_MUX_FUNC_PCLK)] =
ret = devm_clk_hw_register(priv->dev, &clk->hw);
if (ret)
- return ret;
+ goto init_failed;
}
ret = devm_of_clk_add_hw_provider(priv->dev, jh7110_isp_clk_get, priv);
if (ret)
- return ret;
+ goto init_failed;
- clk_bulk_put(num_clks, isp_top_clks);
- reset_control_put(rsts);
+ pm_runtime_put_sync(&pdev->dev);
dev_info(&pdev->dev, "starfive JH7110 clk_isp init successfully.");
return 0;
-clks_enable_failed:
- clk_bulk_put(num_clks, isp_top_clks);
-clks_get_failed:
- reset_control_assert(rsts);
-rsts_deassert_failed:
- reset_control_put(rsts);
-
+init_failed:
return ret;
-
}
static const struct of_device_id clk_starfive_jh7110_isp_match[] = {
.driver = {
.name = "clk-starfive-jh7110-isp",
.of_match_table = clk_starfive_jh7110_isp_match,
+ .pm = &clk_isp_pm_ops,
},
};
module_platform_driver(clk_starfive_jh7110_isp_driver);
GATE_FLAG_NORMAL, JH7110_ISP_AXI),
JH7110_GATE(JH7110_NOC_BUS_CLK_ISP_AXI,
"u0_sft7110_noc_bus_clk_isp_axi",
- CLK_IGNORE_UNUSED, JH7110_ISP_AXI),
+ CLK_IS_CRITICAL, JH7110_ISP_AXI),
//HIFI4
JH7110__DIV(JH7110_HIFI4_CORE, "hifi4_core", 15, JH7110_BUS_ROOT),
JH7110__DIV(JH7110_HIFI4_AXI, "hifi4_axi", 2, JH7110_HIFI4_CORE),
struct starfive_cpu_dvfs_info {
struct regulator *vddcpu;
struct clk *cpu_clk;
- struct clk *pll0_clk;
- struct clk *osc_clk;
unsigned long regulator_latency;
struct device *cpu_dev;
struct cpumask cpus;
}
}
- if (clk_set_parent(policy->clk, info->osc_clk))
- pr_err("cpu set parent osc failed\n");
-
- ret = clk_set_rate(info->pll0_clk, new_freq);
+ ret = clk_set_rate(info->cpu_clk, new_freq);
if (ret < 0) {
pr_err("Failed to set rate %ldkHz: %d\n",
new_freq, ret);
}
- if (clk_set_parent(policy->clk, info->pll0_clk))
- pr_err("cpu set parent pll0 failed\n");
if (info->vddcpu && new_freq < old_freq) {
ret = regulator_set_voltage(info->vddcpu,
int ret;
static int retry = 3;
- info->vddcpu = regulator_get_optional(&pdev->dev, "cpu_vdd_0p9");
+ info->vddcpu = regulator_get_optional(&pdev->dev, "cpu_vdd");
if (IS_ERR(info->vddcpu)) {
if (PTR_ERR(info->vddcpu) == -EPROBE_DEFER)
dev_warn(&pdev->dev, "The cpu regulator is not ready, retry.\n");
else
- dev_err(&pdev->dev, "Failed to get regulator for cpu\n");
+ dev_err(&pdev->dev, "Failed to get regulator for cpu!\n");
if (retry-- > 0)
return -EPROBE_DEFER;
else
PTR_ERR(info->cpu_clk));
return PTR_ERR(info->cpu_clk);
}
- info->pll0_clk = devm_clk_get(dev, "pll0");
- if (IS_ERR(info->pll0_clk)) {
- dev_err(&pdev->dev, "Unable to obtain cpu_clk: %ld\n",
- PTR_ERR(info->pll0_clk));
- return PTR_ERR(info->pll0_clk);
- }
-
- info->osc_clk = devm_clk_get(dev, "osc");
- if (IS_ERR(info->osc_clk)) {
- dev_err(&pdev->dev, "Unable to obtain osc_clk: %ld\n",
- PTR_ERR(info->osc_clk));
- return PTR_ERR(info->osc_clk);
- }
info->cpu_dev = get_cpu_device(1);
if (!info->cpu_dev) {
{
return platform_driver_register(&starfive_cpufreq_plat_driver);
}
-postcore_initcall(starfive_cpufreq_init);
+device_initcall(starfive_cpufreq_init);
MODULE_DESCRIPTION("STARFIVE CPUFREQ Driver");
MODULE_AUTHOR("Mason Huuo <mason.huo@starfivetech.com>");
}
static const struct of_device_id sbi_cpuidle_state_match[] = {
- { .compatible = "starfive,jh7110-idle-state",
+ { .compatible = "riscv,idle-state",
.data = sbi_cpuidle_enter_state },
{ },
};
struct generic_pm_domain *pd;
struct sbi_pd_provider *pd_provider;
struct dev_power_governor *pd_gov;
- int ret = -ENOMEM, state_count = 0;
+ int ret = -ENOMEM;
pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node);
if (!pd)
pd->flags |= GENPD_FLAG_ALWAYS_ON;
/* Use governor for CPU PM domains if it has some states to manage. */
- pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;
+ pd_gov = pd->states ? &pm_domain_cpu_gov : NULL;
ret = pm_genpd_init(pd, pd_gov, false);
if (ret)
int ret;
u32 hw_mode;
+ pm_runtime_resume_and_get(ctx->sdev->dev);
+
jh7110_aes_reset(ctx);
hw_mode = get_aes_mode(ctx->rctx);
free_pages((unsigned long)buf_out, pages);
}
+ pm_runtime_mark_last_busy(ctx->sdev->dev);
+ pm_runtime_put_autosuspend(ctx->sdev->dev);
+
if (is_gcm(rctx) || is_ccm(rctx))
crypto_finalize_aead_request(ctx->sdev->engine, rctx->req.areq, err);
else
return PTR_ERR(sdev->sec_ahb);
}
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
sdev->rst_hresetn = devm_reset_control_get_shared(sdev->dev, "sec_hre");
if (IS_ERR(sdev->rst_hresetn)) {
dev_err(sdev->dev, "failed to get sec reset\n");
dev_info(dev, "Initialized\n");
+ pm_runtime_put_sync(dev);
+
return 0;
err_algs_pka:
jh7110_aes_unregister_algs();
static int jh7110_cryp_remove(struct platform_device *pdev)
{
struct jh7110_sec_dev *sdev = platform_get_drvdata(pdev);
+ int ret;
if (!sdev)
return -ENODEV;
+ ret = pm_runtime_resume_and_get(sdev->dev);
+ if (ret < 0)
+ return ret;
+
jh7110_pka_unregister_algs();
jh7110_aes_unregister_algs();
jh7110_hash_unregister_algs();
list_del(&sdev->list);
spin_unlock(&dev_list.lock);
+ pm_runtime_disable(sdev->dev);
+ pm_runtime_put_noidle(sdev->dev);
+
clk_disable_unprepare(sdev->sec_hclk);
clk_disable_unprepare(sdev->sec_ahb);
+ reset_control_assert(sdev->rst_hresetn);
return 0;
}
jh7110_cryp_runtime_resume, NULL)
};
+
+
static struct platform_driver jh7110_cryp_driver = {
.probe = jh7110_cryp_probe,
.remove = jh7110_cryp_remove,
if (algs_hmac_name)
ctx->sha_mode |= JH7110_SHA_HMAC_FLAGS;
+ pm_runtime_resume_and_get(ctx->sdev->dev);
+
ctx->enginectx.op.do_one_request = jh7110_hash_one_request;
ctx->enginectx.op.prepare_request = jh7110_hash_prepare_req;
ctx->enginectx.op.unprepare_request = NULL;
crypto_free_shash(ctx->fallback.shash);
+ pm_runtime_put_sync_suspend(ctx->sdev->dev);
+
ctx->fallback.shash = NULL;
ctx->enginectx.op.do_one_request = NULL;
ctx->enginectx.op.prepare_request = NULL;
}
else
{
+ PVRSRVSetSystemPowerState(psDeviceNode->psDevConfig, PVRSRV_SYS_POWER_STATE_ON);
return PVRSRV_OK;
}
psDeviceNode->pfnFwMMUInit = RGXMipsMMUInit_Register;
}
- PVRSRVSetSystemPowerState(psDeviceNode->psDevConfig, PVRSRV_SYS_POWER_STATE_ON);
/* The device shared-virtual-memory heap address-space size is stored here for faster
look-up without having to walk the device heap configuration structures during
client device connection (i.e. this size is relative to a zero-based offset) */
#include "srvinit.h"
#include "pvr_ion_stats.h"
+#include "sysconfig.h"
#if defined(SUPPORT_DISPLAY_CLASS)
/* Display class interface */
return 0;
}
+int sPVRSRVDeviceSuspend(PVRSRV_DEVICE_NODE *psDeviceNode)
+{
+ struct sf7110_cfg *sft = sys_get_privdata();
+
+ if (sft->runtime_suspend != NULL)
+ sft->runtime_suspend(NULL);
+
+ return 0;
+}
+
+int sPVRSRVDeviceResume(PVRSRV_DEVICE_NODE *psDeviceNode)
+{
+ struct sf7110_cfg *sft = sys_get_privdata();
+
+ if (sft->runtime_resume != NULL)
+ sft->runtime_resume(NULL);
+
+ return 0;
+}
+
/**************************************************************************/ /*!
@Function PVRSRVDeviceServicesOpen
@Description Services device open.
if (psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_INIT)
{
+ PVRSRVSetSystemPowerState(psDeviceNode->psDevConfig, PVRSRV_SYS_POWER_STATE_ON);
eError = PVRSRVCommonDeviceInitialise(psDeviceNode);
if (eError != PVRSRV_OK)
{
int drm_pvr_srvkm_init(struct drm_device *dev,
void *arg, struct drm_file *psDRMFile);
+int sPVRSRVDeviceSuspend(struct _PVRSRV_DEVICE_NODE_ *psDeviceNode);
+
+int sPVRSRVDeviceResume(struct _PVRSRV_DEVICE_NODE_ *psDeviceNode);
#endif /* MODULE_COMMON_H */
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
#include "module_common.h"
#include "pvr_drm.h"
return PVRSRVDeviceResume(priv->dev_node);
}
+static int pvr_pm_runtime_suspend(struct device *dev)
+{
+ struct drm_device *ddev = dev_get_drvdata(dev);
+ struct pvr_drm_private *priv = ddev->dev_private;
+
+ sPVRSRVDeviceSuspend(priv->dev_node);
+ return 0;
+}
+
+static int pvr_pm_runtime_resume(struct device *dev)
+{
+ struct drm_device *ddev = dev_get_drvdata(dev);
+ struct pvr_drm_private *priv = ddev->dev_private;
+
+ sPVRSRVDeviceResume(priv->dev_node);
+ return 0;
+}
+
const struct dev_pm_ops pvr_pm_ops = {
.suspend = pvr_pm_suspend,
.resume = pvr_pm_resume,
+ .runtime_suspend = pvr_pm_runtime_suspend,
+ .runtime_resume = pvr_pm_runtime_resume,
};
PVRSRVDeviceDeinit(priv->dev_node);
+ pm_runtime_disable(ddev->dev);
mutex_lock(&g_device_mutex);
PVRSRVCommonDeviceDestroy(priv->dev_node);
mutex_unlock(&g_device_mutex);
}
#endif
+static IMG_UINT32 sys_gpu_runtime_resume(IMG_HANDLE hd)
+{
+ starfive_pmu_hw_event_turn_off_mask(0);
+ clk_prepare_enable(sf_cfg_t.clk_axi);
+ u0_img_gpu_enable();
+
+ return 0;
+}
+
+static IMG_UINT32 sys_gpu_runtime_suspend(IMG_HANDLE hd)
+{
+ u0_img_gpu_disable();
+ starfive_pmu_hw_event_turn_off_mask((uint32_t)-1);
+
+ return 0;
+}
+
static int create_sf7110_cfg(struct device *dev)
{
struct sf7110_cfg *psf = &sf_cfg_t;
goto err_gpu_unmap;
}
+ psf->runtime_resume = sys_gpu_runtime_resume;
+ psf->runtime_suspend = sys_gpu_runtime_suspend;
+
return 0;
err_gpu_unmap:
iounmap(psf->gpu_reg_base);
clk_disable_unprepare(sf_cfg_t.clk_sys);
}
-
-
static int sys_gpu_enable(void)
{
int ret;
-
- pm_runtime_enable(sf_cfg_t.dev);
+
ret = pm_runtime_get_sync(sf_cfg_t.dev);
if (ret < 0) {
dev_err(sf_cfg_t.dev, "gpu: failed to get pm runtime: %d\n", ret);
return ret;
}
- starfive_pmu_hw_event_turn_off_mask(0);
- clk_prepare_enable(sf_cfg_t.clk_axi);
- u0_img_gpu_enable();
+
return 0;
}
static int sys_gpu_disable(void)
{
- u0_img_gpu_disable();
- starfive_pmu_hw_event_turn_off_mask((uint32_t)-1);
pm_runtime_put_sync(sf_cfg_t.dev);
//pm_runtime_disable(sf_cfg_t.dev);
return 0;
}
-
-
-
static PVRSRV_ERROR sfSysDevPrePowerState(
IMG_HANDLE hSysData,
PVRSRV_SYS_POWER_STATE eNewPowerState,
PVRSRV_SYS_POWER_STATE eCurrentPowerState,
- IMG_BOOL bForced)
+ PVRSRV_POWER_FLAGS ePwrFlags)
{
struct sf7110_cfg *psf = hSysData;
+ pr_debug("(%s()) state: current=%d, new=%d; flags: 0x%08x", __func__,
+ eCurrentPowerState, eNewPowerState, ePwrFlags);
+
mutex_lock(&psf->set_power_state);
if ((PVRSRV_SYS_POWER_STATE_OFF == eNewPowerState) &&
IMG_HANDLE hSysData,
PVRSRV_SYS_POWER_STATE eNewPowerState,
PVRSRV_SYS_POWER_STATE eCurrentPowerState,
- IMG_BOOL bForced)
+ PVRSRV_POWER_FLAGS ePwrFlags)
{
struct sf7110_cfg *psf = hSysData;
PVRSRV_ERROR ret;
+ pr_debug("(%s()) state: current=%d, new=%d; flags: 0x%08x", __func__,
+ eCurrentPowerState, eNewPowerState, ePwrFlags);
mutex_lock(&psf->set_power_state);
* Setup RGX specific timing data
*/
gsRGXTimingInfo.ui32CoreClockSpeed = RGX_STARFIVE_7100_CORE_CLOCK_SPEED;
- gsRGXTimingInfo.bEnableActivePM = IMG_FALSE;
+ gsRGXTimingInfo.bEnableActivePM = IMG_TRUE;
gsRGXTimingInfo.bEnableRDPowIsland = IMG_TRUE;
gsRGXTimingInfo.ui32ActivePMLatencyms = SYS_RGX_ACTIVE_POWER_LATENCY_MS;
}
gsDevices[0].hSysData = &sf_cfg_t;
+ pm_runtime_enable(sf_cfg_t.dev);
/* power management on HW system */
gsDevices[0].pfnPrePowerState = sfSysDevPrePowerState;
gsDevices[0].pfnPostPowerState = sfSysDevPostPowerState;
return PVRSRV_OK;
}
+struct sf7110_cfg *sys_get_privdata(void)
+{
+ return &sf_cfg_t;
+}
+
/******************************************************************************
End of file (sysconfig.c)
******************************************************************************/
#define STARFIVE_7110_GPU_SIZE 0x00100000
#define STARFIVE_7110_GPU_PBASE 0x18000000
+typedef IMG_UINT32 (*SYS_DEV_CLK_GET)(IMG_HANDLE hData);
+
struct sf7110_cfg {
void __iomem *gpu_reg_base;
resource_size_t gpu_reg_start;
/* for gpu device freq/volt update, to be fill later */
//struct clk **top_clk;
struct device *dev;
+ SYS_DEV_CLK_GET runtime_resume;
+ SYS_DEV_CLK_GET runtime_suspend;
};
#define mk_crg_offset(x) ((x) - (U0_SYS_CRG__SAIF_BD_APBS__BASE_ADDR))
extern void do_sifive_l2_flush64_range(unsigned long start, unsigned long len);
+void u0_img_gpu_enable(void);
+void u0_img_gpu_disable(void);
+struct sf7110_cfg *sys_get_privdata(void);
/*****************************************************************************
* system specific data structures
*****************************************************************************/
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
+#include <linux/pm_runtime.h>
/*
* TempSensor reset. The RSTN can be de-asserted once the analog core has
u32 attr, int channel, long *val)
{
struct sfctemp *sfctemp = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_enable:
*val = sfctemp->enabled;
+ pm_runtime_put(dev);
return 0;
case hwmon_temp_input:
- return sfctemp_convert(sfctemp, val);
+ ret = sfctemp_convert(sfctemp, val);
+ pm_runtime_put(dev);
+ return ret;
}
+ pm_runtime_put(dev);
return -EINVAL;
default:
+ pm_runtime_put(dev);
return -EINVAL;
}
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev->name, sfctemp,
&sfctemp_chip_info, NULL);
+
+ pm_runtime_enable(hwmon_dev);
+ pm_runtime_enable(dev);
+
+ sfctemp_disable(sfctemp);
+
return PTR_ERR_OR_ZERO(hwmon_dev);
}
+#ifdef CONFIG_PM
+
+static int starfive_temp_suspend(struct device *dev)
+{
+ struct sfctemp *sfctemp = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "starfive temp runtime suspend");
+
+ return sfctemp_disable(sfctemp);
+}
+
+static int starfive_temp_resume(struct device *dev)
+{
+ struct sfctemp *sfctemp = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "starfive temp runtime resume");
+
+ return sfctemp_enable(sfctemp);
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops sfctemp_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(starfive_temp_suspend, starfive_temp_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
static const struct of_device_id sfctemp_of_match[] = {
{ .compatible = "starfive,jh7100-temp" },
{ .compatible = "starfive,jh7110-temp" },
.driver = {
.name = "sfctemp",
.of_match_table = sfctemp_of_match,
+ .pm = &sfctemp_dev_pm_ops,
},
};
module_platform_driver(sfctemp_driver);
#endif
#ifdef CONFIG_PM
-/*
static int dw_i2c_plat_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
};
-*/
-//#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
-#define DW_I2C_DEV_PMOPS NULL
+#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
#else
#define DW_I2C_DEV_PMOPS NULL
#endif
{
return platform_driver_register(&dw_i2c_driver);
}
-subsys_initcall(dw_i2c_init_driver);
+device_initcall(dw_i2c_init_driver);
static void __exit dw_i2c_exit_driver(void)
{
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/reset.h>
+#include <linux/pm_runtime.h>
#include "mailbox.h"
{
unsigned long ch = (unsigned long)chan->con_priv;
struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
+ unsigned long irq_flag = IRQF_SHARED;
long ret = 0;
+ pm_runtime_get_sync(mbox->dev);
+ /* MAILBOX should be with IRQF_NO_SUSPEND set */
+ if (!mbox->dev->pm_domain)
+ irq_flag |= IRQF_NO_SUSPEND;
+
/* Mailbox is idle so directly bail out */
if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch))
return -EBUSY;
if (mbox->mchan[ch].dst_irq > 0) {
dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch);
ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler,
- IRQF_SHARED, irq_peer_name[ch].name, chan);
+ irq_flag, irq_peer_name[ch].name, chan);
if (ret < 0)
dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq);
}
if (mbox->mchan[ch].dst_irq > 0)
devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan);
+ pm_runtime_put_sync(mbox->dev);
}
static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg)
platform_set_drvdata(pdev, mbox);
dev_info(dev, "Mailbox enabled\n");
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
}
mbox_controller_unregister(&mbox->controller);
devm_clk_put(mbox->dev, mbox->clk);
+ pm_runtime_disable(mbox->dev);
+
+ return 0;
+}
+
+static int __maybe_unused starfive_mbox_suspend(struct device *dev)
+{
+ struct starfive_mbox *mbox = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(mbox->clk);
return 0;
}
+static int __maybe_unused starfive_mbox_resume(struct device *dev)
+{
+ struct starfive_mbox *mbox = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(mbox->clk);
+ if (ret)
+ dev_err(dev, "failed to enable clock\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops starfive_mbox_pm_ops = {
+ .suspend = starfive_mbox_suspend,
+ .resume = starfive_mbox_resume,
+ SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL)
+};
static struct platform_driver starfive_mbox_driver = {
.probe = starfive_mbox_probe,
.remove = starfive_mbox_remove,
.driver = {
.name = "mailbox",
.of_match_table = starfive_mbox_of_match,
+ .pm = &starfive_mbox_pm_ops,
},
};
const struct imx219_reg_list *reg_list;
int ret;
- ret = pm_runtime_get_sync(&client->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(&client->dev);
- return ret;
- }
-
/* Apply default values of current mode */
reg_list = &imx219->mode->reg_list;
ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
if (ret) {
dev_err(&client->dev, "%s failed to set mode\n", __func__);
- goto err_rpm_put;
+ goto err;
}
ret = imx219_set_framefmt(imx219);
if (ret) {
dev_err(&client->dev, "%s failed to set frame format: %d\n",
__func__, ret);
- goto err_rpm_put;
+ goto err;
}
/* Apply customized values from user */
ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
if (ret)
- goto err_rpm_put;
+ goto err;
/* set stream on register */
ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
if (ret)
- goto err_rpm_put;
+ goto err;
/* vflip and hflip cannot change during streaming */
__v4l2_ctrl_grab(imx219->vflip, true);
return 0;
-err_rpm_put:
- pm_runtime_put(&client->dev);
+err:
return ret;
}
__v4l2_ctrl_grab(imx219->vflip, false);
__v4l2_ctrl_grab(imx219->hflip, false);
-
- pm_runtime_put(&client->dev);
}
static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
{
struct imx219 *imx219 = to_imx219(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
mutex_lock(&imx219->mutex);
goto unlock;
if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ return ret;
+ }
+
/*
* Apply default & customized values
* and then start streaming.
goto err_unlock;
} else {
imx219_stop_streaming(imx219);
+ pm_runtime_put(&client->dev);
}
unlock:
return ret;
err_unlock:
+ pm_runtime_put(&client->dev);
mutex_unlock(&imx219->mutex);
return ret;
return 0;
}
-static int __maybe_unused imx219_suspend(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx219 *imx219 = to_imx219(sd);
-
- if (imx219->streaming)
- imx219_stop_streaming(imx219);
-
- return 0;
-}
-
-static int __maybe_unused imx219_resume(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct imx219 *imx219 = to_imx219(sd);
- int ret;
-
- if (imx219->streaming) {
- ret = imx219_start_streaming(imx219);
- if (ret)
- goto error;
- }
-
- return 0;
-
-error:
- imx219_stop_streaming(imx219);
- imx219->streaming = false;
-
- return ret;
-}
-
static int imx219_get_regulators(struct imx219 *imx219)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
MODULE_DEVICE_TABLE(of, imx219_dt_ids);
static const struct dev_pm_ops imx219_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
};
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include "stfcamss.h"
-#define OV4689_LANES 2
+#define OV4689_LANES 4
#define OV4689_LINK_FREQ_500MHZ 500000000LL
/* min/typical/max system clock (xclk) frequencies */
#define OV4689_XCLK_MIN 6000000
-#define OV4689_XCLK_MAX 54000000
+#define OV4689_XCLK_MAX 64000000
#define OV4689_CHIP_ID (0x4688)
#define OV4689_REG_AWB_R_GAIN 0x500C
#define OV4689_REG_AWB_B_GAIN 0x5010
-
+#define OV4689_REG_STREAM_ON 0x0100
enum ov4689_mode_id {
//OV4689_MODE_720P_1280_720 = 0,
/* lock to protect all members below */
struct mutex lock;
- int power_count;
-
struct v4l2_mbus_framefmt fmt;
const struct ov4689_mode_info *current_mode;
struct ov4689_ctrls ctrls;
- u32 prev_sysclk, prev_hts;
- u32 ae_low, ae_high, ae_target;
-
bool pending_mode_change;
int streaming;
};
return 0;
}
-static int ov4689_set_stream_mipi(struct ov4689_dev *sensor, bool on)
-{
- return 0;
-}
-
#ifdef UNUSED_CODE
static int ov4689_get_sysclk(struct ov4689_dev *sensor)
{
#define OV4689_PLL2_DIVS 0x030e // bits[2:0]
#define OV4689_PLL2_DIVDAC 0x0312 // bits[3:0]
-#define OV4689_TIMING_HTS 0x380c
-
static int ov4689_set_mipi_pclk(struct ov4689_dev *sensor,
unsigned long rate)
{
htot = mode->htot * ov4689_framerates[mode->max_fps] / fps;
- ret = ov4689_write_reg16(sensor, OV4689_TIMING_HTS, htot);
+ ret = ov4689_write_reg16(sensor, OV4689_REG_TIMING_HTS, htot);
- ret = ov4689_read_reg(sensor, OV4689_TIMING_HTS, &val);
+ ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS, &val);
val16 = val << 8;
- ret = ov4689_read_reg(sensor, OV4689_TIMING_HTS + 1, &val);
+ ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS + 1, &val);
val16 |= val;
st_info(ST_SENSOR, "fps = %d, max_fps = %d\n", fps, mode->max_fps);
st_info(ST_SENSOR, "mode->htot = 0x%x, htot = 0x%x\n", mode->htot,
htot);
- st_info(ST_SENSOR, "reg: 0x%x = 0x%x\n", OV4689_TIMING_HTS, val16);
+ st_info(ST_SENSOR, "reg: 0x%x = 0x%x\n", OV4689_REG_TIMING_HTS, val16);
return 0;
}
static int ov4689_set_mode(struct ov4689_dev *sensor)
{
const struct ov4689_mode_info *mode = sensor->current_mode;
- //const struct ov4689_mode_info *orig_mode = sensor->last_mode;
int ret = 0;
if (ret < 0)
return ret;
- /*
- * we support have 10 bits raw RGB(mipi)
- */
- if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
- ret = ov4689_set_mipi_pclk(sensor, 0);
-
+ ret = ov4689_set_mipi_pclk(sensor, 0);
if (ret < 0)
return 0;
usleep_range(1000, 2000);
}
-static int ov4689_set_power_on(struct ov4689_dev *sensor)
+static int ov4689_set_power_on(struct device *dev)
{
- struct i2c_client *client = sensor->i2c_client;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov4689_dev *sensor = to_ov4689_dev(sd);
int ret;
ret = clk_prepare_enable(sensor->xclk);
return ret;
}
-static void ov4689_set_power_off(struct ov4689_dev *sensor)
+static int ov4689_set_power_off(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov4689_dev *sensor = to_ov4689_dev(sd);
+
ov4689_power(sensor, false);
regulator_bulk_disable(OV4689_NUM_SUPPLIES, sensor->supplies);
clk_disable_unprepare(sensor->xclk);
-}
-
-static int ov4689_set_power_mipi(struct ov4689_dev *sensor, bool on)
-{
- return 0;
-}
-
-static int ov4689_set_power(struct ov4689_dev *sensor, bool on)
-{
- int ret = 0;
- u16 chip_id;
-
- if (on) {
- ret = ov4689_set_power_on(sensor);
- if (ret)
- return ret;
-
- ret = ov4689_read_reg16(sensor, OV4689_REG_CHIP_ID, &chip_id);
- if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: failed to read chip identifier\n",
- __func__);
- ret = -ENODEV;
- goto power_off;
- }
-
- if (chip_id != OV4689_CHIP_ID) {
- dev_err(&sensor->i2c_client->dev,
- "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
- __func__, OV4689_CHIP_ID, chip_id);
- ret = -ENXIO;
- goto power_off;
- }
- dev_err(&sensor->i2c_client->dev, "%s: chip identifier, got 0x%x\n",
- __func__, chip_id);
-
- ret = ov4689_restore_mode(sensor);
- if (ret)
- goto power_off;
- }
-
- if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
- ret = ov4689_set_power_mipi(sensor, on);
- if (ret)
- goto power_off;
-
- if (!on)
- ov4689_set_power_off(sensor);
return 0;
-
-power_off:
- ov4689_set_power_off(sensor);
- return ret;
-}
-
-static int ov4689_s_power(struct v4l2_subdev *sd, int on)
-{
- struct ov4689_dev *sensor = to_ov4689_dev(sd);
- int ret = 0;
-
- mutex_lock(&sensor->lock);
-
- /*
- * If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (sensor->power_count == !on) {
- ret = ov4689_set_power(sensor, !!on);
- if (ret)
- goto out;
- }
-
- /* Update the power count. */
- sensor->power_count += on ? 1 : -1;
- WARN_ON(sensor->power_count < 0);
-out:
- mutex_unlock(&sensor->lock);
-
- if (on && !ret && sensor->power_count == 1) {
- /* restore controls */
- ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
- }
-
- return ret;
}
static int ov4689_try_frame_interval(struct ov4689_dev *sensor,
/* v4l2_ctrl_lock() locks our own mutex */
+ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
+ return 0;
+
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
val = ov4689_get_gain(sensor);
break;
}
+ pm_runtime_put(&sensor->i2c_client->dev);
+
return 0;
}
/*
* If the device is not powered up by the host driver do
* not apply any controls to H/W at this time. Instead
- * the controls will be restored right after power-up.
+ * the controls will be restored at start streaming time.
*/
- if (sensor->power_count == 0)
+ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
return 0;
switch (ctrl->id) {
break;
}
+ pm_runtime_put(&sensor->i2c_client->dev);
+
return ret;
}
static int ov4689_stream_start(struct ov4689_dev *sensor, int enable)
{
- return ov4689_write_reg(sensor, 0x100, enable);
+ return ov4689_write_reg(sensor, OV4689_REG_STREAM_ON, enable);
}
static int ov4689_s_stream(struct v4l2_subdev *sd, int enable)
struct ov4689_dev *sensor = to_ov4689_dev(sd);
int ret = 0;
+ if (enable) {
+ pm_runtime_get_sync(&sensor->i2c_client->dev);
+
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+ if (ret) {
+ pm_runtime_put_sync(&sensor->i2c_client->dev);
+ return ret;
+ }
+ }
+
mutex_lock(&sensor->lock);
if (sensor->streaming == !enable) {
+ if (enable) {
+ ret = ov4689_restore_mode(sensor);
+ if (ret)
+ goto out;
+ }
+
if (enable && sensor->pending_mode_change) {
ret = ov4689_set_mode(sensor);
if (ret)
goto out;
}
- if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
- ret = ov4689_set_stream_mipi(sensor, enable);
-
ret = ov4689_stream_start(sensor, enable);
-
if (ret)
goto out;
}
WARN_ON(sensor->streaming < 0);
out:
mutex_unlock(&sensor->lock);
+
+ if (!enable || ret)
+ pm_runtime_put_sync(&sensor->i2c_client->dev);
+
return ret;
}
static const struct v4l2_subdev_core_ops ov4689_core_ops = {
- .s_power = ov4689_s_power,
.log_status = v4l2_ctrl_subdev_log_status,
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
int ret = 0;
u16 chip_id;
- ret = ov4689_set_power_on(sensor);
- if (ret)
- return ret;
-
ret = ov4689_read_reg16(sensor, OV4689_REG_CHIP_ID, &chip_id);
if (ret) {
dev_err(&client->dev, "%s: failed to read chip identifier\n",
__func__);
- goto power_off;
+ return ret;
}
if (chip_id != OV4689_CHIP_ID) {
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
__func__, OV4689_CHIP_ID, chip_id);
- ret = -ENXIO;
+ return -ENXIO;
}
dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
__func__, chip_id);
-power_off:
- ov4689_set_power_off(sensor);
- return ret;
+ return 0;
}
static int ov4689_probe(struct i2c_client *client)
&ov4689_mode_data[OV4689_MODE_1080P_1920_1080];
sensor->last_mode = sensor->current_mode;
- sensor->ae_target = 52;
/* optional indication of physical rotation of sensor */
ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
return ret;
}
- if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
- sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
- sensor->ep.bus_type != V4L2_MBUS_BT656) {
+ if (sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
return -EINVAL;
}
mutex_init(&sensor->lock);
+ ret = ov4689_set_power_on(dev);
+ if (ret) {
+ dev_err(dev, "failed to power on\n");
+ goto entity_cleanup;
+ }
+
ret = ov4689_check_chip_id(sensor);
if (ret)
- goto entity_cleanup;
+ goto error_power_off;
ret = ov4689_init_controls(sensor);
if (ret)
- goto entity_cleanup;
+ goto error_power_off;
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
if (ret)
goto free_ctrls;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
free_ctrls:
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+error_power_off:
+ ov4689_set_power_off(dev);
entity_cleanup:
media_entity_cleanup(&sensor->sd.entity);
mutex_destroy(&sensor->lock);
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
mutex_destroy(&sensor->lock);
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov4689_set_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
return 0;
}
};
MODULE_DEVICE_TABLE(of, ov4689_dt_ids);
+static const struct dev_pm_ops ov4689_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov4689_set_power_off, ov4689_set_power_on, NULL)
+};
+
static struct i2c_driver ov4689_i2c_driver = {
.driver = {
.name = "ov4689",
.of_match_table = ov4689_dt_ids,
+ .pm = &ov4689_pm_ops,
},
.id_table = ov4689_id,
.probe_new = ov4689_probe,
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
-#include <linux/pinctrl/pinctrl.h>
#include "stfcamss.h"
/* min/typical/max system clock (xclk) frequencies */
#define SC2235_XCLK_MIN 6000000
-#define SC2235_XCLK_MAX 54000000
+#define SC2235_XCLK_MAX 27000000
#define SC2235_CHIP_ID (0x2235)
enum sc2235_frame_rate {
SC2235_15_FPS = 0,
SC2235_30_FPS,
- SC2235_60_FPS,
SC2235_NUM_FRAMERATES,
};
};
static const struct sc2235_pixfmt sc2235_formats[] = {
- //{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, },
};
static const int sc2235_framerates[] = {
[SC2235_15_FPS] = 15,
[SC2235_30_FPS] = 30,
- [SC2235_60_FPS] = 60,
};
/* regulator supplies */
/* lock to protect all members below */
struct mutex lock;
- int power_count;
-
struct v4l2_mbus_framefmt fmt;
bool pending_fmt_change;
struct sc2235_ctrls ctrls;
- u32 prev_sysclk, prev_hts;
- u32 ae_low, ae_high, ae_target;
-
bool pending_mode_change;
int streaming;
};
1920, 0x8ca, 1080, 0x4b0,
sc2235_init_regs_tbl_1080,
ARRAY_SIZE(sc2235_init_regs_tbl_1080),
- SC2235_60_FPS,
+ SC2235_30_FPS,
};
static const struct sc2235_mode_info
1920, 0x8ca, 1080, 0x4b0,
sc2235_setting_1080P_1920_1080,
ARRAY_SIZE(sc2235_setting_1080P_1920_1080),
- SC2235_60_FPS},
+ SC2235_30_FPS},
};
static int sc2235_write_reg(struct sc2235_dev *sensor, u16 reg, u8 val)
BIT(1), on ? 0 : BIT(1));
}
-static int sc2235_set_stream_dvp(struct sc2235_dev *sensor, bool on)
-{
- return sc2235_mod_reg(sensor, SC2235_REG_STREAM_ON,
- BIT(0), on);
-}
-
#ifdef UNUSED_CODE
static int sc2235_get_sysclk(struct sc2235_dev *sensor)
{
}
rate = sc2235_calc_pixel_rate(sensor);
- if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL)
- ret = sc2235_set_dvp_pclk(sensor, rate);
-
+ ret = sc2235_set_dvp_pclk(sensor, rate);
if (ret < 0)
return 0;
if (ret < 0)
goto restore_auto_exp_gain;
-
/* restore auto gain and exposure */
if (auto_gain)
sc2235_set_autogain(sensor, true);
usleep_range(20000, 25000);
}
-static int sc2235_set_power_on(struct sc2235_dev *sensor)
+static int sc2235_set_power_on(struct device *dev)
{
- struct i2c_client *client = sensor->i2c_client;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sc2235_dev *sensor = to_sc2235_dev(sd);
int ret;
ret = clk_prepare_enable(sensor->xclk);
return ret;
}
-static void sc2235_set_power_off(struct sc2235_dev *sensor)
+static int sc2235_set_power_off(struct device *dev)
{
- sc2235_power(sensor, false);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sc2235_dev *sensor = to_sc2235_dev(sd);
+ sc2235_power(sensor, false);
regulator_bulk_disable(SC2235_NUM_SUPPLIES, sensor->supplies);
clk_disable_unprepare(sensor->xclk);
-}
-
-static int sc2235_set_power_dvp(struct sc2235_dev *sensor, bool on)
-{
- unsigned int flags = sensor->ep.bus.parallel.flags;
- u8 polarities = 0;
-
- /*
- * configure parallel port control lines polarity
- *
- * POLARITY CTRL0
- * - [5]: PCLK polarity (0: active low, 1: active high)
- * - [1]: HREF polarity (0: active low, 1: active high)
- * - [0]: VSYNC polarity (mismatch here between
- * datasheet and hardware, 0 is active high
- * and 1 is active low...)
- */
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
- polarities |= BIT(1);
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- polarities |= BIT(0);
- if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- polarities |= BIT(5);
-
- // ret = sc2235_write_reg(sensor,
- // SC2235_REG_POLARITY_CTRL00,
- // polarities);
- // if (ret)
- // return ret;
return 0;
}
int ret = 0;
if (on) {
- ret = sc2235_set_power_on(sensor);
- if (ret)
- return ret;
+ pm_runtime_get_sync(&sensor->i2c_client->dev);
ret = sc2235_restore_mode(sensor);
if (ret)
goto power_off;
}
- if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL)
- ret = sc2235_set_power_dvp(sensor, on);
- if (ret)
- goto power_off;
-
if (!on)
- sc2235_set_power_off(sensor);
+ pm_runtime_put_sync(&sensor->i2c_client->dev);
return 0;
power_off:
- sc2235_set_power_off(sensor);
+ pm_runtime_put_sync(&sensor->i2c_client->dev);
return ret;
}
mutex_lock(&sensor->lock);
- /*
- * If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (sensor->power_count == !on) {
- ret = sc2235_set_power(sensor, !!on);
- if (ret)
- goto out;
- }
+ ret = sc2235_set_power(sensor, !!on);
+ if (ret)
+ goto out;
- /* Update the power count. */
- sensor->power_count += on ? 1 : -1;
- WARN_ON(sensor->power_count < 0);
-out:
mutex_unlock(&sensor->lock);
+ return 0;
- if (on && !ret && sensor->power_count == 1) {
- /* restore controls */
- ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
- }
-
+out:
+ mutex_unlock(&sensor->lock);
return ret;
}
int i;
minfps = sc2235_framerates[SC2235_15_FPS];
- maxfps = sc2235_framerates[SC2235_60_FPS];
+ maxfps = sc2235_framerates[SC2235_30_FPS];
if (fi->numerator == 0) {
fi->denominator = maxfps;
fi->numerator = 1;
- rate = SC2235_60_FPS;
+ rate = SC2235_30_FPS;
goto find_mode;
}
/* v4l2_ctrl_lock() locks our own mutex */
+ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
+ return 0;
+
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
val = sc2235_get_gain(sensor);
break;
}
+ pm_runtime_put(&sensor->i2c_client->dev);
+
return 0;
}
/*
* If the device is not powered up by the host driver do
* not apply any controls to H/W at this time. Instead
- * the controls will be restored right after power-up.
+ * the controls will be restored at start streaming time.
*/
- if (sensor->power_count == 0)
+ if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
return 0;
switch (ctrl->id) {
break;
}
+ pm_runtime_put(&sensor->i2c_client->dev);
+
return ret;
}
V4L2_EXPOSURE_MANUAL, 0,
1);
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
- 0, 65535, 1, 0x4600);
+ 0, 65535, 1, 720);
/* Auto/manual gain */
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
0, 1, 1, 0);
return 0;
}
+static int sc2235_stream_start(struct sc2235_dev *sensor, int enable)
+{
+ return sc2235_mod_reg(sensor, SC2235_REG_STREAM_ON, BIT(0), !!enable);
+}
+
static int sc2235_s_stream(struct v4l2_subdev *sd, int enable)
{
struct sc2235_dev *sensor = to_sc2235_dev(sd);
int ret = 0;
+ if (enable) {
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+ if (ret)
+ return ret;
+ }
+
mutex_lock(&sensor->lock);
if (sensor->streaming == !enable) {
sensor->pending_fmt_change = false;
}
- if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) {
- ret = sc2235_set_gain(sensor, 0x10);
- ret = sc2235_set_exposure(sensor, (360 * 2));
- ret = sc2235_set_stream_dvp(sensor, enable);
- }
-
+ ret = sc2235_stream_start(sensor, enable);
if (ret)
goto out;
}
int ret = 0;
u16 chip_id;
- ret = sc2235_set_power_on(sensor);
- if (ret)
- return ret;
-
ret = sc2235_read_reg16(sensor, SC2235_REG_CHIP_ID, &chip_id);
if (ret) {
dev_err(&client->dev, "%s: failed to read chip identifier\n",
__func__);
- goto power_off;
+ return ret;
}
if (chip_id != SC2235_CHIP_ID) {
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
__func__, SC2235_CHIP_ID, chip_id);
- ret = -ENXIO;
+ return -ENXIO;
}
dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
__func__, chip_id);
-power_off:
- sc2235_set_power_off(sensor);
- return ret;
+ return 0;
}
static int sc2235_probe(struct i2c_client *client)
&sc2235_mode_data[SC2235_MODE_1080P_1920_1080];
sensor->last_mode = sensor->current_mode;
- sensor->ae_target = 52;
-
/* optional indication of physical rotation of sensor */
ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
&rotation);
return ret;
}
- if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
- sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
- sensor->ep.bus_type != V4L2_MBUS_BT656) {
+ if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) {
dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
return -EINVAL;
}
return ret;
mutex_init(&sensor->lock);
+ ret = sc2235_set_power_on(dev);
+ if (ret) {
+ dev_err(dev, "failed to power on\n");
+ goto entity_cleanup;
+ }
+
ret = sc2235_check_chip_id(sensor);
if (ret)
- goto entity_cleanup;
+ goto entity_power_off;
ret = sc2235_init_controls(sensor);
if (ret)
- goto entity_cleanup;
+ goto entity_power_off;
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
if (ret)
goto free_ctrls;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
free_ctrls:
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+entity_power_off:
+ sc2235_set_power_off(dev);
entity_cleanup:
media_entity_cleanup(&sensor->sd.entity);
mutex_destroy(&sensor->lock);
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
mutex_destroy(&sensor->lock);
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ sc2235_set_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
return 0;
}
};
MODULE_DEVICE_TABLE(of, sc2235_dt_ids);
+static const struct dev_pm_ops sc2235_pm_ops = {
+ SET_RUNTIME_PM_OPS(sc2235_set_power_off, sc2235_set_power_on, NULL)
+};
+
static struct i2c_driver sc2235_i2c_driver = {
.driver = {
.name = "sc2235",
.of_match_table = sc2235_dt_ids,
+ .pm = &sc2235_pm_ops,
},
.id_table = sc2235_id,
.probe_new = sc2235_probe,
module_i2c_driver(sc2235_i2c_driver);
-MODULE_DESCRIPTION("SC2235 MIPI Camera Subdev Driver");
+MODULE_DESCRIPTION("SC2235 Camera Subdev Driver");
MODULE_LICENSE("GPL");
static int isp_set_power(struct v4l2_subdev *sd, int on)
{
struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
- struct stf_vin2_dev *vin_dev = isp_dev->stfcamss->vin_dev;
st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
mutex_lock(&isp_dev->power_lock);
if (on) {
- if (isp_dev->power_count == 0) {
- /* Needs to enable vin clock before access ISP. */
- vin_dev->hw_ops->vin_top_clk_init(vin_dev);
- vin_dev->hw_ops->vin_clk_enable(vin_dev);
- isp_dev->hw_ops->isp_clk_enable(isp_dev);
- if (!user_config_isp)
- isp_dev->hw_ops->isp_config_set(isp_dev);
- }
+ if (isp_dev->power_count == 0)
+ st_debug(ST_ISP, "turn on isp\n");
isp_dev->power_count++;
} else {
if (isp_dev->power_count == 0)
goto exit;
- if (isp_dev->power_count == 1)
- isp_dev->hw_ops->isp_clk_disable(isp_dev);
isp_dev->power_count--;
}
exit:
mutex_lock(&isp_dev->stream_lock);
if (enable) {
if (isp_dev->stream_count == 0) {
+ isp_dev->hw_ops->isp_clk_enable(isp_dev);
+ if (!user_config_isp)
+ isp_dev->hw_ops->isp_config_set(isp_dev);
interface_type = isp_get_interface_type(&sd->entity);
if (interface_type < 0) {
st_err(ST_ISP, "%s, pipeline not config\n", __func__);
} else {
if (isp_dev->stream_count == 0)
goto exit;
- if (isp_dev->stream_count == 1)
+ if (isp_dev->stream_count == 1) {
isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
+ isp_dev->hw_ops->isp_clk_disable(isp_dev);
+ }
isp_dev->stream_count--;
}
src_ch.type = V4L2_EVENT_SOURCE_CHANGE,
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
#include "stfcamss.h"
goto out;
}
- vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1);
- vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0);
-
- /* Reset device */
- /*Do not configure the CLK before powering on the device,
- *add vin_power_on() to vin_set_power() 2021 1111
- */
- ret = vin_dev->hw_ops->vin_top_clk_init(vin_dev);
- if (ret) {
- st_err(ST_VIN, "Failed to reset device\n");
- goto out;
- }
-
- // /* set the sysctl config */
- // ret = vin_dev->hw_ops->vin_config_set(vin_dev);
- // if (ret) {
- // st_err(ST_VIN, "Failed to config device\n");
- // goto out;
- // }
-
mutex_init(&vin_dev->power_lock);
vin_dev->power_count = 0;
{
struct vin_line *line = v4l2_get_subdevdata(sd);
struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
+ struct stfcamss *stfcamss = vin_dev->stfcamss;
mutex_lock(&line->power_lock);
if (on) {
mutex_lock(&vin_dev->power_lock);
if (on) {
if (vin_dev->power_count == 0) {
- //vin_dev->hw_ops->vin_top_clk_init(vin_dev);
+ pm_runtime_get_sync(stfcamss->dev);
vin_dev->hw_ops->vin_clk_enable(vin_dev);
vin_dev->hw_ops->vin_config_set(vin_dev);
}
}
if (vin_dev->power_count == 1) {
vin_dev->hw_ops->vin_clk_disable(vin_dev);
- //vin_dev->hw_ops->vin_top_clk_deinit(vin_dev);
+ pm_runtime_put_sync(stfcamss->dev);
}
vin_dev->power_count--;
}
}
mutex_unlock(&dummy_buffer->stream_lock);
- if (line->id == VIN_LINE_WR) {
- mutex_lock(&line->stream_lock);
- if (enable) {
- if (line->stream_count == 0) {
+ mutex_lock(&line->stream_lock);
+ if (enable) {
+ if (line->stream_count == 0) {
+ if (line->id == VIN_LINE_WR) {
vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1);
vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 1);
}
- line->stream_count++;
- } else {
- if (line->stream_count == 1) {
+ }
+ line->stream_count++;
+ } else {
+ if (line->stream_count == 1) {
+ if (line->id == VIN_LINE_WR) {
vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0);
vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 0);
}
- line->stream_count--;
}
- mutex_unlock(&line->stream_lock);
+ line->stream_count--;
}
+ mutex_unlock(&line->stream_lock);
if (enable)
vin_enable_output(line);
struct stf_vin2_dev;
struct vin_hw_ops {
- int (*vin_top_clk_init)(struct stf_vin2_dev *vin_dev);
- int (*vin_top_clk_deinit)(struct stf_vin2_dev *vin_dev);
int (*vin_clk_enable)(struct stf_vin2_dev *vin_dev);
int (*vin_clk_disable)(struct stf_vin2_dev *vin_dev);
int (*vin_config_set)(struct stf_vin2_dev *vin_dev);
#include <media/v4l2-async.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
-#include <linux/clk-provider.h>
-#include <linux/pm_runtime.h>
static void vin_intr_clear(void __iomem *sysctrl_base)
{
return IRQ_HANDLED;
}
-static int stf_vin_top_clk_init(struct stf_vin2_dev *vin_dev)
-{
- struct stfcamss *stfcamss = vin_dev->stfcamss;
- int ret;
-
- pm_runtime_enable(stfcamss->dev);
- ret = pm_runtime_get_sync(stfcamss->dev);
- if (ret < 0) {
- dev_err(stfcamss->dev,
- "vin_clk_init: failed to get pm runtime: %d\n", ret);
- return ret;
- }
-
- if (!__clk_is_enabled(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk))
- clk_prepare_enable(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk);
- else
- st_warn(ST_VIN, "noc_bus_clk_isp_axi already enable\n");
-
- clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
- clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
- reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
- reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
-
- return 0;
-}
-
-static int stf_vin_top_clk_deinit(struct stf_vin2_dev *vin_dev)
-{
- struct stfcamss *stfcamss = vin_dev->stfcamss;
-
- reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
- reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
- clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
- clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
-
- pm_runtime_put_sync(stfcamss->dev);
- pm_runtime_disable(stfcamss->dev);
-
- return 0;
-}
-
static int stf_vin_clk_enable(struct stf_vin2_dev *vin_dev)
{
struct stfcamss *stfcamss = vin_dev->stfcamss;
}
struct vin_hw_ops vin_ops = {
- .vin_top_clk_init = stf_vin_top_clk_init,
- .vin_top_clk_deinit = stf_vin_top_clk_deinit,
.vin_clk_enable = stf_vin_clk_enable,
.vin_clk_disable = stf_vin_clk_disable,
.vin_config_set = stf_vin_config_set,
{ .id = "clk_m31dphy_txclkesc_lan0" },
{ .id = "clk_ispcore_2x" },
{ .id = "clk_isp_axi" },
- { .id = "clk_noc_bus_clk_isp_axi" },
};
static struct reset_control_bulk_data stfcamss_resets[] = {
goto err_cam;
}
+ pm_runtime_enable(dev);
+
stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks);
stfcamss->sys_clk = stfcamss_clocks;
stfcamss->nrsts = ARRAY_SIZE(stfcamss_resets);
stfcamss->sys_rst = stfcamss_resets;
- ret = devm_reset_control_bulk_get_exclusive(dev, stfcamss->nrsts,
+ ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
stfcamss->sys_rst);
if (ret) {
st_err(ST_CAMSS, "Failed to get reset controls\n");
stfcamss_unregister_subdevices(stfcamss);
v4l2_device_unregister(&stfcamss->v4l2_dev);
media_device_cleanup(&stfcamss->media_dev);
+ pm_runtime_disable(&pdev->dev);
kfree(stfcamss);
MODULE_DEVICE_TABLE(of, stfcamss_of_match);
+#ifdef CONFIG_PM_SLEEP
+static int stfcamss_suspend(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ struct stfcamss_video *video;
+ struct video_device *vdev;
+ int i = 0;
+
+ for (i = 0; i < VIN_LINE_MAX; i++) {
+ if (vin_dev->line[i].stream_count) {
+ vin_dev->line[i].stream_count ++;
+ video = &vin_dev->line[i].video_out;
+ vdev = &vin_dev->line[i].video_out.vdev;
+ entity = &vdev->entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ v4l2_subdev_call(subdev, video, s_stream, 0);
+ }
+ media_pipeline_stop(&vdev->entity);
+ video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
+ v4l2_pipeline_pm_put(&vdev->entity);
+ }
+ }
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stfcamss_resume(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+ struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ struct stfcamss_video *video;
+ struct video_device *vdev;
+ int i = 0;
+ int ret = 0;
+
+ pm_runtime_force_resume(dev);
+
+ for (i = 0; i < VIN_LINE_MAX; i++) {
+ if (vin_dev->line[i].stream_count) {
+ vin_dev->line[i].stream_count--;
+ video = &vin_dev->line[i].video_out;
+ vdev = &vin_dev->line[i].video_out.vdev;
+
+ ret = v4l2_pipeline_pm_get(&vdev->entity);
+ if (ret < 0)
+ goto err;
+
+ ret = media_pipeline_start(&vdev->entity, &video->stfcamss->pipe);
+ if (ret < 0)
+ goto err_pm_put;
+
+ entity = &vdev->entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto err_pipeline_stop;
+ }
+ }
+ }
+
+ return 0;
+
+err_pipeline_stop:
+ media_pipeline_stop(&vdev->entity);
+err_pm_put:
+ v4l2_pipeline_pm_put(&vdev->entity);
+err:
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int stfcamss_runtime_suspend(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+
+ reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
+ reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
+ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
+ clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
+
+ return 0;
+}
+
+static int stfcamss_runtime_resume(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+
+ clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
+ clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
+ reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
+ reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops stfcamss_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume)
+ SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, stfcamss_runtime_resume, NULL)
+};
+
static struct platform_driver stfcamss_driver = {
.probe = stfcamss_probe,
.remove = stfcamss_remove,
.driver = {
.name = DRV_NAME,
+ .pm = &stfcamss_pm_ops,
.of_match_table = of_match_ptr(stfcamss_of_match),
},
};
STFCLK_M31DPHY_TXCLKESC_LAN0,
STFCLK_ISPCORE_2X,
STFCLK_ISP_AXI,
- STFCLK_NOC_BUS_CLK_ISP_AXI,
STFCLK_NUM
};
.switch_voltage = dw_mci_starfive_switch_voltage,
};
+static const struct of_device_id dw_mci_starfive_match[] = {
+ { .compatible = "starfive,jh7110-sdio",
+ .data = &starfive_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_starfive_match);
+
static int dw_mci_starfive_probe(struct platform_device *pdev)
{
- return dw_mci_pltfm_register(pdev, &starfive_data);
+ const struct dw_mci_drv_data *drv_data;
+ const struct of_device_id *match;
+ int ret;
+
+ match = of_match_node(dw_mci_starfive_match, pdev->dev.of_node);
+ drv_data = match->data;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = dw_mci_pltfm_register(pdev, drv_data);
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return ret;
+ }
+
+ return 0;
}
static int dw_mci_starfive_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return dw_mci_pltfm_remove(pdev);
}
-static const struct of_device_id dw_mci_starfive_match[] = {
- { .compatible = "starfive,jh7110-sdio", },
- {},
+#ifdef CONFIG_PM
+static int dw_mci_starfive_runtime_suspend(struct device *dev)
+{
+ struct dw_mci *host = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(host->biu_clk);
+ clk_disable_unprepare(host->ciu_clk);
+
+ return 0;
+}
+
+static int dw_mci_starfive_runtime_resume(struct device *dev)
+{
+ struct dw_mci *host = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(host->biu_clk);
+ if (ret) {
+ dev_err(host->dev, "Failed to prepare_enable biu_clk clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(host->ciu_clk);
+ if (ret) {
+ dev_err(host->dev, "Failed to prepare_enable ciu_clk clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dw_mci_starfive_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_starfive_runtime_suspend,
+ dw_mci_starfive_runtime_resume, NULL)
};
-MODULE_DEVICE_TABLE(of, dw_mci_starfive_match);
static struct platform_driver dw_mci_starfive_driver = {
.probe = dw_mci_starfive_probe,
.remove = dw_mci_starfive_remove,
.driver = {
.name = "dwmmc_starfive",
+ .pm = &dw_mci_starfive_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_starfive_match,
},
free_irq(ndev->irq, ndev);
close_candev(ndev);
+ pm_runtime_put(priv->dev);
+
return 0;
}
struct ipms_canfd_priv *priv = netdev_priv(ndev);
int ret;
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ dev_err(priv->dev, " %s: pm_runtime_get failed\n", __func__);
+ goto err;
+ }
+
/* Set chip into reset mode */
ret = set_reset_mode(ndev);
if (ret) {
exit_can_start:
free_irq(ndev->irq, ndev);
+err:
+ pm_runtime_put(priv->dev);
exit_irq:
close_candev(ndev);
return ret;
priv->reg_base = addr;
priv->write_reg = canfd_write_reg_le;
priv->read_reg = canfd_read_reg_le;
+
+ pm_runtime_enable(&pdev->dev);
+
priv->can_clk = devm_clk_get(&pdev->dev, "core_clk");
if (IS_ERR(priv->can_clk)) {
dev_err(&pdev->dev, "Device clock not found.\n");
reset_control_assert(priv->resets);
clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
+ pm_runtime_disable(&pdev->dev);
unregister_candev(ndev);
netif_napi_del(&priv->napi);
return 0;
}
+#ifdef CONFIG_PM
+static int canfd_runtime_suspend(struct device *dev)
+{
+ struct ipms_canfd_priv *priv = dev_get_drvdata(dev);
+
+ reset_control_assert(priv->resets);
+ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
+
+ return 0;
+}
+
+static int canfd_runtime_resume(struct device *dev)
+{
+ struct ipms_canfd_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks);
+ if (ret) {
+ dev_err(dev, "Failed to prepare_enable clk\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(priv->resets);
+ if (ret) {
+ dev_err(dev, "Failed to deassert reset\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops canfd_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(canfd_runtime_suspend,
+ canfd_runtime_resume, NULL)
+};
+
static const struct of_device_id canfd_of_match[] = {
{ .compatible = "ipms,can" },
{ }
.remove = canfd_driver_remove,
.driver = {
.name = DRIVER_NAME,
+ .pm = &canfd_pm_ops,
.of_match_table = canfd_of_match,
},
};
#include <linux/pci.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#define PLDA_EP_ENABLE 0
#define PLDA_RP_ENABLE 1
+#define PLDA_LINK_UP 1
+#define PLDA_LINK_DOWN 0
+
+#define PLDA_DATA_LINK_ACTIVE BIT(5)
#define PREF_MEM_WIN_64_SUPPORT BIT(3)
#define PMSG_LTR_SUPPORT BIT(2)
#define PDLA_LINK_SPEED_GEN2 BIT(12)
u32 stg_arfun;
u32 stg_awfun;
u32 stg_rp_nep;
+ u32 stg_lnksta;
int irq;
struct irq_domain *legacy_irq_domain;
struct pci_host_bridge *bridge;
struct pinctrl *pinctrl;
struct pinctrl_state *perst_state_def;
struct pinctrl_state *perst_state_active;
+ struct pinctrl_state *power_state_def;
struct pinctrl_state *power_state_active;
};
}
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
- "starfive,stg-syscon", 3, 0, &args);
+ "starfive,stg-syscon", 4, 0, &args);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to parse starfive,stg-syscon\n");
return -EINVAL;
pcie->stg_arfun = args.args[0];
pcie->stg_awfun = args.args[1];
pcie->stg_rp_nep = args.args[2];
+ pcie->stg_lnksta = args.args[3];
/* Clear all interrupts */
plda_writel(pcie, 0xffffffff, ISTATUS_LOCAL);
return -EINVAL;
}
+ pcie->power_state_def
+ = pinctrl_lookup_state(pcie->pinctrl, "power-default");
+ if (IS_ERR_OR_NULL(pcie->power_state_def)) {
+ dev_err(dev, "Failed to get the power-default pinctrl handle\n");
+ return -EINVAL;
+ }
+
pcie->power_state_active
= pinctrl_lookup_state(pcie->pinctrl, "power-active");
if (IS_ERR_OR_NULL(pcie->power_state_active)) {
- dev_err(dev, "Failed to get the power-default pinctrl handle\n");
+ dev_err(dev, "Failed to get the power-active pinctrl handle\n");
return -EINVAL;
}
if (ret)
dev_err(dev, "Cannot set reset pin to high\n");
}
+}
+
+static int plda_pcie_is_link_up(struct plda_pcie *pcie)
+{
+ struct device *dev = &pcie->pdev->dev;
+ int ret;
+ u32 stg_reg_val;
+
+ /* 100ms timeout value should be enough for Gen1/2 training */
+ ret = regmap_read_poll_timeout(pcie->reg_syscon,
+ pcie->stg_lnksta,
+ stg_reg_val,
+ stg_reg_val & PLDA_DATA_LINK_ACTIVE,
+ 10 * 1000, 100 * 1000);
+
+ /* If the link is down (no device in slot), then exit. */
+ if (ret == -ETIMEDOUT) {
+ dev_info(dev, "Port link down, exit.\n");
+ return PLDA_LINK_DOWN;
+ } else if (ret == 0) {
+ dev_info(dev, "Port link up.\n");
+ return PLDA_LINK_UP;
+ }
+ dev_warn(dev, "Read stg_linksta failed.\n");
+ return ret;
}
static int plda_pcie_probe(struct platform_device *pdev)
goto exit;
}
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
/* Set default bus ops */
bridge->ops = &plda_pcie_ops;
bridge->sysdata = pcie;
plda_pcie_hw_init(pcie);
+ if (plda_pcie_is_link_up(pcie) == PLDA_LINK_DOWN)
+ goto release;
+
ret = pci_host_probe(bridge);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to pci host probe: %d\n", ret);
- goto exit;
+ goto release;
}
if (IS_ENABLED(CONFIG_PCI_MSI)) {
ret = plda_pcie_enable_msi(pcie, bus);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&pdev->dev, "Failed to enable MSI support: %d\n", ret);
+ goto release;
+ }
}
exit:
return ret;
+
+release:
+ if (pcie->power_state_def &&
+ pinctrl_select_state(pcie->pinctrl, pcie->power_state_def))
+ dev_err(dev, "Cannot set power pin to low\n");
+ plda_clk_rst_deinit(pcie);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ pci_free_host_bridge(pcie->bridge);
+ devm_kfree(&pdev->dev, pcie);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
}
static int plda_pcie_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int __maybe_unused plda_pcie_suspend_noirq(struct device *dev)
+{
+ struct plda_pcie *pcie = dev_get_drvdata(dev);
+
+ if (!pcie)
+ return 0;
+
+ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
+
+ return 0;
+}
+
+static int __maybe_unused plda_pcie_resume_noirq(struct device *dev)
+{
+ struct plda_pcie *pcie = dev_get_drvdata(dev);
+ int ret;
+
+ if (!pcie)
+ return 0;
+
+ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
+ if (ret)
+ dev_err(dev, "Failed to enable clocks\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops plda_pcie_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(plda_pcie_suspend_noirq,
+ plda_pcie_resume_noirq)
+};
+#endif
static const struct of_device_id plda_pcie_of_match[] = {
{ .compatible = "plda,pci-xpressrich3-axi"},
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = of_match_ptr(plda_pcie_of_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &plda_pcie_pm_ops,
+#endif
},
.probe = plda_pcie_probe,
.remove = plda_pcie_remove,
static struct platform_driver starfive_jh7110_pinctrl_driver = {
.driver = {
.name = "starfive_jh7110-pinctrl",
+ .pm = &starfive_pinctrl_pm_ops,
.of_match_table = of_match_ptr(starfive_jh7110_pinctrl_of_match),
},
.probe = starfive_jh7110_pinctrl_probe,
#include <dt-bindings/pwm/pwm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/clk.h>
struct pwm_state *state)
{
struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip);
- u32 data_lrc;
- u32 data_hrc;
+ u32 data_lrc, data_hrc, data_ctrl;
u32 pwm_clk_ns = 0;
+ pm_runtime_get_sync(chip->dev);
+
data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm));
data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm));
+ data_ctrl = ioread32(REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm));
pwm_clk_ns = NS_PER_SECOND / pwm->approx_freq;
state->period = data_lrc * pwm_clk_ns;
state->duty_cycle = data_hrc * pwm_clk_ns;
state->polarity = PWM_POLARITY_NORMAL;
- state->enabled = 1;
+ state->enabled = (data_ctrl & PTC_EN) ? true : false;
+
+ pm_runtime_put(chip->dev);
}
static int starfive_pwm_ptc_apply(struct pwm_chip *chip,
s64 multi = pwm->approx_freq;
s64 div = NS_PER_SECOND;
void __iomem *reg_addr;
+ u32 val;
+
+ if (state->enabled) {
+ if (!pwm_is_enabled(dev)) {
+ pm_runtime_get_sync(chip->dev);
+ reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm);
+ val = ioread32(reg_addr);
+ iowrite32(val | PTC_EN | PTC_OE, reg_addr);
+ }
+ } else if (pwm_is_enabled(dev)) {
+ reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm);
+ val = ioread32(reg_addr);
+ iowrite32(val & ~(PTC_EN | PTC_OE), reg_addr);
+ pm_runtime_put(chip->dev);
+ return 0;
+ } else {
+ return 0;
+ }
if (state->duty_cycle > state->period)
state->duty_cycle = state->period;
(state->period > 0 && period_data == 0))
period_data += 1;
- if (state->enabled) {
- duty_data = (u32)(state->duty_cycle * multi / div);
- if (abs(duty_data * div / multi - state->duty_cycle)
- > abs((duty_data + 1) * div / multi - state->duty_cycle) ||
- (state->duty_cycle > 0 && duty_data == 0))
- duty_data += 1;
- } else {
- duty_data = 0;
- }
+ duty_data = (u32)(state->duty_cycle * multi / div);
+ if (abs(duty_data * div / multi - state->duty_cycle)
+ > abs((duty_data + 1) * div / multi - state->duty_cycle) ||
+ (state->duty_cycle > 0 && duty_data == 0))
+ duty_data += 1;
if (state->polarity == PWM_POLARITY_NORMAL)
data_hrc = period_data - duty_data;
reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm);
iowrite32(0, reg_addr);
- reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm);
- iowrite32(PTC_EN | PTC_OE, reg_addr);
-
return 0;
}
return PTR_ERR(pwm->rst);
}
- ret = clk_prepare_enable(pwm->clk);
- if (ret) {
- dev_err(dev,
- "Failed to enable pwm clock, %d\n", ret);
- return ret;
- }
- reset_control_deassert(pwm->rst);
-
ret = of_property_read_u32(node, "starfive,approx-freq",
&clk_apb_freq);
if (!ret)
platform_set_drvdata(pdev, pwm);
+ pm_runtime_enable(dev);
+
return 0;
}
clk_disable_unprepare(pwm->clk);
pwmchip_remove(chip);
+ pm_runtime_disable(&dev->dev);
+
return 0;
}
+#if defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP)
+static int __maybe_unused starfive_pwm_runtime_suspend(struct device *dev)
+{
+ struct starfive_pwm_ptc_device *pwm = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "starfive pwm runtime suspending..");
+ reset_control_assert(pwm->rst);
+ clk_disable_unprepare(pwm->clk);
+
+ return 0;
+}
+
+static int __maybe_unused starfive_pwm_runtime_resume(struct device *dev)
+{
+ struct starfive_pwm_ptc_device *pwm = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "starfive pwm runtime resuming..");
+ ret = clk_prepare_enable(pwm->clk);
+ if (ret)
+ dev_err(dev,
+ "Failed to resume pwm clock, %d\n", ret);
+ reset_control_deassert(pwm->rst);
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops starfive_pwm_pm_ops = {
+ SET_RUNTIME_PM_OPS(starfive_pwm_runtime_suspend,
+ starfive_pwm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
static const struct of_device_id starfive_pwm_ptc_of_match[] = {
{ .compatible = "starfive,jh7110-pwm" },
{},
.driver = {
.name = "pwm-starfive-ptc",
.of_match_table = of_match_ptr(starfive_pwm_ptc_of_match),
+ .pm = &starfive_pwm_pm_ops,
},
};
module_platform_driver(starfive_pwm_ptc_driver);
ATC260x PMICs. This will enable support for all the software
controllable DCDC/LDO regulators.
+config REGULATOR_AXP15060
+ tristate "X-POWERS AXP15060 PMIC Regulators"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver provides support for the voltage regulators on the
+ AXP15060 PMIC.
+
config REGULATOR_AXP20X
tristate "X-POWERS AXP20X PMIC Regulators"
depends on MFD_AXP20X
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o
+obj-$(CONFIG_REGULATOR_AXP15060) += axp15060-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_BD71815) += bd71815-regulator.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Starfive Technology Co., Ltd.
+ * Author: Kevin Xie <kevin.xie@starfivetech.com>
+ */
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/axp15060.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#define AXP15060_ON_OFF_CTRL_1 0x10
+#define AXP15060_ON_OFF_CTRL_2 0x11
+#define AXP15060_ON_OFF_CTRL_3 0x12
+#define AXP15060_VOL_CTRL_DCDC_1 0x13
+#define AXP15060_VOL_CTRL_DCDC_2 0x14
+#define AXP15060_VOL_CTRL_DCDC_3 0x15
+#define AXP15060_VOL_CTRL_DCDC_4 0x16
+#define AXP15060_VOL_CTRL_DCDC_5 0x17
+#define AXP15060_VOL_CTRL_DCDC_6 0x18
+#define AXP15060_VOL_CTRL_ALDO_1 0x19
+#define AXP15060_DCDC_MODE_CTRL_1 0x1A
+#define AXP15060_DCDC_MODE_CTRL_2 0x1B
+
+#define AXP15060_OUTPUT_MONITOR_OFF_DISCHARGE 0x1E
+#define AXP15060_IRQ_PWROK_VOFF_SETTING 0x1F
+#define AXP15060_VOL_CTRL_ALDO_2 0x20
+#define AXP15060_VOL_CTRL_ALDO_3 0x21
+#define AXP15060_VOL_CTRL_ALDO_4 0x22
+#define AXP15060_VOL_CTRL_ALDO_5 0x23
+#define AXP15060_VOL_CTRL_BLDO_1 0x24
+#define AXP15060_VOL_CTRL_BLDO_2 0x25
+#define AXP15060_VOL_CTRL_BLDO_3 0x26
+#define AXP15060_VOL_CTRL_BLDO_4 0x27
+#define AXP15060_VOL_CTRL_BLDO_5 0x28
+#define AXP15060_VOL_CTRL_CLDO_1 0x29
+#define AXP15060_VOL_CTRL_CLDO_2 0x2A
+/* CLDO3 voltage ctrl and CLDO3/GPIO1/Wakeup ctrl */
+#define AXP15060_VOL_CTRL_CLDO_3 0x2B
+#define AXP15060_CLDO_4_GPIO_2_CTRL 0x2C
+#define AXP15060_VOL_CTRL_CLDO_4 0x2D
+#define AXP15060_VOL_CTRL_CPUSLDO 0x2E
+
+#define AXP15060_PWR_WAKEUP_CTRL 0x31
+/* Power disable and power down sequence */
+#define AXP15060_PWR_DISABLE 0x32
+
+#define AXP15060_POK_SETTING 0x36
+
+#define AXP15060_INTERFACE_MODE_SEL 0x3E
+
+#define AXP15060_IRQ_ENABLE_1 0x40
+#define AXP15060_IRQ_ENABLE_2 0x41
+
+#define AXP15060_IRQ_STATUS_1 0x48
+#define AXP15060_IRQ_STATUS_2 0x49
+
+/* AXP15060_ON_OFF_CTRL_1 */
+#define AXP15060_PWR_OUT_DCDC1_MASK BIT(0)
+#define AXP15060_PWR_OUT_DCDC2_MASK BIT(1)
+#define AXP15060_PWR_OUT_DCDC3_MASK BIT(2)
+#define AXP15060_PWR_OUT_DCDC4_MASK BIT(3)
+#define AXP15060_PWR_OUT_DCDC5_MASK BIT(4)
+#define AXP15060_PWR_OUT_DCDC6_MASK BIT(5)
+
+/* AXP15060_ON_OFF_CTRL_2 */
+#define AXP15060_PWR_OUT_ALDO1_MASK BIT(0)
+#define AXP15060_PWR_OUT_ALDO2_MASK BIT(1)
+#define AXP15060_PWR_OUT_ALDO3_MASK BIT(2)
+#define AXP15060_PWR_OUT_ALDO4_MASK BIT(3)
+#define AXP15060_PWR_OUT_ALDO5_MASK BIT(4)
+#define AXP15060_PWR_OUT_BLDO1_MASK BIT(5)
+#define AXP15060_PWR_OUT_BLDO2_MASK BIT(6)
+#define AXP15060_PWR_OUT_BLDO3_MASK BIT(7)
+
+/* AXP15060_ON_OFF_CTRL_3 */
+#define AXP15060_PWR_OUT_BLDO4_MASK BIT(0)
+#define AXP15060_PWR_OUT_BLDO5_MASK BIT(1)
+#define AXP15060_PWR_OUT_CLDO1_MASK BIT(2)
+#define AXP15060_PWR_OUT_CLDO2_MASK BIT(3)
+#define AXP15060_PWR_OUT_CLDO3_MASK BIT(4)
+#define AXP15060_PWR_OUT_CLDO4_MASK BIT(5)
+#define AXP15060_PWR_OUT_CPULDO_MASK BIT(6)
+#define AXP15060_PWR_OUT_SWITCH_MASK BIT(7)
+
+#define AXP15060_ALDO1_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_ALDO2_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_ALDO3_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_ALDO4_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_ALDO5_V_OUT_MASK GENMASK(4, 0)
+
+#define AXP15060_BLDO1_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_BLDO2_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_BLDO3_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_BLDO4_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_BLDO5_V_OUT_MASK GENMASK(4, 0)
+
+#define AXP15060_CLDO1_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_CLDO2_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_CLDO3_V_OUT_MASK GENMASK(4, 0)
+#define AXP15060_CLDO4_V_OUT_MASK GENMASK(5, 0)
+#define AXP15060_CPUSLDO_V_OUT_MASK GENMASK(3, 0)
+
+#define AXP15060_DCDC1_V_OUT_MASK GENMASK(4, 0)
+/* DCDC2 bit7 is set by fw, which is different from datasheet desc. */
+#define AXP15060_DCDC2_V_OUT_MASK GENMASK(7, 0)
+#define AXP15060_DCDC3_V_OUT_MASK GENMASK(6, 0)
+#define AXP15060_DCDC4_V_OUT_MASK GENMASK(6, 0)
+#define AXP15060_DCDC5_V_OUT_MASK GENMASK(6, 0)
+#define AXP15060_DCDC6_V_OUT_MASK GENMASK(4, 0)
+
+#define AXP15060_DCDC2_V_OUT_RANGE1_7bit_500mV_START 0x00
+#define AXP15060_DCDC2_V_OUT_RANGE1_7bit_1200mV_END 0x46
+#define AXP15060_DCDC2_V_OUT_RANGE2_7bit_1220mV_START 0x47
+#define AXP15060_DCDC2_V_OUT_RANGE2_7bit_1540mV_END 0x57
+#define AXP15060_DCDC2_V_OUT_RANGE1_500mV_START 0x80
+#define AXP15060_DCDC2_V_OUT_RANGE1_1200mV_END 0xC6
+#define AXP15060_DCDC2_V_OUT_RANGE2_1220mV_START 0xC7
+#define AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END 0xD7
+#define AXP15060_DCDC2_NUM_VOLTAGES \
+ (AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END + 1)
+
+static const struct linear_range axp15060_dcdc2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000,
+ AXP15060_DCDC2_V_OUT_RANGE1_7bit_500mV_START,
+ AXP15060_DCDC2_V_OUT_RANGE1_7bit_1200mV_END,
+ 10000),
+ REGULATOR_LINEAR_RANGE(1220000,
+ AXP15060_DCDC2_V_OUT_RANGE2_7bit_1220mV_START,
+ AXP15060_DCDC2_V_OUT_RANGE2_7bit_1540mV_END,
+ 20000),
+ REGULATOR_LINEAR_RANGE(500000,
+ AXP15060_DCDC2_V_OUT_RANGE1_500mV_START,
+ AXP15060_DCDC2_V_OUT_RANGE1_1200mV_END,
+ 10000),
+ REGULATOR_LINEAR_RANGE(1220000,
+ AXP15060_DCDC2_V_OUT_RANGE2_1220mV_START,
+ AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END,
+ 20000),
+};
+
+static const struct regmap_range axp15060_writeable_ranges[] = {
+ regmap_reg_range(AXP15060_ON_OFF_CTRL_1, AXP15060_DCDC_MODE_CTRL_2),
+ regmap_reg_range(AXP15060_OUTPUT_MONITOR_OFF_DISCHARGE,
+ AXP15060_VOL_CTRL_CPUSLDO),
+ regmap_reg_range(AXP15060_PWR_WAKEUP_CTRL, AXP15060_PWR_DISABLE),
+ regmap_reg_range(AXP15060_POK_SETTING, AXP15060_POK_SETTING),
+ regmap_reg_range(AXP15060_INTERFACE_MODE_SEL, AXP15060_INTERFACE_MODE_SEL),
+ regmap_reg_range(AXP15060_IRQ_ENABLE_1, AXP15060_IRQ_ENABLE_2),
+ regmap_reg_range(AXP15060_IRQ_STATUS_1, AXP15060_IRQ_STATUS_2),
+};
+
+static const struct regmap_access_table axp15060_writeable_table = {
+ .yes_ranges = axp15060_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp15060_writeable_ranges),
+};
+
+static const struct regmap_config axp15060_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp15060_writeable_table,
+ .max_register = AXP15060_IRQ_STATUS_2,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regulator_ops axp15060_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static const struct regulator_ops axp15060_sw_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops axp15060_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops axp15060_range_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define AXP15060_DESC_FIXED(_id, _match, _volt) \
+ { \
+ .name = (_match), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP15060_ID_##_id, \
+ .n_voltages = 1, \
+ .owner = THIS_MODULE, \
+ .min_uV = (_volt) * 1000, \
+ .ops = &axp15060_fixed_ops, \
+ }
+
+#define AXP_DESC_SW(_id, _match, _ereg, _emask) \
+ { \
+ .name = (_match), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP15060_ID_##_id, \
+ .owner = THIS_MODULE, \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp15060_sw_ops, \
+ }
+
+#define AXP15060_DESC(_id, _match, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ { \
+ .name = (_match), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP15060_ID_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp15060_ops, \
+ }
+
+#define AXP15060_DESC_RANGES(_id, _match, _ranges, _n_voltages,\
+ _vreg, _vmask, _ereg, _emask) \
+ { \
+ .name = (_match), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP15060_ID_##_id, \
+ .n_voltages = (_n_voltages), \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .linear_ranges = (_ranges), \
+ .n_linear_ranges = ARRAY_SIZE(_ranges), \
+ .ops = &axp15060_range_ops, \
+ }
+/* Only register the regulators that needed to be controlled(onoff/vol) */
+static const struct regulator_desc axp15060_regulators[] = {
+ AXP15060_DESC(ALDO1, "mipi_0p9", 700, 3300, 100,
+ AXP15060_VOL_CTRL_ALDO_1, AXP15060_ALDO1_V_OUT_MASK,
+ AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO1_MASK),
+
+ AXP15060_DESC(ALDO3, "hdmi_1p8", 700, 3300, 100,
+ AXP15060_VOL_CTRL_ALDO_3, AXP15060_ALDO3_V_OUT_MASK,
+ AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO3_MASK),
+
+ AXP15060_DESC(ALDO4, "sdio_vdd", 700, 3300, 100,
+ AXP15060_VOL_CTRL_ALDO_4, AXP15060_ALDO4_V_OUT_MASK,
+ AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO4_MASK),
+
+ AXP15060_DESC(ALDO5, "hdmi_0p9", 700, 3300, 100,
+ AXP15060_VOL_CTRL_ALDO_5, AXP15060_ALDO5_V_OUT_MASK,
+ AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO5_MASK),
+
+ AXP15060_DESC_RANGES(DCDC2, "cpu_vdd",
+ axp15060_dcdc2_ranges, AXP15060_DCDC2_NUM_VOLTAGES,
+ AXP15060_VOL_CTRL_DCDC_2, AXP15060_DCDC2_V_OUT_MASK,
+ AXP15060_ON_OFF_CTRL_1, AXP15060_PWR_OUT_DCDC2_MASK),
+};
+
+static struct of_regulator_match axp15060_matches[] = {
+ { .name = "mipi_0p9", },
+ { .name = "hdmi_1p8", },
+ { .name = "sdio_vdd", },
+ { .name = "hdmi_0p9", },
+ { .name = "cpu_vdd", },
+};
+
+static int axp15060_i2c_probe(struct i2c_client *i2c)
+{
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct device_node *np, *regulators;
+ struct regmap *regmap;
+ int i, ret;
+
+ np = of_node_get(i2c->dev.of_node);
+ if (!np)
+ return -EINVAL;
+
+ regulators = of_get_child_by_name(np, "regulators");
+ if (!regulators) {
+ dev_err(&i2c->dev, "Regulators node not found\n");
+ return -EINVAL;
+ }
+
+ i = of_regulator_match(&i2c->dev, regulators, axp15060_matches,
+ ARRAY_SIZE(axp15060_matches));
+ of_node_put(i2c->dev.of_node);
+ if (i < 0) {
+ dev_err(&i2c->dev, "Failed to match regulators\n");
+ return -EINVAL;
+ }
+
+ regmap = devm_regmap_init_i2c(i2c, &axp15060_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < AXP15060_MAX_REGULATORS; i++) {
+ config.dev = &i2c->dev;
+ config.regmap = regmap;
+ config.init_data = axp15060_matches[i].init_data;
+
+ rdev = devm_regulator_register(&i2c->dev,
+ &axp15060_regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&i2c->dev,
+ "Failed to register AXP15060 regulator\n");
+ return PTR_ERR(rdev);
+ }
+ dev_info(&i2c->dev, "Register %s done! vol range:%d ~ %d mV\n",
+ rdev->desc->name,
+ (rdev->constraints->min_uV) / 1000,
+ (rdev->constraints->max_uV) / 1000);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id axp15060_i2c_id[] = {
+ {"axp15060_reg", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, axp15060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id axp15060_dt_ids[] = {
+ { .compatible = "stf,axp15060-regulator",
+ .data = &axp15060_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, axp15060_dt_ids);
+#endif
+
+static struct i2c_driver axp15060_regulator_driver = {
+ .driver = {
+ .name = "axp15060-regulator",
+ .of_match_table = of_match_ptr(axp15060_dt_ids),
+ },
+ .probe_new = axp15060_i2c_probe,
+ .id_table = axp15060_i2c_id,
+};
+
+module_i2c_driver(axp15060_regulator_driver);
+
+MODULE_AUTHOR("Kevin Xie <kevin.xie@starfivetech.com>");
+MODULE_DESCRIPTION("Regulator device driver for X-Powers AXP15060");
+MODULE_LICENSE("GPL v2");
struct device *dev = &pdev->dev;
struct sft_rtc *srtc;
struct rtc_time tm;
+ struct irq_desc *desc;
int ret;
srtc = devm_kzalloc(dev, sizeof(*srtc), GFP_KERNEL);
srtc->rtc_dev->ops = &starfive_rtc_ops;
device_init_wakeup(dev, true);
+ desc = irq_to_desc(srtc->rtc_irq);
+ irq_desc_get_chip(desc)->flags = IRQCHIP_SKIP_SET_WAKE;
+
/* Always use 24-hour mode and keep the RTC values */
sft_rtc_set_mode(srtc, RTC_HOUR_MODE_24H);
.driver = {
.name = "starfive-rtc",
.of_match_table = sft_rtc_of_match,
- .pm = &sft_rtc_pm_ops,
+ .pm = &sft_rtc_pm_ops,
},
.probe = sft_rtc_probe,
.remove = sft_rtc_remove,
*/
static void readwriter(struct pl022 *pl022)
{
-
/*
* The FIFO depth is different between primecell variants.
* I believe filling in too much in the FIFO might cause
master->cleanup = pl022_cleanup;
master->setup = pl022_setup;
/* If open CONFIG_PM, auto_runtime_pm should be false when of-platform.*/
- if (platform_flag)
- master->auto_runtime_pm = false;
- else
- master->auto_runtime_pm = true;
+ master->auto_runtime_pm = true;
master->transfer_one_message = pl022_transfer_one_message;
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
master->rt = platform_info->rt;
/* Register with the SPI framework */
amba_set_drvdata(adev, pl022);
+
if (platform_flag)
status = spi_register_master(master);
else
goto err_spi_register;
}
dev_dbg(dev, "probe succeeded\n");
-
+ if (!platform_flag)
+ platform_info->autosuspend_delay = 100;
/* let runtime pm put suspend */
if (platform_info->autosuspend_delay > 0) {
dev_info(&adev->dev,
platform_info->autosuspend_delay);
pm_runtime_use_autosuspend(dev);
}
- pm_runtime_put(dev);
+ if (platform_flag)
+ clk_disable_unprepare(pl022->clk);
+ else
+ pm_runtime_put(dev);
return 0;
pinctrl_pm_select_sleep_state(dev);
- dev_dbg(dev, "suspended\n");
+ dev_dbg(dev, "starfive spi suspended\n");
+
return 0;
}
/* Start the queue running */
ret = spi_master_resume(pl022->master);
if (!ret)
- dev_dbg(dev, "resumed\n");
+ dev_dbg(dev, "starfive spi resumed\n");
return ret;
}
clk_disable_unprepare(pl022->clk);
pinctrl_pm_select_idle_state(dev);
+ dev_dbg(dev, "starfive spi runtime suspend");
+
return 0;
}
pinctrl_pm_select_default_state(dev);
clk_prepare_enable(pl022->clk);
+ dev_dbg(dev, "stafive spi runtime resume");
return 0;
}
#endif
if (ret)
goto err_probe;
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
-
ret = pl022_probe(pcdev, &id);
+ struct pl022 *pl022 = amba_get_drvdata(pcdev);
+
+ pl022->master->dev.parent = &pdev->dev;
+ platform_set_drvdata(pdev, pl022);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
if (ret) {
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
size = resource_size(pdev->resource);
release_mem_region(pdev->resource->start, size);
tasklet_disable(&pl022->pump_transfers);
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}
goto exit;
}
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
dev_info(dev, "usb mode %d %s probe success\n",
data->mode, data->usb2_only ? "2.0" : "3.0");
static int cdns_starfive_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+ pm_runtime_get_sync(dev);
device_for_each_child(dev, NULL, cdns_starfive_remove_core);
+ reset_control_assert(data->resets);
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
platform_set_drvdata(pdev, NULL);
return 0;
}
+#ifdef CONFIG_PM
+static int cdns_starfive_resume(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_set_rate(data->usb_125m_clk, USB_125M_CLK_RATE);
+ if (ret)
+ goto err;
+
+ ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
+ if (ret)
+ goto err;
+
+ ret = reset_control_deassert(data->resets);
+err:
+ return ret;
+}
+
+static int cdns_starfive_suspend(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+ reset_control_assert(data->resets);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cdns_starfive_pm_ops = {
+ SET_RUNTIME_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume)
+};
+
static const struct of_device_id cdns_starfive_of_match[] = {
{ .compatible = "starfive,jh7110-cdns3", },
{},
.driver = {
.name = "cdns3-starfive",
.of_match_table = cdns_starfive_of_match,
+ .pm = &cdns_starfive_pm_ops,
},
};
#include <linux/watchdog.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
+#include <linux/pm_runtime.h>
#define WDT_INT_DIS BIT(0)
#define DELAY_US 0
__MODULE_STRING(STARFIVE_WATCHDOG_ATBOOT));
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
+MODULE_PARM_DESC(soft_noboot,
+ "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
struct starfive_wdt_variant_t {
u32 unlock_key;
};
static const struct of_device_id starfive_wdt_match[] = {
- { .compatible = "starfive,jh7100-wdt",
- .data = &drv_data_jh7100 },
- { .compatible = "starfive,jh7110-wdt",
- .data = &drv_data_jh7110 },
+ { .compatible = "starfive,jh7100-wdt", .data = &drv_data_jh7100 },
+ { .compatible = "starfive,jh7110-wdt", .data = &drv_data_jh7110 },
{},
};
MODULE_DEVICE_TABLE(of, starfive_wdt_match);
return 0;
}
+static int starfive_wdt_pm_stop(struct watchdog_device *wdd)
+{
+ struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ starfive_wdt_stop(wdd);
+ pm_runtime_put(wdt->dev);
+
+ return 0;
+}
+
static int starfive_wdt_start(struct watchdog_device *wdd)
{
struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
+ pm_runtime_get_sync(wdt->dev);
+
spin_lock(&wdt->lock);
starfive_wdt_unlock(wdt);
static const struct watchdog_ops starfive_wdt_ops = {
.owner = THIS_MODULE,
.start = starfive_wdt_start,
- .stop = starfive_wdt_stop,
+ .stop = starfive_wdt_pm_stop,
.ping = starfive_wdt_keepalive,
.set_timeout = starfive_wdt_set_timeout,
.restart = starfive_wdt_restart,
int started = 0;
int ret;
+ pm_runtime_enable(dev);
+
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
*/
starfive_wdt_stop(&wdt->wdt_device);
}
+ clk_disable_unprepare(wdt->core_clk);
+ clk_disable_unprepare(wdt->apb_clk);
platform_set_drvdata(pdev, wdt);
return 0;
- err_unregister:
+err_unregister:
watchdog_unregister_device(&wdt->wdt_device);
-
- err:
+err:
return ret;
}
watchdog_unregister_device(&wdt->wdt_device);
clk_disable_unprepare(wdt->core_clk);
+ clk_disable_unprepare(wdt->apb_clk);
+ pm_runtime_disable(wdt->dev);
return 0;
}
starfive_wdt_mask_and_disable_reset(wdt, true);
- starfive_wdt_stop(&wdt->wdt_device);
-
+ starfive_wdt_pm_stop(&wdt->wdt_device);
}
#ifdef CONFIG_PM_SLEEP
/* Note that WTCNT doesn't need to be saved. */
starfive_wdt_stop(&wdt->wdt_device);
+ pm_runtime_force_suspend(dev);
starfive_wdt_lock(wdt);
/* Restore watchdog state. */
starfive_wdt_set_relod_count(wdt, wdt->reload);
+ pm_runtime_force_resume(dev);
+
+ starfive_wdt_restart(&wdt->wdt_device, 0, NULL);
+
ret = starfive_wdt_mask_and_disable_reset(wdt, false);
if (ret < 0)
return ret;
starfive_wdt_lock(wdt);
- dev_info(dev, "watchdog resume\n")
-
return 0;
}
#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(starfive_wdt_pm_ops, starfive_wdt_suspend,
- starfive_wdt_resume);
+#ifdef CONFIG_PM
+
+static int starfive_wdt_runtime_suspend(struct device *dev)
+{
+ struct starfive_wdt *wdt = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(wdt->apb_clk);
+ clk_disable_unprepare(wdt->core_clk);
+
+ return 0;
+}
+
+static int starfive_wdt_runtime_resume(struct device *dev)
+{
+ struct starfive_wdt *wdt = dev_get_drvdata(dev);
+
+ clk_prepare_enable(wdt->apb_clk);
+ clk_prepare_enable(wdt->core_clk);
+
+ return 0;
+}
+
+#endif /*CONFIG_PM*/
+
+static const struct dev_pm_ops starfive_wdt_pm_ops = {
+ SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume)
+};
static struct platform_driver starfive_starfive_wdt_driver = {
.probe = starfive_wdt_probe,
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Starfive Technology Co., Ltd.
+ * Author: Mason Huo <mason.huo@starfivetech.com>
+ */
+
+#ifndef __LINUX_REGULATOR_AXP15060_H
+#define __LINUX_REGULATOR_AXP15060_H
+
+/* Hide useless or always-on regulators */
+/*
+enum axp15060_reg_id {
+ AXP15060_ID_ALDO1 = 0,
+ AXP15060_ID_ALDO2,
+ AXP15060_ID_ALDO3,
+ AXP15060_ID_ALDO4,
+ AXP15060_ID_ALDO5,
+ AXP15060_ID_BLDO1,
+ AXP15060_ID_BLDO2,
+ AXP15060_ID_BLDO3,
+ AXP15060_ID_BLDO4,
+ AXP15060_ID_BLDO5,
+ AXP15060_ID_CLDO1,
+ AXP15060_ID_CLDO2,
+ AXP15060_ID_CLDO3,
+ AXP15060_ID_CLDO4,
+ AXP15060_ID_CPUSLDO,
+ AXP15060_ID_DCDC1,
+ AXP15060_ID_DCDC2,
+ AXP15060_ID_DCDC3,
+ AXP15060_ID_DCDC4,
+ AXP15060_ID_DCDC5,
+ AXP15060_ID_DCDC6,
+ AXP15060_MAX_REGULATORS,
+};
+*/
+
+enum axp15060_reg_id {
+ AXP15060_ID_ALDO1 = 0,
+ AXP15060_ID_ALDO3,
+ AXP15060_ID_ALDO4,
+ AXP15060_ID_ALDO5,
+ AXP15060_ID_DCDC2,
+ AXP15060_MAX_REGULATORS,
+};
+
+#endif /* __LINUX_REGULATOR_AXP15060_H */
}
if (txrx == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = clk_prepare_enable(dev->clks_dac_bclk);
- if (ret) {
- dev_err(dev->dev, "%s: failed to enable clks_dac_bclk\n", __func__);
- return ret;
- }
-
ret = clk_set_parent(dev->clks_bclk, dev->clks_dac_bclk);
if (ret) {
dev_err(dev->dev, "Can't set clock source for clks_bclk: %d\n", ret);
return ret;
}
- ret = clk_prepare_enable(dev->clks_dac_lrck);
- if (ret) {
- dev_err(dev->dev, "%s: failed to enable clks_dac_lrck\n", __func__);
- return ret;
- }
-
ret = clk_set_parent(dev->clks_lrclk, dev->clks_dac_lrck);
if (ret) {
dev_err(dev->dev, "Can't set clock source for clks_lrclk: %d\n", ret);
return ret;
}
} else if (txrx == SNDRV_PCM_STREAM_CAPTURE) {
- ret = clk_prepare_enable(dev->clks[CLK_ADC_BCLK_EXT]);
- if (ret) {
- dev_err(dev->dev, "%s: failed to enable CLK_ADC_BCLK_EXT\n", __func__);
- return ret;
- }
-
ret = clk_set_parent(dev->clks[CLK_ADC_RX_BCLK], dev->clks[CLK_ADC_BCLK_EXT]);
if (ret) {
dev_err(dev->dev, "Can't set clock source for CLK_ADC_RX_BCLK: %d\n", ret);
return ret;
}
- ret = clk_prepare_enable(dev->clks[CLK_ADC_LRCK_EXT]);
- if (ret) {
- dev_err(dev->dev, "%s: failed to enable CLK_ADC_LRCK_EXT\n", __func__);
- return ret;
- }
-
ret = clk_set_parent(dev->clks[CLK_ADC_RX_LRCK], dev->clks[CLK_ADC_LRCK_EXT]);
if (ret) {
dev_err(dev->dev, "Can't set clock source for CLK_ADC_RX_LRCK: %d\n", ret);
if (dw_dev->capability & DW_I2S_MASTER)
clk_disable(dw_dev->clk);
+ else {
+ if (dw_dev->capability & DWC_I2S_PLAY) {
+ clk_disable_unprepare(dw_dev->clks_mclk_out);
+ clk_disable_unprepare(dw_dev->clks_bclk_mst);
+ clk_disable_unprepare(dw_dev->clks_4ch_apb);
+ } else {
+ clk_disable_unprepare(dw_dev->clks[CLK_ADC_BCLK]);
+ clk_disable_unprepare(dw_dev->clks[CLK_ADC_APB]);
+ }
+ }
return 0;
}
static int dw_i2s_runtime_resume(struct device *dev)
{
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+ int ret;
if (dw_dev->capability & DW_I2S_MASTER)
clk_enable(dw_dev->clk);
+ else {
+ if (dw_dev->capability & DWC_I2S_PLAY) {
+ ret = clk_prepare_enable(dw_dev->clks_4ch_apb);
+ if (ret) {
+ dev_err(dev, "failed to enable clks_4ch_apb\n");
+ goto failed_enable;
+ }
+ ret = clk_prepare_enable(dw_dev->clks_bclk_mst);
+ if (ret) {
+ dev_err(dev, "failed to enable clks_bclk_mst\n");
+ goto failed_enable;
+ }
+ ret = clk_prepare_enable(dw_dev->clks_mclk_out);
+ if (ret) {
+ dev_err(dev, "failed to enable clks_mclk_out\n");
+ goto failed_enable;
+ }
+ } else {
+ ret = clk_prepare_enable(dw_dev->clks[CLK_ADC_BCLK]);
+ if (ret) {
+ dev_err(dev, "failed to enable CLK_ADC_BCLK\n");
+ goto failed_enable;
+ }
+ ret = clk_prepare_enable(dw_dev->clks[CLK_ADC_APB]);
+ if (ret) {
+ dev_err(dev, "failed to enable CLK_ADC_APB\n");
+ goto failed_enable;
+ }
+ }
+ }
return 0;
+
+failed_enable:
+ return ret;
}
static int dw_i2s_suspend(struct snd_soc_component *component)
{
struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+ int ret;
if (dev->capability & DW_I2S_MASTER)
clk_disable(dev->clk);
+
+ ret = pm_runtime_force_suspend(component->dev);
+ if (ret)
+ return ret;
+
return 0;
}
struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *dai;
int stream;
+ int ret;
if (dev->capability & DW_I2S_MASTER)
clk_enable(dev->clk);
+ ret = pm_runtime_force_resume(component->dev);
+ if (ret)
+ return ret;
+
for_each_component_dais(component, dai) {
for_each_pcm_streams(stream)
if (snd_soc_dai_stream_active(dai, stream))
dev->clks[CLK_ADC_BCLK_EXT] = clks[9].clk;
dev->clks[CLK_ADC_LRCK_EXT] = clks[10].clk;
- ret = clk_prepare_enable(dev->clks[CLK_ADC_APB0]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_APB0\n", __func__);
- goto disable_APB0_clk;
- }
-
ret = clk_prepare_enable(dev->clks[CLK_ADC_APB]);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_APB\n", __func__);
goto disable_APB_clk;
}
- ret = clk_prepare_enable(dev->clks[CLK_ADC_AUDROOT]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_AUDROOT\n", __func__);
- goto disable_audroot_clk;
- }
-
ret = clk_set_rate(dev->clks[CLK_ADC_AUDROOT], 204800000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for CLK_ADC_MCLK \n");
goto disable_audroot_clk;
}
- ret = clk_prepare_enable(dev->clks[CLK_ADC_MCLK_INNER]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_MCLK_INNER\n", __func__);
- goto disable_inner_clk;
- }
-
ret = clk_set_rate(dev->clks[CLK_ADC_MCLK_INNER], 4096000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for CLK_ADC_MCLK \n");
- goto disable_inner_clk;
+ goto disable_audroot_clk;
}
ret = clk_prepare_enable(dev->clks[CLK_ADC_BCLK]);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_BCLK\n", __func__);
- goto disable_bclk;
- }
-
- ret = clk_prepare_enable(dev->clks[CLK_ADC_LRCLK]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_LRCLK\n", __func__);
- goto disable_lrclk;
- }
-
- ret = clk_prepare_enable(dev->clks[CLK_ADC_RX_BCLK]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_RX_BCLK\n", __func__);
- goto disable_rx_bclk;
+ goto disable_audroot_clk;
}
- ret = clk_prepare_enable(dev->clks[CLK_ADC_RX_LRCK]);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_RX_LRCK\n", __func__);
- goto disable_rx_lrclk;
- }
dev_dbg(&pdev->dev, "dev->clks[CLK_ADC_APB0] = %lu \n", clk_get_rate(dev->clks[CLK_ADC_APB0]));
dev_dbg(&pdev->dev, "dev->clks[CLK_ADC_APB] = %lu \n", clk_get_rate(dev->clks[CLK_ADC_APB]));
dev_dbg(&pdev->dev, "dev->clks[CLK_ADC_BCLK] = %lu \n", clk_get_rate(dev->clks[CLK_ADC_BCLK]));
dev->rstc_rx = devm_reset_control_array_get_exclusive(&pdev->dev);
if (IS_ERR(dev->rstc_rx)) {
dev_err(&pdev->dev, "%s: failed to get rstc_rx reset control\n", __func__);
- goto disable_rx_lrclk;
+ goto disable_rst;
}
ret = reset_control_assert(dev->rstc_rx);
if (ret) {
dev_err(&pdev->dev, "%s: failed to reset control assert rstc_rx\n", __func__);
- goto disable_rx_lrclk;
+ goto disable_rst;
}
udelay(5);
ret = reset_control_deassert(dev->rstc_rx);
if (ret) {
dev_err(&pdev->dev, "%s: failed to reset control deassert rstc_rx\n", __func__);
- goto disable_rx_lrclk;
+ goto disable_rst;
}
/*i2srx_3ch_adc_enable*/
return 0;
-disable_rx_lrclk:
- clk_disable_unprepare(dev->clks[CLK_ADC_RX_LRCK]);
-disable_rx_bclk:
- clk_disable_unprepare(dev->clks[CLK_ADC_RX_BCLK]);
-disable_lrclk:
- clk_disable_unprepare(dev->clks[CLK_ADC_LRCLK]);
-disable_bclk:
+disable_rst:
clk_disable_unprepare(dev->clks[CLK_ADC_BCLK]);
-disable_inner_clk:
- clk_disable_unprepare(dev->clks[CLK_ADC_MCLK_INNER]);
disable_audroot_clk:
- clk_disable_unprepare(dev->clks[CLK_ADC_AUDROOT]);
-disable_APB_clk:
clk_disable_unprepare(dev->clks[CLK_ADC_APB]);
-disable_APB0_clk:
- clk_disable_unprepare(dev->clks[CLK_ADC_APB0]);
+disable_APB_clk:
return ret;
}
if (IS_ERR(dev->clks_dac_lrck))
return PTR_ERR(dev->clks_dac_lrck);
- ret = clk_prepare_enable(dev->clks_audroot);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_audroot\n", __func__);
- goto disable_audioroot_clk;
- }
ret = clk_set_rate(dev->clks_audroot, 204800000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for clks_audroot ret=%d\n", ret);
goto disable_audioroot_clk;
}
- ret = clk_prepare_enable(dev->clks_inner);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_inner\n", __func__);
- goto disable_audinner_clk;
- }
-
ret = clk_set_rate(dev->clks_inner, 4096000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for clks_inner ret=%d\n", ret);
- goto disable_audinner_clk;
+ goto disable_audioroot_clk;
}
ret = clk_prepare_enable(dev->clks_bclk_mst);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable clks_bclk_mst\n", __func__);
- goto disable_mst_bclk;
+ goto disable_audioroot_clk;
}
ret = clk_set_rate(dev->clks_bclk_mst, 1024000);
goto disable_mst_bclk;
}
- ret = clk_prepare_enable(dev->clks_lrclk_mst);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_lrclk_mst\n", __func__);
- goto disable_mst_lrclk;
- }
-
- ret = clk_prepare_enable(dev->clks_mclk);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_mclk\n", __func__);
- goto disable_mclk;
- }
-
- ret = clk_prepare_enable(dev->clks_bclk);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_bclk\n", __func__);
- goto disable_bclk;
- }
-
- ret = clk_prepare_enable(dev->clks_lrclk);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_lrclk\n", __func__);
- goto disable_lrclk;
- }
-
ret = clk_prepare_enable(dev->clks_mclk_out);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable clks_mclk_out\n", __func__);
- goto disable_mclk_out;
- }
-
- ret = clk_prepare_enable(dev->clks_apb0);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to enable clks_apb0\n", __func__);
- goto disable_apb0;
+ goto disable_mst_bclk;
}
ret = clk_prepare_enable(dev->clks_4ch_apb);
goto disable_4ch_apb;
}
-
dev_dbg(&pdev->dev, "dev->clks_inner = %lu \n", clk_get_rate(dev->clks_inner));
dev_dbg(&pdev->dev, "dev->clks_bclk_mst = %lu \n", clk_get_rate(dev->clks_bclk_mst));
dev_dbg(&pdev->dev, "dev->clks_lrclk_mst = %lu \n", clk_get_rate(dev->clks_lrclk_mst));
dev->rstc_ch1 = devm_reset_control_array_get_exclusive(&pdev->dev);
if (IS_ERR(dev->rstc_ch1)) {
dev_err(&pdev->dev, "%s: failed to get rstc_ch1 reset control\n", __func__);
- goto disable_4ch_apb;
+ goto disable_rst;
}
ret = reset_control_assert(dev->rstc_ch1);
if (ret) {
dev_err(&pdev->dev, "%s: failed to reset control assert rstc_ch1\n", __func__);
- goto disable_4ch_apb;
+ goto disable_rst;
}
ret = reset_control_deassert(dev->rstc_ch1);
if (ret) {
dev_err(&pdev->dev, "%s: failed to reset control deassert rstc_ch1\n", __func__);
- goto disable_4ch_apb;
+ goto disable_rst;
}
return 0;
-disable_4ch_apb:
+disable_rst:
clk_disable_unprepare(dev->clks_4ch_apb);
-disable_apb0:
- clk_disable_unprepare(dev->clks_apb0);
-disable_mclk_out:
+disable_4ch_apb:
clk_disable_unprepare(dev->clks_mclk_out);
-disable_lrclk:
- clk_disable_unprepare(dev->clks_lrclk);
-disable_bclk:
- clk_disable_unprepare(dev->clks_bclk);
-disable_mclk:
- clk_disable_unprepare(dev->clks_mclk);
-disable_mst_lrclk:
- clk_disable_unprepare(dev->clks_lrclk_mst);
disable_mst_bclk:
clk_disable_unprepare(dev->clks_bclk_mst);
-disable_audinner_clk:
- clk_disable_unprepare(dev->clks_inner);
disable_audioroot_clk:
- clk_disable_unprepare(dev->clks_audroot);
return ret;
}
}
pm_runtime_enable(&pdev->dev);
- clk_disable_unprepare(dev->clks_mclk_out);
- clk_disable_unprepare(dev->clks_bclk_mst);
- clk_disable_unprepare(dev->clks_lrclk_mst);
+
+#ifdef CONFIG_PM
+ if (dev->capability & DWC_I2S_PLAY) {
+ clk_disable_unprepare(dev->clks_mclk_out);
+ clk_disable_unprepare(dev->clks_bclk_mst);
+ clk_disable_unprepare(dev->clks_4ch_apb);
+ } else {
+ clk_disable_unprepare(dev->clks[CLK_ADC_BCLK]);
+ clk_disable_unprepare(dev->clks[CLK_ADC_APB]);
+ }
+#endif
+
return 0;
err_clk_disable:
if (dev->capability & DW_I2S_MASTER)
clk_disable_unprepare(dev->clk);
+ else{
+ if (dev->capability & DWC_I2S_PLAY) {
+ clk_disable_unprepare(dev->clks_mclk_out);
+ clk_disable_unprepare(dev->clks_bclk_mst);
+ clk_disable_unprepare(dev->clks_4ch_apb);
+ } else {
+ clk_disable_unprepare(dev->clks[CLK_ADC_BCLK]);
+ clk_disable_unprepare(dev->clks[CLK_ADC_APB]);
+ }
+ }
+
return ret;
}
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
if (dw_dev->capability & DW_I2S_MASTER) {
- clk_disable_unprepare(dw_dev->clk_i2s_lrck);
- clk_disable_unprepare(dw_dev->clk_i2s_bclk);
+ clk_disable_unprepare(dw_dev->clk_i2s_bclk_mst);
+ clk_disable_unprepare(dw_dev->clk_i2s_apb);
}
return 0;
int ret;
if (dw_dev->capability & DW_I2S_MASTER) {
- ret = clk_prepare_enable(dw_dev->clk_i2s_bclk);
+ ret = clk_prepare_enable(dw_dev->clk_i2s_apb);
if (ret) {
- dev_err(dw_dev->dev, "Failed to enable clk_i2s_bclk\n");
+ dev_err(dw_dev->dev, "Failed to enable clk_i2s_apb\n");
return ret;
}
- ret = clk_prepare_enable(dw_dev->clk_i2s_lrck);
+ ret = clk_prepare_enable(dw_dev->clk_i2s_bclk_mst);
if (ret) {
dev_err(dw_dev->dev, "Failed to enable clk_i2s_lrck\n");
return ret;
static int dw_i2s_suspend(struct snd_soc_component *component)
{
- struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
-
- if (dev->capability & DW_I2S_MASTER) {
- clk_disable_unprepare(dev->clk_i2s_lrck);
- clk_disable_unprepare(dev->clk_i2s_bclk);
- }
-
- return 0;
+ return pm_runtime_force_suspend(component->dev);
}
static int dw_i2s_resume(struct snd_soc_component *component)
int stream;
int ret;
- if (dev->capability & DW_I2S_MASTER) {
- ret = clk_prepare_enable(dev->clk_i2s_bclk);
- if (ret) {
- dev_err(dev->dev, "Failed to enable clk_i2s_bclk\n");
- return ret;
- }
-
- ret = clk_prepare_enable(dev->clk_i2s_lrck);
- if (ret) {
- dev_err(dev->dev, "Failed to enable clk_i2s_lrck\n");
- return ret;
- }
- }
+ ret = pm_runtime_force_resume(component->dev);
+ if (ret)
+ return ret;
for_each_component_dais(component, dai) {
for_each_pcm_streams(stream)
int ret = 0;
static struct clk_bulk_data clks[] = {
- { .id = "apb0" },
{ .id = "i2srx_apb" },
{ .id = "i2srx_bclk_mst" },
{ .id = "i2srx_lrck_mst" },
goto exit;
}
- dev->clk_apb0 = clks[0].clk;
- dev->clk_i2s_apb = clks[1].clk;
- dev->clk_i2s_bclk_mst = clks[2].clk;
- dev->clk_i2s_lrck_mst = clks[3].clk;
- dev->clk_i2s_bclk = clks[4].clk;
- dev->clk_i2s_lrck = clks[5].clk;
+ dev->clk_i2s_apb = clks[0].clk;
+ dev->clk_i2s_bclk_mst = clks[1].clk;
+ dev->clk_i2s_lrck_mst = clks[2].clk;
+ dev->clk_i2s_bclk = clks[3].clk;
+ dev->clk_i2s_lrck = clks[4].clk;
dev->rst_i2s_apb = devm_reset_control_get_exclusive(&pdev->dev, "rst_apb_rx");
if (IS_ERR(dev->rst_i2s_apb)) {
reset_control_assert(dev->rst_i2s_apb);
reset_control_assert(dev->rst_i2s_bclk);
- ret = clk_prepare_enable(dev->clk_apb0);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_apb0\n");
- goto exit;
- }
-
ret = clk_prepare_enable(dev->clk_i2s_apb);
if (ret) {
dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_apb\n");
- goto err_dis_i2srx_apb;
+ goto exit;
}
ret = clk_prepare_enable(dev->clk_i2s_bclk_mst);
goto err_dis_bclk_mst;
}
- ret = clk_prepare_enable(dev->clk_i2s_lrck_mst);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_lrck_mst\n");
- goto err_dis_lrck_mst;
- }
-
- ret = clk_prepare_enable(dev->clk_i2s_bclk);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_bclk\n");
- goto err_dis_bclk;
- }
-
- ret = clk_prepare_enable(dev->clk_i2s_lrck);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_lrck\n");
- goto err_dis_lrck;
- }
-
reset_control_deassert(dev->rst_i2s_apb);
reset_control_deassert(dev->rst_i2s_bclk);
I2SRX_3CH_ADC_MASK, I2SRX_3CH_ADC_EN);
return 0;
-err_dis_i2srx_apb:
- clk_disable_unprepare(dev->clk_apb0);
err_dis_bclk_mst:
clk_disable_unprepare(dev->clk_i2s_apb);
-err_dis_lrck_mst:
- clk_disable_unprepare(dev->clk_i2s_bclk_mst);
-err_dis_bclk:
- clk_disable_unprepare(dev->clk_i2s_lrck_mst);
-err_dis_lrck:
- clk_disable_unprepare(dev->clk_i2s_bclk);
exit:
return ret;
}
}
pm_runtime_enable(&pdev->dev);
+
+#ifdef CONFIG_PM
+ if (dev->capability & DW_I2S_MASTER) {
+ clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+ clk_disable_unprepare(dev->clk_i2s_apb);
+ }
+#endif
+
return 0;
err_clk_disable:
- if (dev->capability & DW_I2S_MASTER)
+ if (dev->capability & DW_I2S_MASTER) {
clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+ clk_disable_unprepare(dev->clk_i2s_apb);
+ }
return ret;
}
{
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- if (dev->capability & DW_I2S_MASTER)
+ if (dev->capability & DW_I2S_MASTER) {
clk_disable_unprepare(dev->clk_i2s_bclk_mst);
+ clk_disable_unprepare(dev->clk_i2s_apb);
+ }
pm_runtime_disable(&pdev->dev);
return 0;
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/pcm_params.h>
return 0;
}
+#ifdef CONFIG_PM
+static int sf_pdm_runtime_suspend(struct device *dev)
+{
+ struct sf_pdm *priv = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(priv->clk_pdm_apb);
+ clk_disable_unprepare(priv->clk_pdm_mclk);
+ clk_disable_unprepare(priv->clk_mclk);
+
+ return 0;
+}
+
+static int sf_pdm_runtime_resume(struct device *dev)
+{
+ struct sf_pdm *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk_mclk);
+ if (ret) {
+ dev_err(dev, "failed to prepare enable clk_mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->clk_pdm_mclk);
+ if (ret) {
+ dev_err(dev, "failed to prepare enable clk_pdm_mclk\n");
+ goto disable_mclk;
+ }
+
+ ret = clk_prepare_enable(priv->clk_pdm_apb);
+ if (ret) {
+ dev_err(dev, "failed to prepare enable clk_pdm_apb\n");
+ goto disable_pdm_mclk;
+ }
+
+ return 0;
+
+disable_pdm_mclk:
+ clk_disable_unprepare(priv->clk_pdm_mclk);
+disable_mclk:
+ clk_disable_unprepare(priv->clk_mclk);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sf_pdm_suspend(struct snd_soc_component *component)
+{
+ return pm_runtime_force_suspend(component->dev);
+}
+
+static int sf_pdm_resume(struct snd_soc_component *component)
+{
+ return pm_runtime_force_resume(component->dev);
+}
+
+#else
+#define sf_pdm_suspend NULL
+#define sf_pdm_resume NULL
+#endif
+
static const struct snd_soc_component_driver sf_pdm_component_drv = {
.name = "jh7110-pdm",
.probe = pdm_probe,
+ .suspend = sf_pdm_suspend,
+ .resume = sf_pdm_resume,
};
static const struct regmap_config sf_pdm_regmap_cfg = {
return ret;
}
- return devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
+ dev_set_drvdata(&pdev->dev, priv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
&sf_pdm_dai_drv, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register pdm dai\n");
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+#ifdef CONFIG_PM
+ clk_disable_unprepare(priv->clk_pdm_apb);
+ clk_disable_unprepare(priv->clk_pdm_mclk);
+ clk_disable_unprepare(priv->clk_mclk);
+#endif
+
+ return 0;
}
static int sf_pdm_dev_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
return 0;
}
};
MODULE_DEVICE_TABLE(of, sf_pdm_of_match);
+static const struct dev_pm_ops sf_pdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend,
+ sf_pdm_runtime_resume, NULL)
+};
+
static struct platform_driver sf_pdm_driver = {
.driver = {
.name = "jh7110-pdm",
.of_match_table = sf_pdm_of_match,
+ .pm = &sf_pdm_pm_ops,
},
.probe = sf_pdm_probe,
.remove = sf_pdm_dev_remove,
+// SPDX-License-Identifier: GPL-2.0
/*
* PWMDAC driver for the StarFive JH7110 SoC
*
mclk_dac_value = mclk_dac_value + 64;
pwmdac_set(dev);
- ret = clk_prepare_enable(dev->clk_pwmdac_core);
- if (ret) {
- dev_err(dai->dev, "failed to prepare enable clk_pwmdac_core\n");
- goto err_clk_pwmdac;
- }
-
ret = clk_set_rate(dev->clk_pwmdac_core, mclk_dac_value);
if (ret) {
dev_err(dai->dev, "failed to set rate for clk_pwmdac_core %lu\n", mclk_dac_value);
return 0;
}
-static int sf_pwmdac_clk_init(struct platform_device *pdev,
- struct sf_pwmdac_dev *dev)
+static int starfive_pwmdac_crg_enable(struct sf_pwmdac_dev *dev, bool enable)
{
int ret = 0;
- ret = clk_prepare_enable(dev->clk_apb0);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_apb0\n");
- goto err_clk_pwmdac;
- }
+ dev_dbg(dev->dev, "starfive_pwmdac clk&rst %sable.\n", enable ? "en":"dis");
+ if (enable) {
+ ret = clk_prepare_enable(dev->clk_apb0);
+ if (ret) {
+ dev_err(dev->dev, "failed to prepare enable clk_apb0\n");
+ goto err_clk_apb0;
+ }
- ret = clk_prepare_enable(dev->clk_pwmdac_apb);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_pwmdac_apb\n");
- goto err_clk_pwmdac;
+ ret = clk_prepare_enable(dev->clk_pwmdac_apb);
+ if (ret) {
+ dev_err(dev->dev, "failed to prepare enable clk_pwmdac_apb\n");
+ goto err_clk_apb;
+ }
+
+ ret = clk_prepare_enable(dev->clk_pwmdac_core);
+ if (ret) {
+ dev_err(dev->dev, "failed to prepare enable clk_pwmdac_core\n");
+ goto err_clk_core;
+ }
+
+ ret = reset_control_deassert(dev->rst_apb);
+ if (ret) {
+ dev_err(dev->dev, "failed to deassert apb\n");
+ goto err_rst_apb;
+ }
+ } else {
+ clk_disable_unprepare(dev->clk_pwmdac_core);
+ clk_disable_unprepare(dev->clk_pwmdac_apb);
+ clk_disable_unprepare(dev->clk_apb0);
}
- ret = clk_prepare_enable(dev->clk_pwmdac_core);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable clk_pwmdac_core\n");
+ return 0;
+
+err_rst_apb:
+ clk_disable_unprepare(dev->clk_pwmdac_core);
+err_clk_core:
+ clk_disable_unprepare(dev->clk_pwmdac_apb);
+err_clk_apb:
+ clk_disable_unprepare(dev->clk_apb0);
+err_clk_apb0:
+ return ret;
+}
+
+static int sf_pwmdac_clk_init(struct platform_device *pdev,
+ struct sf_pwmdac_dev *dev)
+{
+ int ret = 0;
+
+ ret = starfive_pwmdac_crg_enable(dev, true);
+ if (ret)
goto err_clk_pwmdac;
- }
ret = clk_set_rate(dev->clk_pwmdac_core, 4096000);
if (ret) {
clk_get_rate(dev->clk_apb0), clk_get_rate(dev->clk_pwmdac_apb),
clk_get_rate(dev->clk_pwmdac_core));
- ret = reset_control_deassert(dev->rst_apb);
- if (ret) {
- dev_err(&pdev->dev, "failed to deassert apb\n");
- goto err_clk_pwmdac;
- }
-
err_clk_pwmdac:
return ret;
}
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int starfive_pwmdac_system_suspend(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int starfive_pwmdac_system_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int starfive_pwmdac_runtime_suspend(struct device *dev)
+{
+ struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+
+ return starfive_pwmdac_crg_enable(pwmdac, false);
+}
+
+static int starfive_pwmdac_runtime_resume(struct device *dev)
+{
+ struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
+
+ return starfive_pwmdac_crg_enable(pwmdac, true);
+}
+#endif
+
+static const struct dev_pm_ops starfive_pwmdac_pm_ops = {
+ SET_RUNTIME_PM_OPS(starfive_pwmdac_runtime_suspend, starfive_pwmdac_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(starfive_pwmdac_system_suspend, starfive_pwmdac_system_resume)
+};
+
#define SOC_PWMDAC_ENUM_DECL(xname, xinfo, xget, xput) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = xinfo, .get = xget, .put = xput,}
return PTR_ERR(dev->pwmdac_base);
dev->mapbase = res->start;
+ dev->dev = &pdev->dev;
ret = sf_pwmdac_clks_get(pdev, dev);
if (ret) {
return ret;
}
- dev->dev = &pdev->dev;
dev->mode = shift_8Bit_inverter;
dev->fifo_th = 1;//8byte
pwmdac_config(dev);
0);
}
+#ifdef CONFIG_PM
+ starfive_pwmdac_crg_enable(dev, false);
+#endif
+
+ pm_runtime_enable(dev->dev);
+
return 0;
}
.driver = {
.name = "starfive-pwmdac",
.of_match_table = of_match_ptr(sf_pwmdac_of_match),
+ .pm = &starfive_pwmdac_pm_ops,
},
};
module_exit(pwmdac_driver_exit);
MODULE_AUTHOR("curry.zhang <curry.zhang@starfivetech.com>");
+MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("starfive pwmdac SoC Interface");
MODULE_ALIAS("platform:starfive-pwmdac");
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/dmaengine_pcm.h>
+#include <linux/pm_runtime.h>
#include "starfive_spdif.h"
static irqreturn_t spdif_irq_handler(int irq, void *dev_id)
return 0;
}
+static int starfive_spdif_crg_enable(struct sf_spdif_dev *spdif, bool enable)
+{
+ int ret;
+
+ dev_dbg(spdif->dev, "starfive_spdif clk&rst %sable.\n", enable ? "en":"dis");
+ if (enable) {
+ ret = clk_prepare_enable(spdif->spdif_apb);
+ if (ret) {
+ dev_err(spdif->dev, "failed to prepare enable spdif_apb\n");
+ goto failed_apb_clk;
+ }
+
+ ret = clk_prepare_enable(spdif->spdif_core);
+ if (ret) {
+ dev_err(spdif->dev, "failed to prepare enable spdif_core\n");
+ goto failed_core_clk;
+ }
+
+ ret = reset_control_deassert(spdif->rst_apb);
+ if (ret) {
+ dev_err(spdif->dev, "failed to deassert apb\n");
+ goto failed_rst;
+ }
+ } else {
+ clk_disable_unprepare(spdif->spdif_core);
+ clk_disable_unprepare(spdif->spdif_apb);
+ }
+
+ return 0;
+
+failed_rst:
+ clk_disable_unprepare(spdif->spdif_core);
+failed_core_clk:
+ clk_disable_unprepare(spdif->spdif_apb);
+failed_apb_clk:
+ return ret;
+}
+
static int sf_spdif_clk_init(struct platform_device *pdev,
struct sf_spdif_dev *spdif)
{
int ret = 0;
- ret = clk_prepare_enable(spdif->spdif_apb);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable spdif_apb\n");
- goto disable_apb_clk;
- }
-
- ret = clk_prepare_enable(spdif->spdif_core);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable spdif_core\n");
- goto disable_core_clk;
- }
+ ret = starfive_spdif_crg_enable(spdif, true);
+ if (ret)
+ return ret;
ret = clk_set_rate(spdif->audio_root, 204800000);
if (ret) {
goto disable_core_clk;
}
- ret = reset_control_deassert(spdif->rst_apb);
- if (ret) {
- dev_err(&pdev->dev, "failed to deassert apb\n");
- goto disable_core_clk;
- }
-
return 0;
disable_core_clk:
clk_disable_unprepare(spdif->spdif_core);
-disable_apb_clk:
clk_disable_unprepare(spdif->spdif_apb);
return ret;
{
struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ pm_runtime_get_sync(spdif->dev);
+
/* reset */
regmap_update_bits(spdif->regmap, SPDIF_CTRL,
SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0);
regmap_update_bits(spdif->regmap, SPDIF_CTRL,
SPDIF_CHANNEL_MODE, 0);
+ pm_runtime_put_sync(spdif->dev);
return 0;
}
.hw_params = sf_spdif_hw_params,
};
+#ifdef CONFIG_PM_SLEEP
+static int spdif_system_suspend(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int spdif_system_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int spdif_runtime_suspend(struct device *dev)
+{
+ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
+
+ return starfive_spdif_crg_enable(spdif, false);
+}
+
+static int spdif_runtime_resume(struct device *dev)
+{
+ struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
+
+ return starfive_spdif_crg_enable(spdif, true);
+}
+#endif
+
+static const struct dev_pm_ops spdif_pm_ops = {
+ SET_RUNTIME_PM_OPS(spdif_runtime_suspend, spdif_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(spdif_system_suspend, spdif_system_resume)
+};
+
+
#define SF_PCM_RATE_44100_192000 (SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000 | \
if (IS_ERR(spdif->regmap))
return PTR_ERR(spdif->regmap);
+ spdif->dev = &pdev->dev;
+
ret = sf_spdif_clks_get(pdev, spdif);
if (ret) {
dev_err(&pdev->dev, "failed to get audio clock\n");
return ret;
}
- spdif->dev = &pdev->dev;
spdif->fifo_th = 16;
irq = platform_get_irq(pdev, 0);
if (ret)
goto err_clk_disable;
+ starfive_spdif_crg_enable(spdif, false);
+ pm_runtime_enable(&pdev->dev);
+ dev_info(&pdev->dev, "spdif register done.\n");
+
return 0;
err_clk_disable:
.driver = {
.name = "starfive-spdif",
.of_match_table = sf_spdif_of_match,
+ .pm = &spdif_pm_ops,
},
.probe = sf_spdif_probe,
};
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
#include <linux/dma/starfive-dma.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
{
u32 data;
- data = (dev->clkpolity << 5) | (dev->elm << 3) | (dev->syncm << 2) | (dev->ms_mode << 1);
+ data = (dev->clkpolity << CLKPOL_BIT) |
+ (dev->elm << ELM_BIT) |
+ (dev->syncm << SYNCM_BIT) |
+ (dev->ms_mode << MS_BIT);
sf_tdm_writel(dev, TDM_PCMGBCR, data);
}
sf_tdm_contrl(dev);
sf_tdm_syncdiv(dev);
- datarx = (dev->rx.ifl << 11) | (dev->rx.wl << 8) | (dev->rx.sscale << 4) |
- (dev->rx.sl << 2) | (dev->rx.lrj << 1);
+ datarx = (dev->rx.ifl << IFL_BIT) |
+ (dev->rx.wl << WL_BIT) |
+ (dev->rx.sscale << SSCALE_BIT) |
+ (dev->rx.sl << SL_BIT) |
+ (dev->rx.lrj << LRJ_BIT);
- datatx = (dev->tx.ifl << 11) | (dev->tx.wl << 8) | (dev->tx.sscale << 4) |
- (dev->tx.sl << 2) | (dev->tx.lrj << 1);
+ datatx = (dev->tx.ifl << IFL_BIT) |
+ (dev->tx.wl << WL_BIT) |
+ (dev->tx.sscale << SSCALE_BIT) |
+ (dev->tx.sl << SL_BIT) |
+ (dev->tx.lrj << LRJ_BIT);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sf_tdm_writel(dev, TDM_PCMTXCR, datatx);
sf_tdm_writel(dev, TDM_PCMRXCR, datarx);
}
+static void sf_tdm_clk_disable(struct sf_tdm_dev *priv)
+{
+ clk_disable_unprepare(priv->clk_tdm);
+ clk_disable_unprepare(priv->clk_tdm_ext);
+ clk_disable_unprepare(priv->clk_tdm_internal);
+ clk_disable_unprepare(priv->clk_tdm_apb);
+ clk_disable_unprepare(priv->clk_tdm_ahb);
+ clk_disable_unprepare(priv->clk_mclk_inner);
+}
+
+#ifdef CONFIG_PM
+static int sf_tdm_runtime_suspend(struct device *dev)
+{
+ struct sf_tdm_dev *priv = dev_get_drvdata(dev);
+
+ sf_tdm_clk_disable(priv);
+ return 0;
+}
+
+static int sf_tdm_runtime_resume(struct device *dev)
+{
+ struct sf_tdm_dev *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk_mclk_inner);
+ if (ret) {
+ dev_err(dev, "failed to prepare enable clk_mclk_inner\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->clk_tdm_ahb);
+ if (ret) {
+ dev_err(dev, "Failed to prepare enable clk_tdm_ahb\n");
+ goto dis_mclk_inner;
+ }
+
+ ret = clk_prepare_enable(priv->clk_tdm_apb);
+ if (ret) {
+ dev_err(dev, "Failed to prepare enable clk_tdm_apb\n");
+ goto dis_tdm_ahb;
+ }
+
+ ret = clk_prepare_enable(priv->clk_tdm_internal);
+ if (ret) {
+ dev_err(dev, "Failed to prepare enable clk_tdm_intl\n");
+ goto dis_tdm_apb;
+ }
+
+ ret = clk_prepare_enable(priv->clk_tdm_ext);
+ if (ret) {
+ dev_err(dev, "Failed to prepare enable clk_tdm_ext\n");
+ goto dis_tdm_internal;
+ }
+ ret = clk_prepare_enable(priv->clk_tdm);
+ if (ret) {
+ dev_err(dev, "Failed to prepare enable clk_tdm\n");
+ goto dis_tdm_ext;
+ }
+
+ return 0;
+
+dis_tdm_ext:
+ clk_disable_unprepare(priv->clk_tdm_ext);
+dis_tdm_internal:
+ clk_disable_unprepare(priv->clk_tdm_internal);
+dis_tdm_apb:
+ clk_disable_unprepare(priv->clk_tdm_apb);
+dis_tdm_ahb:
+ clk_disable_unprepare(priv->clk_tdm_ahb);
+dis_mclk_inner:
+ clk_disable_unprepare(priv->clk_mclk_inner);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sf_tdm_suspend(struct snd_soc_component *component)
+{
+ return pm_runtime_force_suspend(component->dev);
+}
+
+static int sf_tdm_resume(struct snd_soc_component *component)
+{
+ return pm_runtime_force_resume(component->dev);
+}
+
+#else
#define sf_tdm_suspend NULL
#define sf_tdm_resume NULL
+#endif
-/*
+/*
* To stop dma first, we must implement this function, because it is
- * called before stopping the stream.
+ * called before stopping the stream.
*/
static int sf_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
/* There are some limitation when using 8k sample rate */
case 8000:
mclk_rate = 12288000;
- dev->pcmclk = 512000;
if ((data_width == 16) || (channels == 1)) {
pr_err("TDM: not support 16bit or 1-channel when using 8k sample rate\n");
return -EINVAL;
}
break;
case 11025:
- mclk_rate = 11289600; //sysclk
- dev->pcmclk = 352800; //bit clock, for 16-bit
+ /* sysclk */
+ mclk_rate = 11289600;
break;
case 16000:
- mclk_rate = 12288000; //sysclk
- dev->pcmclk = 512000; //bit clock
+ mclk_rate = 12288000;
break;
case 22050:
mclk_rate = 11289600;
- dev->pcmclk = 705600;
break;
case 32000:
mclk_rate = 12288000;
- dev->pcmclk = 1024000;
break;
case 44100:
mclk_rate = 11289600;
- dev->pcmclk = 1411200;
break;
case 48000:
mclk_rate = 12288000;
- dev->pcmclk = 1536000;
break;
default:
pr_err("TDM: not support sample rate:%d\n", dev->samplerate);
ret = clk_prepare_enable(dev->clk_ahb0);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_ahb0\n");
- goto err_dis_ahb0;
+ goto dis_mclk_inner;
}
ret = clk_prepare_enable(dev->clk_tdm_ahb);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_ahb\n");
- goto err_dis_tdm_ahb;
+ goto dis_ahb0;
}
ret = clk_prepare_enable(dev->clk_apb0);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_apb0\n");
- goto err_dis_apb0;
+ goto dis_tdm_ahb;
}
ret = clk_prepare_enable(dev->clk_tdm_apb);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_apb\n");
- goto err_dis_tdm_apb;
+ goto dis_apb0;
}
ret = clk_prepare_enable(dev->clk_tdm_internal);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_intl\n");
- goto err_dis_tdm_internal;
+ goto dis_tdm_apb;
}
ret = clk_prepare_enable(dev->clk_tdm_ext);
if (ret) {
dev_err(&pdev->dev, "failed to prepare enable clk_tdm_ext\n");
- goto err_dis_tdm_ext;
+ goto dis_tdm_internal;
+ }
+
+ ret = clk_prepare_enable(dev->clk_tdm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_tdm\n");
+ goto dis_tdm_ext;
}
ret = reset_control_deassert(dev->resets);
if (ret) {
dev_err(&pdev->dev, "Failed to deassert tdm resets\n");
- goto err_clk_disable;
+ goto dis_tdm_clk;
}
return 0;
-err_clk_disable:
+dis_tdm_clk:
+ clk_disable_unprepare(dev->clk_tdm);
+dis_tdm_ext:
clk_disable_unprepare(dev->clk_tdm_ext);
-err_dis_tdm_ext:
+dis_tdm_internal:
clk_disable_unprepare(dev->clk_tdm_internal);
-err_dis_tdm_internal:
+dis_tdm_apb:
clk_disable_unprepare(dev->clk_tdm_apb);
-err_dis_tdm_apb:
+dis_apb0:
clk_disable_unprepare(dev->clk_apb0);
-err_dis_apb0:
+dis_tdm_ahb:
clk_disable_unprepare(dev->clk_tdm_ahb);
-err_dis_tdm_ahb:
+dis_ahb0:
clk_disable_unprepare(dev->clk_ahb0);
-err_dis_ahb0:
+dis_mclk_inner:
clk_disable_unprepare(dev->clk_mclk_inner);
exit:
return ret;
dev->frame_mode = SHORT_LATER;
tdm_init_params(dev);
-
dev_set_drvdata(&pdev->dev, dev);
+
ret = devm_snd_soc_register_component(&pdev->dev, &sf_tdm_component,
&sf_tdm_dai, 1);
if (ret != 0) {
return ret;
}
+ pm_runtime_enable(&pdev->dev);
+#ifdef CONFIG_PM
+ sf_tdm_clk_disable(dev);
+#endif
+
return 0;
}
static int sf_tdm_dev_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id sf_tdm_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sf_tdm_of_match);
+static const struct dev_pm_ops sf_tdm_pm_ops = {
+ SET_RUNTIME_PM_OPS(sf_tdm_runtime_suspend,
+ sf_tdm_runtime_resume, NULL)
+};
+
static struct platform_driver sf_tdm_driver = {
.driver = {
.name = "jh7110-tdm",
.of_match_table = sf_tdm_of_match,
+ .pm = &sf_tdm_pm_ops,
},
.probe = sf_tdm_probe,
.remove = sf_tdm_dev_remove,
#define MS_BIT 1
#define TDM_PCMTXCR 0x04
#define PCMTXCR_TXEN BIT(0)
+ #define IFL_BIT 11
+ #define WL_BIT 8
+ #define SSCALE_BIT 4
+ #define SL_BIT 2
+ #define LRJ_BIT 1
#define TDM_PCMRXCR 0x08
#define PCMRXCR_RXEN BIT(0)
#define PCMRXCR_RXSL_MASK 0xc
#define TDM_PCMDIV 0x0c
/* DMA registers */
-#define TDM_FIFO 0x170c0000
-//#define TDM_FIFO_DEPTH 16
+#define TDM_FIFO 0x170c0000
#define TDM_FIFO_DEPTH 32
#define ONE_CHANNEL_SUPPORT 1