mirror of
https://github.com/revyos/thead-kernel.git
synced 2026-06-21 09:12:26 +02:00
Linux_SDK_V1.3.3
Signed-off-by: thead_admin <occ_thead@service.alibaba.com>
This commit is contained in:
committed by
Han Gao/Revy/Rabenda
parent
2bd2eac4a7
commit
e17ac7bab2
@@ -1385,14 +1385,13 @@ compress_again:
|
||||
__GFP_KSWAPD_RECLAIM |
|
||||
__GFP_NOWARN |
|
||||
__GFP_HIGHMEM |
|
||||
__GFP_MOVABLE |
|
||||
__GFP_CMA);
|
||||
__GFP_MOVABLE);
|
||||
if (!handle) {
|
||||
zcomp_stream_put(zram->comp);
|
||||
atomic64_inc(&zram->stats.writestall);
|
||||
handle = zs_malloc(zram->mem_pool, comp_len,
|
||||
GFP_NOIO | __GFP_HIGHMEM |
|
||||
__GFP_MOVABLE | __GFP_CMA);
|
||||
__GFP_MOVABLE);
|
||||
if (handle)
|
||||
goto compress_again;
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -50,6 +50,7 @@ static u32 share_cnt_spi_clk_en;
|
||||
static u32 share_cnt_uart0_clk_en;
|
||||
static u32 share_cnt_uart2_clk_en;
|
||||
static u32 share_cnt_i2c2_clk_en;
|
||||
static u32 share_cnt_i2c3_clk_en;
|
||||
static u32 share_cnt_peri_i2s_clk_en;
|
||||
static u32 share_cnt_qspi1_clk_en;
|
||||
static u32 share_cnt_uart1_clk_en;
|
||||
@@ -378,31 +379,31 @@ static int light_clocks_probe(struct platform_device *pdev)
|
||||
clks[AONSYS_BUS_CLK] = thead_clk_fixed("aonsys_hclk", 101606400); //from sys_pll, maybe change ?
|
||||
|
||||
/* Light Fullmask AP MUX */
|
||||
clks[CPU_PLL0_BYPASS] = thead_light_clk_mux_flags("cpu_pll0_bypass", ap_base + 0x4, 30, 1, cpu_pll0_bypass_sels, ARRAY_SIZE(cpu_pll0_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CPU_PLL1_BYPASS] = thead_light_clk_mux_flags("cpu_pll1_bypass", ap_base + 0x14, 30, 1, cpu_pll1_bypass_sels, ARRAY_SIZE(cpu_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[GMAC_PLL_BYPASS] = thead_light_clk_mux_flags("gmac_pll_bypass", ap_base + 0x24, 30, 1, gmac_pll_bypass_sels, ARRAY_SIZE(gmac_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[VIDEO_PLL_BYPASS] = thead_light_clk_mux_flags("video_pll_bypass", ap_base + 0x34, 30, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[TEE_PLL_BYPASS] = thead_light_clk_mux_flags("tee_pll_bypass", ap_base + 0x64, 30, 1, tee_pll_bypass_sels, ARRAY_SIZE(tee_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[DPU0_PLL_BYPASS] = thead_light_clk_mux_flags("dpu0_pll_bypass", ap_base + 0x44, 30, 1, dpu0_pll_bypass_sels, ARRAY_SIZE(dpu0_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[DPU1_PLL_BYPASS] = thead_light_clk_mux_flags("dpu1_pll_bypass", ap_base + 0x54, 30, 1, dpu1_pll_bypass_sels, ARRAY_SIZE(dpu1_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CPU_PLL0_BYPASS] = thead_light_clk_mux_flags("cpu_pll0_bypass", ap_base + 0x4, 30, 1, cpu_pll0_bypass_sels, ARRAY_SIZE(cpu_pll0_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CPU_PLL1_BYPASS] = thead_light_clk_mux_flags("cpu_pll1_bypass", ap_base + 0x14, 30, 1, cpu_pll1_bypass_sels, ARRAY_SIZE(cpu_pll1_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[GMAC_PLL_BYPASS] = thead_light_clk_mux_flags("gmac_pll_bypass", ap_base + 0x24, 30, 1, gmac_pll_bypass_sels, ARRAY_SIZE(gmac_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[VIDEO_PLL_BYPASS] = thead_light_clk_mux_flags("video_pll_bypass", ap_base + 0x34, 30, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[TEE_PLL_BYPASS] = thead_light_clk_mux_flags("tee_pll_bypass", ap_base + 0x64, 30, 1, tee_pll_bypass_sels, ARRAY_SIZE(tee_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[DPU0_PLL_BYPASS] = thead_light_clk_mux_flags("dpu0_pll_bypass", ap_base + 0x44, 30, 1, dpu0_pll_bypass_sels, ARRAY_SIZE(dpu0_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[DPU1_PLL_BYPASS] = thead_light_clk_mux_flags("dpu1_pll_bypass", ap_base + 0x54, 30, 1, dpu1_pll_bypass_sels, ARRAY_SIZE(dpu1_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
|
||||
clks[AHB2_CPUSYS_HCLK] = thead_light_clk_mux_flags("ahb2_cpusys_hclk", ap_base + 0x120, 5, 1, ahb2_cpusys_hclk_sels, ARRAY_SIZE(ahb2_cpusys_hclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[C910_CCLK_I0] = thead_light_clk_mux_flags("c910_cclk_i0", ap_base + 0x100, 1, 1, c910_cclk_i0_sels, ARRAY_SIZE(c910_cclk_i0_sels), CLK_SET_RATE_PARENT);
|
||||
clks[C910_CCLK] = thead_light_clk_mux_flags("c910_cclk", ap_base + 0x100, 0, 1, c910_cclk_sels, ARRAY_SIZE(c910_cclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CFG_AXI_ACLK] = thead_light_clk_mux_flags("cfg_axi_aclk", ap_base + 0x138, 5, 1, cfg_axi_aclk_sels, ARRAY_SIZE(cfg_axi_aclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[AHB2_CPUSYS_HCLK] = thead_light_clk_mux_flags("ahb2_cpusys_hclk", ap_base + 0x120, 5, 1, ahb2_cpusys_hclk_sels, ARRAY_SIZE(ahb2_cpusys_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[C910_CCLK_I0] = thead_light_clk_mux_flags("c910_cclk_i0", ap_base + 0x100, 1, 1, c910_cclk_i0_sels, ARRAY_SIZE(c910_cclk_i0_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[C910_CCLK] = thead_light_clk_mux_flags("c910_cclk", ap_base + 0x100, 0, 1, c910_cclk_sels, ARRAY_SIZE(c910_cclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CFG_AXI_ACLK] = thead_light_clk_mux_flags("cfg_axi_aclk", ap_base + 0x138, 5, 1, cfg_axi_aclk_sels, ARRAY_SIZE(cfg_axi_aclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
|
||||
if (teesys)
|
||||
clks[TEESYS_HCLK] = thead_light_clk_mux_flags("teesys_hclk", ap_base + 0x1cc, 13, 1, teesys_hclk_sels, ARRAY_SIZE(teesys_hclk_sels), CLK_SET_RATE_PARENT); //just for teesys!!!
|
||||
clks[TEESYS_HCLK] = thead_light_clk_mux_flags("teesys_hclk", ap_base + 0x1cc, 13, 1, teesys_hclk_sels, ARRAY_SIZE(teesys_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); //just for teesys!!!
|
||||
|
||||
clks[PERISYS_AHB_HCLK] = thead_light_clk_mux_flags("perisys_ahb_hclk", ap_base + 0x140, 5, 1, perisys_ahb_hclk_sels, ARRAY_SIZE(perisys_ahb_hclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CLK_OUT_1] = thead_light_clk_mux_flags("clk_out_1", ap_base + 0x1b4, 4, 1, clk_out_1_sels, ARRAY_SIZE(clk_out_1_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CLK_OUT_2] = thead_light_clk_mux_flags("clk_out_2", ap_base + 0x1b8, 4, 1, clk_out_2_sels, ARRAY_SIZE(clk_out_2_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CLK_OUT_3] = thead_light_clk_mux_flags("clk_out_3", ap_base + 0x1bc, 4, 1, clk_out_3_sels, ARRAY_SIZE(clk_out_3_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CLK_OUT_4] = thead_light_clk_mux_flags("clk_out_4", ap_base + 0x1c0, 4, 1, clk_out_4_sels, ARRAY_SIZE(clk_out_4_sels), CLK_SET_RATE_PARENT);
|
||||
clks[PERI_I2S_SRC_CLK] = thead_light_clk_mux_flags("peri_i2s_src_clk", ap_base + 0x1f0, 0, 1, peri_i2s_src_clk_sels, ARRAY_SIZE(peri_i2s_src_clk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[PERISYS_AHB_HCLK] = thead_light_clk_mux_flags("perisys_ahb_hclk", ap_base + 0x140, 5, 1, perisys_ahb_hclk_sels, ARRAY_SIZE(perisys_ahb_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CLK_OUT_1] = thead_light_clk_mux_flags("clk_out_1", ap_base + 0x1b4, 4, 1, clk_out_1_sels, ARRAY_SIZE(clk_out_1_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CLK_OUT_2] = thead_light_clk_mux_flags("clk_out_2", ap_base + 0x1b8, 4, 1, clk_out_2_sels, ARRAY_SIZE(clk_out_2_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CLK_OUT_3] = thead_light_clk_mux_flags("clk_out_3", ap_base + 0x1bc, 4, 1, clk_out_3_sels, ARRAY_SIZE(clk_out_3_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[CLK_OUT_4] = thead_light_clk_mux_flags("clk_out_4", ap_base + 0x1c0, 4, 1, clk_out_4_sels, ARRAY_SIZE(clk_out_4_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[PERI_I2S_SRC_CLK] = thead_light_clk_mux_flags("peri_i2s_src_clk", ap_base + 0x1f0, 0, 1, peri_i2s_src_clk_sels, ARRAY_SIZE(peri_i2s_src_clk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[NPU_CCLK] = thead_light_clk_mux_flags("npu_cclk", ap_base + 0x1c8, 6, 1, npu_cclk_sels, ARRAY_SIZE(npu_cclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CFG_APB_PCLK] = thead_light_clk_mux_flags("cfg_apb_pclk", ap_base + 0x1c4, 7, 1, cfg_apb_pclk_sels, ARRAY_SIZE(cfg_apb_pclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[UART_SCLK] = thead_light_clk_mux_flags("uart_sclk", ap_base + 0x210, 0, 1, uart_sclk_sels, ARRAY_SIZE(uart_sclk_sels), CLK_SET_RATE_PARENT);
|
||||
clks[CFG_APB_PCLK] = thead_light_clk_mux_flags("cfg_apb_pclk", ap_base + 0x1c4, 7, 1, cfg_apb_pclk_sels, ARRAY_SIZE(cfg_apb_pclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
clks[UART_SCLK] = thead_light_clk_mux_flags("uart_sclk", ap_base + 0x210, 0, 1, uart_sclk_sels, ARRAY_SIZE(uart_sclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
|
||||
|
||||
/* Light Fullmask AP Divider */
|
||||
clks[AHB2_CPUSYS_HCLK_OUT_DIV] = thead_clk_light_divider("ahb2_cpusys_hclk_out_div", "gmac_pll_fout1ph0", ap_base + 0x120, 0, 3, 4, MUX_TYPE_DIV, 2, 7);
|
||||
@@ -436,7 +437,7 @@ static int light_clocks_probe(struct platform_device *pdev)
|
||||
/* Light Fullmask PLL FOUT */
|
||||
clks[GMAC_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("gmac_pll_fout1ph0", "gmac_pll_bypass", 1, 2);
|
||||
clks[GMAC_PLL_FOUT4] = thead_light_clk_fixed_factor("gmac_pll_fout4", "gmac_pll_bypass", 1, 8);
|
||||
clks[VIDEO_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("video_pll_fout1ph0", "video_pll_bybass", 1, 2);
|
||||
clks[VIDEO_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("video_pll_fout1ph0", "video_pll_bypass", 1, 2);
|
||||
clks[VIDEO_PLL_FOUT4] = thead_light_clk_fixed_factor("video_pll_fout4", "video_pll_bypass", 1, 8);
|
||||
clks[TEE_PLL_FOUT4] = thead_light_clk_fixed_factor("tee_pll_fout4", "tee_pll_bypass", 1, 8);
|
||||
clks[CPU_PLL0_FOUT4] = thead_light_clk_fixed_factor("cpu_pll0_fout4", "cpu_pll0_bypass", 1, 8);
|
||||
@@ -450,7 +451,7 @@ static int light_clocks_probe(struct platform_device *pdev)
|
||||
clks[QSPI0_SSI_CLK] = thead_light_clk_fixed_factor("qspi0_ssi_clk", "qspi_ssi_clk", 1, 1);
|
||||
clks[QSPI1_SSI_CLK] = thead_light_clk_fixed_factor("qspi1_ssi_clk", "video_pll_fout1ph0", 1, 1);
|
||||
clks[SPI_SSI_CLK] = thead_light_clk_fixed_factor("spi_ssi_clk", "video_pll_fout1ph0", 1, 1);
|
||||
clks[EMMC_SDIO_REF_CLK] = thead_light_clk_fixed_factor("emmc_sdio_ref_clk", "video_pll_foutpostdiv", 1, 1); /* Note: no mux to select, use default value */
|
||||
clks[EMMC_SDIO_REF_CLK] = thead_light_clk_fixed_factor("emmc_sdio_ref_clk", "video_pll_foutpostdiv", 1, 4); /* Note: base clk is div 4 to 198M*/
|
||||
clks[PWM_CCLK] = thead_light_clk_fixed_factor("pwm_cclk", "osc_24m", 1, 1);
|
||||
clks[CHIP_DBG_CCLK] = thead_light_clk_fixed_factor("chip_dbg_cclk", "osc_24m", 1, 1);
|
||||
clks[GMAC_CCLK] = thead_light_clk_fixed_factor("gmac_cclk", "gmac_pll_fout1ph0", 1, 1);
|
||||
@@ -568,8 +569,8 @@ static int light_clocks_probe(struct platform_device *pdev)
|
||||
clks[CLKGEN_UART2_SCLK] = thead_clk_light_gate_shared("clkgen_uart2_sclk", "uart_sclk", ap_base + 0x204, 12, &share_cnt_uart2_clk_en);
|
||||
clks[CLKGEN_I2C2_PCLK] = thead_clk_light_gate_shared("clkgen_i2c2_pclk", "perisys_apb_pclk", ap_base + 0x204, 3, &share_cnt_i2c2_clk_en);
|
||||
clks[CLKGEN_I2C2_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c2_ic_clk", "i2c_ic_clk", ap_base + 0x204, 3, &share_cnt_i2c2_clk_en);
|
||||
clks[CLKGEN_I2C3_PCLK] = thead_clk_light_gate_shared("clkgen_i2c3_pclk", "perisys_apb_pclk", ap_base + 0x204, 2, &share_cnt_i2c2_clk_en);
|
||||
clks[CLKGEN_I2C3_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c3_ic_clk", "i2c_ic_clk", ap_base + 0x204, 2, &share_cnt_i2c2_clk_en);
|
||||
clks[CLKGEN_I2C3_PCLK] = thead_clk_light_gate_shared("clkgen_i2c3_pclk", "perisys_apb_pclk", ap_base + 0x204, 2, &share_cnt_i2c3_clk_en);
|
||||
clks[CLKGEN_I2C3_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c3_ic_clk", "i2c_ic_clk", ap_base + 0x204, 2, &share_cnt_i2c3_clk_en);
|
||||
clks[CLKGEN_I2S_PCLK] = thead_clk_light_gate_shared("clkgen_i2s_pclk", "perisys_apb_pclk", ap_base + 0x1f0, 1, &share_cnt_peri_i2s_clk_en);
|
||||
clks[CLKGEN_I2S_SRC_CLK] = thead_clk_light_gate_shared("clkgen_i2s_src_clk", "peri_i2s_src_clk", ap_base + 0x1f0, 1, &share_cnt_peri_i2s_clk_en);
|
||||
clks[CLKGEN_QSPI1_PCLK] = thead_clk_light_gate_shared("clkgen_qspi1_pclk", "peri2sys_apb_pclk", ap_base + 0x204, 16, &share_cnt_qspi1_clk_en);
|
||||
|
||||
@@ -111,7 +111,7 @@ static inline struct clk *thead_light_clk_mux_flags(const char *name,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_register_mux(NULL, name, parents, num_parents,
|
||||
flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0,
|
||||
flags , reg, shift, width, 0,
|
||||
&thead_light_clk_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CLK_LIGHT_FM) += thead-gate.o visys-gate.o vpsys-gate.o vosys-gate.o dspsys-gate.o audiosys-gate.o
|
||||
obj-$(CONFIG_CLK_LIGHT_FM) += thead-gate.o visys-gate.o vpsys-gate.o vosys-gate.o dspsys-gate.o audiosys-gate.o miscsys-gate.o
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
static struct clk *gates[LIGHT_CLKGEN_DSPSYS_CLK_END];
|
||||
static struct clk_onecell_data clk_gate_data;
|
||||
static const char * const dsp0_cclk_sels[] = {"gmac_pll_foutpostdiv", "dspsys_dsp_clk"};
|
||||
static const char * const dsp1_cclk_sels[] = {"gmac_pll_foutpostdiv", "dspsys_dsp_clk"};
|
||||
|
||||
static int light_dspsys_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *dspsys_regmap, *tee_dspsys_regmap;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *np_reg = of_parse_phandle(np, "dspsys-regmap", 0);
|
||||
void __iomem *gate_base;
|
||||
int ret;
|
||||
|
||||
dspsys_regmap = syscon_regmap_lookup_by_phandle(np, "dspsys-regmap");
|
||||
@@ -39,14 +43,24 @@ static int light_dspsys_clk_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "cannot find regmap for tee dsp system register\n");
|
||||
tee_dspsys_regmap = NULL;
|
||||
}
|
||||
gate_base = of_iomap(np_reg,0);
|
||||
// MUX
|
||||
gates[DSPSYS_DSP0_CLK_SWITCH] = thead_light_clk_mux_flags("dspsys_dsp0_clk_switch", gate_base + 0x1c, 0, 1, dsp0_cclk_sels, ARRAY_SIZE(dsp0_cclk_sels), 0);
|
||||
gates[DSPSYS_DSP1_CLK_SWITCH] = thead_light_clk_mux_flags("dspsys_dsp1_clk_switch", gate_base + 0x20, 0, 1, dsp1_cclk_sels, ARRAY_SIZE(dsp1_cclk_sels), 0);
|
||||
|
||||
// DIV & CDE
|
||||
gates[DSPSYS_DSP_CLK] = thead_light_clk_fixed_factor("dspsys_dsp_clk", "video_pll_foutvco", 1, 3);
|
||||
gates[DSPSYS_DSP0_CLK_CDE] = thead_clk_light_divider("dspsys_dsp0_clk_cde", "dspsys_dsp0_clk_switch", gate_base + 0x0, 0, 3, 4, MUX_TYPE_CDE, 0, 7);
|
||||
gates[DSPSYS_DSP1_CLK_CDE] = thead_clk_light_divider("dspsys_dsp1_clk_cde", "dspsys_dsp1_clk_switch", gate_base + 0x4, 0, 3, 4, MUX_TYPE_CDE, 0, 7);
|
||||
|
||||
// gate
|
||||
gates[CLKGEN_DSP0_PCLK] = thead_gate_clk_register("clkgen_dsp0_pclk", NULL, dspsys_regmap,
|
||||
0x24, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_DSP1_PCLK] = thead_gate_clk_register("clkgen_dsp1_pclk", NULL, dspsys_regmap,
|
||||
0x24, 1, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_DSP1_CCLK] = thead_gate_clk_register("clkgen_dsp1_cclk", NULL, dspsys_regmap,
|
||||
gates[CLKGEN_DSP1_CCLK] = thead_gate_clk_register("clkgen_dsp1_cclk", "dspsys_dsp1_clk_cde", dspsys_regmap,
|
||||
0x24, 2, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_DSP0_CCLK] = thead_gate_clk_register("clkgen_dsp0_cclk", NULL, dspsys_regmap,
|
||||
gates[CLKGEN_DSP0_CCLK] = thead_gate_clk_register("clkgen_dsp0_cclk", "dspsys_dsp0_clk_cde", dspsys_regmap,
|
||||
0x24, 3, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_X2X_DSP2_ACLK_S] = thead_gate_clk_register("clkgen_x2x_dsp2_aclk_s", NULL, dspsys_regmap,
|
||||
0x24, 4, GATE_NOT_SHARED, NULL, dev);
|
||||
|
||||
108
drivers/clk/thead/gate/miscsys-gate.c
Normal file
108
drivers/clk/thead/gate/miscsys-gate.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2022 Alibaba Group Holding Limited.
|
||||
*/
|
||||
#include <dt-bindings/clock/light-miscsys.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include "clk-gate.h"
|
||||
#include "../clk.h"
|
||||
static struct clk *gates[CLKGEN_MISCSYS_CLK_END];
|
||||
static struct clk_onecell_data clk_gate_data;
|
||||
static int light_miscsys_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *miscsys_regmap, *tee_miscsys_regmap = NULL;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
miscsys_regmap = syscon_regmap_lookup_by_phandle(np, "miscsys-regmap");
|
||||
if (IS_ERR(miscsys_regmap)) {
|
||||
dev_err(&pdev->dev, "cannot find regmap for misc system register\n");
|
||||
return PTR_ERR(miscsys_regmap);
|
||||
}
|
||||
tee_miscsys_regmap = syscon_regmap_lookup_by_phandle(np, "tee-miscsys-regmap");
|
||||
if (IS_ERR(tee_miscsys_regmap)) {
|
||||
dev_err(&pdev->dev, "cannot find regmap for tee misc system register\n");
|
||||
return PTR_ERR(tee_miscsys_regmap);
|
||||
}
|
||||
/* we assume that the gate clock is a root clock */
|
||||
gates[CLKGEN_MISCSYS_MISCSYS_ACLK] = thead_gate_clk_register("clkgen_missys_aclk", NULL,
|
||||
miscsys_regmap, 0x100, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_USB3_DRD_CLK] = thead_gate_clk_register("clkgen_usb3_drd_clk", NULL,
|
||||
miscsys_regmap, 0x104, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_USB3_DRD_CTRL_REF_CLK] = thead_gate_clk_register("clkgen_usb3_drd_ctrl_ref_clk", "osc_24m",
|
||||
miscsys_regmap, 0x104, 1, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_USB3_DRD_PHY_REF_CLK] = thead_gate_clk_register("clkgen_usb3_drd_phy_ref_clk", "osc_24m",
|
||||
miscsys_regmap, 0x104, 2, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_USB3_DRD_SUSPEND_CLK] = thead_gate_clk_register("clkgen_usb3_drd_suspend_clk", NULL,
|
||||
miscsys_regmap, 0x104, 3, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EMMC_CLK] = thead_gate_clk_register("clkgen_emmc_clk", "osc_24m",
|
||||
miscsys_regmap, 0x108, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_SDIO0_CLK] = thead_gate_clk_register("clkgen_sdio0_clk", "osc_24m",
|
||||
miscsys_regmap, 0x10c, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_SDIO1_CLK] = thead_gate_clk_register("clkgen_sdio1_clk", "osc_24m",
|
||||
miscsys_regmap, 0x110, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
if (tee_miscsys_regmap) {
|
||||
gates[CLKGEN_MISCSYS_AHB2_TEESYS_HCLK] = thead_gate_clk_register("clkgen_ahb2_teesys_hclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 0, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_APB3_TEESYS_HCLK] = thead_gate_clk_register("clkgen_apb3_teesys_hclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 1, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_AXI4_TEESYS_ACLK] = thead_gate_clk_register("clkgen_axi4_teesys_aclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 2, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EIP120SI_CLK] = thead_gate_clk_register("clkgen_eip120si_clk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 3, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EIP120SII_CLK] = thead_gate_clk_register("clkgen_eip120sii_clk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 4, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EIP120SIII_CLK] = thead_gate_clk_register("clkgen_eip120siii_clk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 5, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_TEEDMAC_CLK] = thead_gate_clk_register("clkgen_teedmac_clk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 6, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EIP150B_HCLK] = thead_gate_clk_register("clkgen_eip150b_hclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 7, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_OCRAM_HCLK] = thead_gate_clk_register("clkgen_ocram_hclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 8, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_EFUSE_PCLK] = thead_gate_clk_register("clkgen_efuse_pclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 9, GATE_NOT_SHARED, NULL, dev);
|
||||
gates[CLKGEN_MISCSYS_TEE_SYSREG_PCLK] = thead_gate_clk_register("clkgen_tee_sysreg_pclk", NULL,
|
||||
tee_miscsys_regmap, 0x120, 10, GATE_NOT_SHARED, NULL, dev);
|
||||
}
|
||||
clk_gate_data.clks = gates;
|
||||
clk_gate_data.clk_num = ARRAY_SIZE(gates);
|
||||
ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_gate_data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register gate clks for light miscsys\n");
|
||||
goto unregister_clks;
|
||||
}
|
||||
dev_info(dev, "succeed to register miscsys gate clock provider\n");
|
||||
return 0;
|
||||
unregister_clks:
|
||||
thead_unregister_clocks(gates, ARRAY_SIZE(gates));
|
||||
return ret;
|
||||
}
|
||||
static const struct of_device_id miscsys_clk_gate_of_match[] = {
|
||||
{ .compatible = "thead,miscsys-gate-controller" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, miscsys_clk_gate_of_match);
|
||||
static struct platform_driver light_miscsys_clk_driver = {
|
||||
.probe = light_miscsys_clk_probe,
|
||||
.driver = {
|
||||
.name = "miscsys-clk-gate-provider",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(miscsys_clk_gate_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(light_miscsys_clk_driver);
|
||||
MODULE_AUTHOR("wei.liu <lw312886@linux.alibaba.com>");
|
||||
MODULE_AUTHOR("Esther.Z <Esther.Z@linux.alibaba.com>");
|
||||
MODULE_DESCRIPTION("Thead Light Fullmask miscsys clock gate provider");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -34,15 +34,18 @@ static int light_vpsys_clk_probe(struct platform_device *pdev)
|
||||
if (WARN_ON(IS_ERR(gate_base)))
|
||||
return PTR_ERR(gate_base);
|
||||
|
||||
/* we assume that the gate clock is a root clock */
|
||||
// DIV & CDE
|
||||
gates[LIGHT_VPSYS_G2D_CCLK_DIV] = thead_clk_light_divider("light_vpsys_g2d_cclk_div", "video_pll_foutvco", gate_base + 0x30, 0, 4, 4, MUX_TYPE_DIV, 3, 9);
|
||||
|
||||
/* G2D clock configuration : Completed the upward configuration of CCLK */
|
||||
gates[LIGHT_VPSYS_G2D_PCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_pclk", NULL,
|
||||
gate_base + 0x20, 3, &share_cnt_g2d_clk_en);
|
||||
gates[LIGHT_VPSYS_G2D_ACLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_aclk", NULL,
|
||||
gate_base + 0x20, 3, &share_cnt_g2d_clk_en);
|
||||
gates[LIGHT_VPSYS_G2D_CCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_cclk", NULL,
|
||||
gates[LIGHT_VPSYS_G2D_CCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_cclk", "light_vpsys_g2d_cclk_div",
|
||||
gate_base + 0x20, 3, &share_cnt_g2d_clk_en);
|
||||
|
||||
|
||||
/* we assume that the gate clock is a root clock */
|
||||
gates[LIGHT_VPSYS_FCE_PCLK] = thead_clk_light_gate_shared("clkgen_vpsys_fce_pclk", NULL,
|
||||
gate_base + 0x20, 2, &share_cnt_fce_clk_en);
|
||||
gates[LIGHT_VPSYS_FCE_ACLK] = thead_clk_light_gate_shared("clkgen_vpsys_fce_aclk", NULL,
|
||||
|
||||
@@ -40,6 +40,9 @@ enum LIGHT_MPW_CPUFREQ_CLKS {
|
||||
#define LIGHT_C910_BUS_CLK_DIV_RATIO_2 0x100
|
||||
#define LIGHT_C910_BUS_CLK_DIV_RATIO_3 0x200
|
||||
|
||||
#define LIGHT_CPU_PLL_IDX(x) (x)
|
||||
#define LIGHT_CPU_PLL_COUNT 2
|
||||
|
||||
static int num_clks;
|
||||
static struct clk_bulk_data clks[] = {
|
||||
{ .id = "c910_cclk" },
|
||||
@@ -51,6 +54,7 @@ static struct clk_bulk_data clks[] = {
|
||||
static struct device *cpu_dev;
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
static unsigned int max_freq;
|
||||
static unsigned int min_freq;
|
||||
static unsigned int transition_latency;
|
||||
static void __iomem *ap_sys_reg;
|
||||
static bool light_dvfs_sv = false;
|
||||
@@ -58,6 +62,40 @@ static bool light_dvfs_sv = false;
|
||||
static u32 *light_dvddm_volt;
|
||||
static u32 soc_opp_count = 0;
|
||||
|
||||
static int _light_get_pllid(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)),
|
||||
__clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) // pll index 0
|
||||
ret = LIGHT_CPU_PLL_IDX(0);
|
||||
else // pll index 1
|
||||
ret = LIGHT_CPU_PLL_IDX(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _light_switch_pllid(int pllid, int target_freq)
|
||||
{
|
||||
pr_debug("[%s] switchto pll[%d], freq[%u]\n", __func__, pllid, target_freq);
|
||||
if (pllid == LIGHT_CPU_PLL_IDX(1)) {
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, target_freq * 1000);
|
||||
clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
udelay(1);
|
||||
clk_disable_unprepare(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk);
|
||||
} else {
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, target_freq * 1000);
|
||||
clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk);
|
||||
udelay(1);
|
||||
clk_disable_unprepare(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int light_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
@@ -140,20 +178,8 @@ static int light_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)),
|
||||
__clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) {
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, new_freq * 1000);
|
||||
ret = clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
udelay(1);
|
||||
clk_disable_unprepare(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk);
|
||||
} else {
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, new_freq * 1000);
|
||||
ret = clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk);
|
||||
udelay(1);
|
||||
clk_disable_unprepare(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
}
|
||||
/* switch pll */
|
||||
_light_switch_pllid((_light_get_pllid()+1)&(LIGHT_CPU_PLL_COUNT-1), new_freq);
|
||||
|
||||
/*add delay for clk-switch*/
|
||||
udelay(1);
|
||||
@@ -200,6 +226,35 @@ static int light_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_cpufreq_suspend(struct cpufreq_policy *policy)
|
||||
{
|
||||
int ret;
|
||||
int index;
|
||||
|
||||
pr_debug("%s: cpu: %d, %u KHz to %u KHz\n",
|
||||
__func__, policy->cpu, policy->cur, policy->suspend_freq);
|
||||
|
||||
ret = cpufreq_generic_suspend(policy);
|
||||
if (ret) {
|
||||
pr_err("%s: failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only CPU PLL0 would be active after STR resume. We should switch
|
||||
* CPU PLL to be PLL0 after policy stopped.
|
||||
*/
|
||||
if (_light_get_pllid() == LIGHT_CPU_PLL_IDX(1))
|
||||
_light_switch_pllid(LIGHT_CPU_PLL_IDX(0), policy->suspend_freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_cpufreq_resume(struct cpufreq_policy *policy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
policy->clk = clks[LIGHT_C910_CCLK].clk;
|
||||
@@ -234,7 +289,8 @@ static struct cpufreq_driver light_cpufreq_driver = {
|
||||
.init = light_cpufreq_init,
|
||||
.name = "light-cpufreq",
|
||||
.attr = cpufreq_generic_attr,
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
.suspend = light_cpufreq_suspend,
|
||||
.resume = light_cpufreq_resume,
|
||||
};
|
||||
|
||||
static int light_cpufreq_pm_notify(struct notifier_block *nb,
|
||||
@@ -274,15 +330,9 @@ static int panic_cpufreq_notifier_call(struct notifier_block *nb,
|
||||
* set CPU PLL1's frequency as minimum to compatible voltage
|
||||
* becarefull if the PLL1 is serving the cpu core, swith to PLL0 first
|
||||
*/
|
||||
if (strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)),
|
||||
__clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) {
|
||||
pr_debug("[%s,%d]\n", __func__, __LINE__);
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, policy->min * 1000);
|
||||
udelay(1);
|
||||
clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk);
|
||||
|
||||
if (_light_get_pllid() == LIGHT_CPU_PLL_IDX(1)) {
|
||||
pr_debug("[%s,%d]\n", __func__, __LINE__);
|
||||
_light_switch_pllid(LIGHT_CPU_PLL_IDX(0), policy->min);
|
||||
}
|
||||
|
||||
pr_debug("[%s,%d]\n", __func__, __LINE__);
|
||||
@@ -292,9 +342,7 @@ static int panic_cpufreq_notifier_call(struct notifier_block *nb,
|
||||
* set the CPU PLL1's frequency as minimum in advance, otherwise the
|
||||
* system may crash in crash kernel stage.
|
||||
*/
|
||||
clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk);
|
||||
clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, policy->min * 1000);
|
||||
udelay(1);
|
||||
_light_switch_pllid(LIGHT_CPU_PLL_IDX(1), policy->min);
|
||||
|
||||
pr_info("finish to execute cpufreq notifier callback on panic\n");
|
||||
|
||||
@@ -411,6 +459,7 @@ soc_opp_out:
|
||||
transition_latency = CPUFREQ_ETERNAL;
|
||||
|
||||
max_freq = freq_table[--num].frequency;
|
||||
min_freq = freq_table[0].frequency;
|
||||
|
||||
ret = cpufreq_register_driver(&light_cpufreq_driver);
|
||||
if (ret) {
|
||||
|
||||
@@ -47,6 +47,10 @@ config CPU_IDLE_GOV_HALTPOLL
|
||||
config DT_IDLE_STATES
|
||||
bool
|
||||
|
||||
config DT_IDLE_GENPD
|
||||
depends on PM_GENERIC_DOMAINS_OF
|
||||
bool
|
||||
|
||||
menu "ARM CPU Idle Drivers"
|
||||
depends on ARM || ARM64
|
||||
source "drivers/cpuidle/Kconfig.arm"
|
||||
|
||||
@@ -27,6 +27,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN
|
||||
bool "PSCI CPU idle Domain"
|
||||
depends on ARM_PSCI_CPUIDLE
|
||||
depends on PM_GENERIC_DOMAINS_OF
|
||||
select DT_IDLE_GENPD
|
||||
default y
|
||||
help
|
||||
Select this to enable the PSCI based CPUidle driver to use PM domains,
|
||||
|
||||
@@ -11,3 +11,13 @@ config LIGHT_CPUIDLE
|
||||
Select this option to enable processor idle state management
|
||||
through cpuidle subsystem.
|
||||
|
||||
config RISCV_SBI_CPUIDLE
|
||||
bool "RISC-V SBI CPU idle Driver"
|
||||
depends on RISCV_SBI
|
||||
select DT_IDLE_STATES
|
||||
select CPU_IDLE_MULTIPLE_DRIVERS
|
||||
select DT_IDLE_GENPD if PM_GENERIC_DOMAINS_OF
|
||||
help
|
||||
Select this option to enable RISC-V SBI firmware based CPU idle
|
||||
driver for RISC-V systems. This drivers also supports hierarchical
|
||||
DT based layout of the idle state.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
||||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
|
||||
obj-$(CONFIG_DT_IDLE_GENPD) += dt_idle_genpd.o
|
||||
obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o
|
||||
obj-$(CONFIG_HALTPOLL_CPUIDLE) += cpuidle-haltpoll.o
|
||||
|
||||
@@ -34,6 +35,8 @@ obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
|
||||
# POWERPC drivers
|
||||
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
|
||||
obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
|
||||
|
||||
###############################################################################
|
||||
# RISC-V drivers
|
||||
obj-$(CONFIG_LIGHT_CPUIDLE) += cpuidle-light.o
|
||||
obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o
|
||||
|
||||
@@ -47,73 +47,14 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psci_pd_parse_state_nodes(struct genpd_power_state *states,
|
||||
int state_count)
|
||||
{
|
||||
int i, ret;
|
||||
u32 psci_state, *psci_state_buf;
|
||||
|
||||
for (i = 0; i < state_count; i++) {
|
||||
ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode),
|
||||
&psci_state);
|
||||
if (ret)
|
||||
goto free_state;
|
||||
|
||||
psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
|
||||
if (!psci_state_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto free_state;
|
||||
}
|
||||
*psci_state_buf = psci_state;
|
||||
states[i].data = psci_state_buf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_state:
|
||||
i--;
|
||||
for (; i >= 0; i--)
|
||||
kfree(states[i].data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int psci_pd_parse_states(struct device_node *np,
|
||||
struct genpd_power_state **states, int *state_count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Parse the domain idle states. */
|
||||
ret = of_genpd_parse_idle_states(np, states, state_count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Fill out the PSCI specifics for each found state. */
|
||||
ret = psci_pd_parse_state_nodes(*states, *state_count);
|
||||
if (ret)
|
||||
kfree(*states);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void psci_pd_free_states(struct genpd_power_state *states,
|
||||
unsigned int state_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state_count; i++)
|
||||
kfree(states[i].data);
|
||||
kfree(states);
|
||||
}
|
||||
|
||||
static int psci_pd_init(struct device_node *np, bool use_osi)
|
||||
{
|
||||
struct generic_pm_domain *pd;
|
||||
struct psci_pd_provider *pd_provider;
|
||||
struct dev_power_governor *pd_gov;
|
||||
struct genpd_power_state *states = NULL;
|
||||
int ret = -ENOMEM, state_count = 0;
|
||||
|
||||
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
|
||||
pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node);
|
||||
if (!pd)
|
||||
goto out;
|
||||
|
||||
@@ -121,22 +62,6 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
|
||||
if (!pd_provider)
|
||||
goto free_pd;
|
||||
|
||||
pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
|
||||
if (!pd->name)
|
||||
goto free_pd_prov;
|
||||
|
||||
/*
|
||||
* Parse the domain idle states and let genpd manage the state selection
|
||||
* for those being compatible with "domain-idle-state".
|
||||
*/
|
||||
ret = psci_pd_parse_states(np, &states, &state_count);
|
||||
if (ret)
|
||||
goto free_name;
|
||||
|
||||
pd->free_states = psci_pd_free_states;
|
||||
pd->name = kbasename(pd->name);
|
||||
pd->states = states;
|
||||
pd->state_count = state_count;
|
||||
pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
|
||||
|
||||
/* Allow power off when OSI has been successfully enabled. */
|
||||
@@ -149,10 +74,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
|
||||
pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;
|
||||
|
||||
ret = pm_genpd_init(pd, pd_gov, false);
|
||||
if (ret) {
|
||||
psci_pd_free_states(states, state_count);
|
||||
goto free_name;
|
||||
}
|
||||
if (ret)
|
||||
goto free_pd_prov;
|
||||
|
||||
ret = of_genpd_add_provider_simple(np, pd);
|
||||
if (ret)
|
||||
@@ -166,12 +89,10 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
|
||||
|
||||
remove_pd:
|
||||
pm_genpd_remove(pd);
|
||||
free_name:
|
||||
kfree(pd->name);
|
||||
free_pd_prov:
|
||||
kfree(pd_provider);
|
||||
free_pd:
|
||||
kfree(pd);
|
||||
dt_idle_pd_free(pd);
|
||||
out:
|
||||
pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
|
||||
return ret;
|
||||
@@ -195,30 +116,6 @@ static void psci_pd_remove(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int psci_pd_init_topology(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct of_phandle_args child, parent;
|
||||
int ret;
|
||||
|
||||
for_each_child_of_node(np, node) {
|
||||
if (of_parse_phandle_with_args(node, "power-domains",
|
||||
"#power-domain-cells", 0, &parent))
|
||||
continue;
|
||||
|
||||
child.np = node;
|
||||
child.args_count = 0;
|
||||
ret = of_genpd_add_subdomain(&parent, &child);
|
||||
of_node_put(parent.np);
|
||||
if (ret) {
|
||||
of_node_put(node);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool psci_pd_try_set_osi_mode(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -282,7 +179,7 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev)
|
||||
goto no_pd;
|
||||
|
||||
/* Link genpd masters/subdomains to model the CPU topology. */
|
||||
ret = psci_pd_init_topology(np);
|
||||
ret = dt_idle_pd_init_topology(np);
|
||||
if (ret)
|
||||
goto remove_pd;
|
||||
|
||||
@@ -314,28 +211,3 @@ static int __init psci_idle_init_domains(void)
|
||||
return platform_driver_register(&psci_cpuidle_domain_driver);
|
||||
}
|
||||
subsys_initcall(psci_idle_init_domains);
|
||||
|
||||
struct device *psci_dt_attach_cpu(int cpu)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci");
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return dev;
|
||||
|
||||
pm_runtime_irq_safe(dev);
|
||||
if (cpu_online(cpu))
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
dev_pm_syscore_device(dev, true);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void psci_dt_detach_cpu(struct device *dev)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return;
|
||||
|
||||
dev_pm_domain_detach(dev, false);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,19 @@ void psci_set_domain_state(u32 state);
|
||||
int psci_dt_parse_state_node(struct device_node *np, u32 *state);
|
||||
|
||||
#ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN
|
||||
struct device *psci_dt_attach_cpu(int cpu);
|
||||
void psci_dt_detach_cpu(struct device *dev);
|
||||
|
||||
#include "dt_idle_genpd.h"
|
||||
|
||||
static inline struct device *psci_dt_attach_cpu(int cpu)
|
||||
{
|
||||
return dt_idle_attach_cpu(cpu, "psci");
|
||||
}
|
||||
|
||||
static inline void psci_dt_detach_cpu(struct device *dev)
|
||||
{
|
||||
dt_idle_detach_cpu(dev);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; }
|
||||
static inline void psci_dt_detach_cpu(struct device *dev) { }
|
||||
|
||||
639
drivers/cpuidle/cpuidle-riscv-sbi.c
Normal file
639
drivers/cpuidle/cpuidle-riscv-sbi.c
Normal file
@@ -0,0 +1,639 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* RISC-V SBI CPU idle driver.
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cpuidle-riscv-sbi: " fmt
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include "dt_idle_states.h"
|
||||
#include "dt_idle_genpd.h"
|
||||
|
||||
struct sbi_cpuidle_data {
|
||||
u32 *states;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct sbi_domain_state {
|
||||
bool available;
|
||||
u32 state;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data);
|
||||
static DEFINE_PER_CPU(struct sbi_domain_state, domain_state);
|
||||
static bool sbi_cpuidle_use_osi;
|
||||
static bool sbi_cpuidle_use_cpuhp;
|
||||
static bool sbi_cpuidle_pd_allow_domain_state;
|
||||
|
||||
extern void arch_cpu_idle(void);
|
||||
|
||||
static inline void sbi_set_domain_state(u32 state)
|
||||
{
|
||||
struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
|
||||
|
||||
data->available = true;
|
||||
data->state = state;
|
||||
}
|
||||
|
||||
static inline u32 sbi_get_domain_state(void)
|
||||
{
|
||||
struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
|
||||
|
||||
return data->state;
|
||||
}
|
||||
|
||||
static inline void sbi_clear_domain_state(void)
|
||||
{
|
||||
struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
|
||||
|
||||
data->available = false;
|
||||
}
|
||||
|
||||
static inline bool sbi_is_domain_state_available(void)
|
||||
{
|
||||
struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
|
||||
|
||||
return data->available;
|
||||
}
|
||||
|
||||
/* Actual code that puts the SoC in different idle states */
|
||||
static int light_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
arch_cpu_idle();
|
||||
return index;
|
||||
}
|
||||
|
||||
static int sbi_suspend_finisher(unsigned long suspend_type,
|
||||
unsigned long resume_addr,
|
||||
unsigned long opaque)
|
||||
{
|
||||
struct sbiret ret;
|
||||
|
||||
ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND,
|
||||
suspend_type, resume_addr, opaque, 0, 0, 0);
|
||||
|
||||
return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0;
|
||||
}
|
||||
|
||||
static int sbi_suspend(u32 state)
|
||||
{
|
||||
if (state & SBI_HSM_SUSP_NON_RET_BIT)
|
||||
return cpu_suspend(state, sbi_suspend_finisher);
|
||||
else
|
||||
return sbi_suspend_finisher(state, 0, 0);
|
||||
}
|
||||
|
||||
static int sbi_cpuidle_enter_state(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
u32 *states = __this_cpu_read(sbi_cpuidle_data.states);
|
||||
|
||||
return CPU_PM_CPU_IDLE_ENTER_PARAM(sbi_suspend, idx, states[idx]);
|
||||
}
|
||||
|
||||
static int __sbi_enter_domain_idle_state(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx,
|
||||
bool s2idle)
|
||||
{
|
||||
struct sbi_cpuidle_data *data = this_cpu_ptr(&sbi_cpuidle_data);
|
||||
u32 *states = data->states;
|
||||
struct device *pd_dev = data->dev;
|
||||
u32 state;
|
||||
int ret;
|
||||
|
||||
ret = cpu_pm_enter();
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
/* Do runtime PM to manage a hierarchical CPU toplogy. */
|
||||
rcu_irq_enter_irqson();
|
||||
if (s2idle)
|
||||
dev_pm_genpd_suspend(pd_dev);
|
||||
else
|
||||
pm_runtime_put_sync_suspend(pd_dev);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
if (sbi_is_domain_state_available())
|
||||
state = sbi_get_domain_state();
|
||||
else
|
||||
state = states[idx];
|
||||
|
||||
ret = sbi_suspend(state) ? -1 : idx;
|
||||
|
||||
rcu_irq_enter_irqson();
|
||||
if (s2idle)
|
||||
dev_pm_genpd_resume(pd_dev);
|
||||
else
|
||||
pm_runtime_get_sync(pd_dev);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
cpu_pm_exit();
|
||||
|
||||
/* Clear the domain state to start fresh when back from idle. */
|
||||
sbi_clear_domain_state();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sbi_enter_domain_idle_state(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
return __sbi_enter_domain_idle_state(dev, drv, idx, false);
|
||||
}
|
||||
|
||||
static int sbi_enter_s2idle_domain_idle_state(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int idx)
|
||||
{
|
||||
return __sbi_enter_domain_idle_state(dev, drv, idx, true);
|
||||
}
|
||||
|
||||
static int sbi_cpuidle_cpuhp_up(unsigned int cpu)
|
||||
{
|
||||
struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev);
|
||||
|
||||
if (pd_dev)
|
||||
pm_runtime_get_sync(pd_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_cpuidle_cpuhp_down(unsigned int cpu)
|
||||
{
|
||||
struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev);
|
||||
|
||||
if (pd_dev) {
|
||||
pm_runtime_put_sync(pd_dev);
|
||||
/* Clear domain state to start fresh at next online. */
|
||||
sbi_clear_domain_state();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sbi_idle_init_cpuhp(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!sbi_cpuidle_use_cpuhp)
|
||||
return;
|
||||
|
||||
err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
|
||||
"cpuidle/sbi:online",
|
||||
sbi_cpuidle_cpuhp_up,
|
||||
sbi_cpuidle_cpuhp_down);
|
||||
if (err)
|
||||
pr_warn("Failed %d while setup cpuhp state\n", err);
|
||||
}
|
||||
|
||||
static const struct of_device_id sbi_cpuidle_state_match[] = {
|
||||
{ .compatible = "riscv,idle-state",
|
||||
.data = sbi_cpuidle_enter_state },
|
||||
{ },
|
||||
};
|
||||
|
||||
static bool sbi_suspend_state_is_valid(u32 state)
|
||||
{
|
||||
if (state > SBI_HSM_SUSPEND_RET_DEFAULT &&
|
||||
state < SBI_HSM_SUSPEND_RET_PLATFORM)
|
||||
return false;
|
||||
if (state > SBI_HSM_SUSPEND_NON_RET_DEFAULT &&
|
||||
state < SBI_HSM_SUSPEND_NON_RET_PLATFORM)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
|
||||
{
|
||||
int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state);
|
||||
|
||||
if (err) {
|
||||
pr_warn("%pOF missing riscv,sbi-suspend-param property\n", np);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!sbi_suspend_state_is_valid(*state)) {
|
||||
pr_warn("Invalid SBI suspend state %#x\n", *state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_dt_cpu_init_topology(struct cpuidle_driver *drv,
|
||||
struct sbi_cpuidle_data *data,
|
||||
unsigned int state_count, int cpu)
|
||||
{
|
||||
/* Currently limit the hierarchical topology to be used in OSI mode. */
|
||||
if (!sbi_cpuidle_use_osi)
|
||||
return 0;
|
||||
|
||||
data->dev = dt_idle_attach_cpu(cpu, "sbi");
|
||||
if (IS_ERR_OR_NULL(data->dev))
|
||||
return PTR_ERR_OR_ZERO(data->dev);
|
||||
|
||||
/*
|
||||
* Using the deepest state for the CPU to trigger a potential selection
|
||||
* of a shared state for the domain, assumes the domain states are all
|
||||
* deeper states.
|
||||
*/
|
||||
drv->states[state_count - 1].enter = sbi_enter_domain_idle_state;
|
||||
drv->states[state_count - 1].enter_s2idle =
|
||||
sbi_enter_s2idle_domain_idle_state;
|
||||
sbi_cpuidle_use_cpuhp = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_cpuidle_dt_init_states(struct device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
unsigned int cpu,
|
||||
unsigned int state_count)
|
||||
{
|
||||
struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu);
|
||||
struct device_node *state_node;
|
||||
struct device_node *cpu_node;
|
||||
u32 *states;
|
||||
int i, ret;
|
||||
|
||||
cpu_node = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_node)
|
||||
return -ENODEV;
|
||||
|
||||
states = devm_kcalloc(dev, state_count, sizeof(*states), GFP_KERNEL);
|
||||
if (!states) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Parse SBI specific details from state DT nodes */
|
||||
for (i = 1; i < state_count; i++) {
|
||||
state_node = of_get_cpu_state_node(cpu_node, i - 1);
|
||||
if (!state_node)
|
||||
break;
|
||||
|
||||
ret = sbi_dt_parse_state_node(state_node, &states[i]);
|
||||
of_node_put(state_node);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_debug("sbi-state %#x index %d\n", states[i], i);
|
||||
}
|
||||
if (i != state_count) {
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Initialize optional data, used for the hierarchical topology. */
|
||||
ret = sbi_dt_cpu_init_topology(drv, data, state_count, cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Store states in the per-cpu struct. */
|
||||
data->states = states;
|
||||
|
||||
fail:
|
||||
of_node_put(cpu_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sbi_cpuidle_deinit_cpu(int cpu)
|
||||
{
|
||||
struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu);
|
||||
|
||||
dt_idle_detach_cpu(data->dev);
|
||||
sbi_cpuidle_use_cpuhp = false;
|
||||
}
|
||||
|
||||
static int sbi_cpuidle_init_cpu(struct device *dev, int cpu)
|
||||
{
|
||||
struct cpuidle_driver *drv;
|
||||
unsigned int state_count = 0;
|
||||
int ret = 0;
|
||||
|
||||
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
drv->name = "sbi_cpuidle";
|
||||
drv->owner = THIS_MODULE;
|
||||
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
|
||||
|
||||
/* RISC-V architectural WFI to be represented as state index 0. */
|
||||
drv->states[0].enter = sbi_cpuidle_enter_state;
|
||||
drv->states[0].exit_latency = 1;
|
||||
drv->states[0].target_residency = 1;
|
||||
drv->states[0].power_usage = UINT_MAX;
|
||||
strcpy(drv->states[0].name, "WFI");
|
||||
strcpy(drv->states[0].desc, "RISC-V WFI");
|
||||
|
||||
/*
|
||||
* If no DT idle states are detected (ret == 0) let the driver
|
||||
* initialization fail accordingly since there is no reason to
|
||||
* initialize the idle driver if only wfi is supported, the
|
||||
* default archictectural back-end already executes wfi
|
||||
* on idle entry.
|
||||
*/
|
||||
ret = dt_init_idle_driver(drv, sbi_cpuidle_state_match, 1);
|
||||
if (ret <= 0) {
|
||||
pr_debug("HART%ld: failed to parse DT idle states\n",
|
||||
cpuid_to_hartid_map(cpu));
|
||||
return ret ? : -ENODEV;
|
||||
}
|
||||
state_count = ret + 1; /* Include WFI state as well */
|
||||
|
||||
/* Initialize idle states from DT. */
|
||||
ret = sbi_cpuidle_dt_init_states(dev, drv, cpu, state_count);
|
||||
if (ret) {
|
||||
pr_err("HART%ld: failed to init idle states\n",
|
||||
cpuid_to_hartid_map(cpu));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpuidle_register(drv, NULL);
|
||||
if (ret)
|
||||
goto deinit;
|
||||
|
||||
cpuidle_cooling_register(drv);
|
||||
|
||||
return 0;
|
||||
deinit:
|
||||
sbi_cpuidle_deinit_cpu(cpu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sbi_cpuidle_domain_sync_state(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* All devices have now been attached/probed to the PM domain
|
||||
* topology, hence it's fine to allow domain states to be picked.
|
||||
*/
|
||||
sbi_cpuidle_pd_allow_domain_state = true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DT_IDLE_GENPD
|
||||
|
||||
static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
|
||||
{
|
||||
struct genpd_power_state *state = &pd->states[pd->state_idx];
|
||||
u32 *pd_state;
|
||||
|
||||
if (!state->data)
|
||||
return 0;
|
||||
|
||||
if (!sbi_cpuidle_pd_allow_domain_state)
|
||||
return -EBUSY;
|
||||
|
||||
/* OSI mode is enabled, set the corresponding domain state. */
|
||||
pd_state = state->data;
|
||||
sbi_set_domain_state(*pd_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sbi_pd_provider {
|
||||
struct list_head link;
|
||||
struct device_node *node;
|
||||
};
|
||||
|
||||
static LIST_HEAD(sbi_pd_providers);
|
||||
|
||||
static int sbi_pd_init(struct device_node *np)
|
||||
{
|
||||
struct generic_pm_domain *pd;
|
||||
struct sbi_pd_provider *pd_provider;
|
||||
struct dev_power_governor *pd_gov;
|
||||
int ret = -ENOMEM, state_count = 0;
|
||||
|
||||
pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node);
|
||||
if (!pd)
|
||||
goto out;
|
||||
|
||||
pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
|
||||
if (!pd_provider)
|
||||
goto free_pd;
|
||||
|
||||
pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
|
||||
|
||||
/* Allow power off when OSI is available. */
|
||||
if (sbi_cpuidle_use_osi)
|
||||
pd->power_off = sbi_cpuidle_pd_power_off;
|
||||
else
|
||||
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;
|
||||
|
||||
ret = pm_genpd_init(pd, pd_gov, false);
|
||||
if (ret)
|
||||
goto free_pd_prov;
|
||||
|
||||
ret = of_genpd_add_provider_simple(np, pd);
|
||||
if (ret)
|
||||
goto remove_pd;
|
||||
|
||||
pd_provider->node = of_node_get(np);
|
||||
list_add(&pd_provider->link, &sbi_pd_providers);
|
||||
|
||||
pr_debug("init PM domain %s\n", pd->name);
|
||||
return 0;
|
||||
|
||||
remove_pd:
|
||||
pm_genpd_remove(pd);
|
||||
free_pd_prov:
|
||||
kfree(pd_provider);
|
||||
free_pd:
|
||||
dt_idle_pd_free(pd);
|
||||
out:
|
||||
pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sbi_pd_remove(void)
|
||||
{
|
||||
struct sbi_pd_provider *pd_provider, *it;
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
list_for_each_entry_safe(pd_provider, it, &sbi_pd_providers, link) {
|
||||
of_genpd_del_provider(pd_provider->node);
|
||||
|
||||
genpd = of_genpd_remove_last(pd_provider->node);
|
||||
if (!IS_ERR(genpd))
|
||||
kfree(genpd);
|
||||
|
||||
of_node_put(pd_provider->node);
|
||||
list_del(&pd_provider->link);
|
||||
kfree(pd_provider);
|
||||
}
|
||||
}
|
||||
|
||||
static int sbi_genpd_probe(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
int ret = 0, pd_count = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Parse child nodes for the "#power-domain-cells" property and
|
||||
* initialize a genpd/genpd-of-provider pair when it's found.
|
||||
*/
|
||||
for_each_child_of_node(np, node) {
|
||||
if (!of_find_property(node, "#power-domain-cells", NULL))
|
||||
continue;
|
||||
|
||||
ret = sbi_pd_init(node);
|
||||
if (ret)
|
||||
goto put_node;
|
||||
|
||||
pd_count++;
|
||||
}
|
||||
|
||||
/* Bail out if not using the hierarchical CPU topology. */
|
||||
if (!pd_count)
|
||||
goto no_pd;
|
||||
|
||||
/* Link genpd masters/subdomains to model the CPU topology. */
|
||||
ret = dt_idle_pd_init_topology(np);
|
||||
if (ret)
|
||||
goto remove_pd;
|
||||
|
||||
return 0;
|
||||
|
||||
put_node:
|
||||
of_node_put(node);
|
||||
remove_pd:
|
||||
sbi_pd_remove();
|
||||
pr_err("failed to create CPU PM domains ret=%d\n", ret);
|
||||
no_pd:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int sbi_genpd_probe(struct device_node *np)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int sbi_cpuidle_probe(struct platform_device *pdev)
|
||||
{
|
||||
int cpu, ret;
|
||||
struct cpuidle_driver *drv;
|
||||
struct cpuidle_device *dev;
|
||||
struct device_node *np, *pds_node;
|
||||
|
||||
/* Detect OSI support based on CPU DT nodes */
|
||||
sbi_cpuidle_use_osi = true;
|
||||
for_each_possible_cpu(cpu) {
|
||||
np = of_cpu_device_node_get(cpu);
|
||||
if (np &&
|
||||
of_find_property(np, "power-domains", NULL) &&
|
||||
of_find_property(np, "power-domain-names", NULL)) {
|
||||
continue;
|
||||
} else {
|
||||
sbi_cpuidle_use_osi = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate generic power domains from DT nodes */
|
||||
pds_node = of_find_node_by_path("/cpus/power-domains");
|
||||
if (pds_node) {
|
||||
ret = sbi_genpd_probe(pds_node);
|
||||
of_node_put(pds_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize CPU idle driver for each CPU */
|
||||
for_each_possible_cpu(cpu) {
|
||||
ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu);
|
||||
if (ret) {
|
||||
pr_debug("HART%ld: idle driver init failed\n",
|
||||
cpuid_to_hartid_map(cpu));
|
||||
goto out_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup CPU hotplut notifiers */
|
||||
sbi_idle_init_cpuhp();
|
||||
|
||||
pr_info("idle driver registered for all CPUs\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
while (--cpu >= 0) {
|
||||
dev = per_cpu(cpuidle_devices, cpu);
|
||||
drv = cpuidle_get_cpu_driver(dev);
|
||||
cpuidle_unregister(drv);
|
||||
sbi_cpuidle_deinit_cpu(cpu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver sbi_cpuidle_driver = {
|
||||
.probe = sbi_cpuidle_probe,
|
||||
.driver = {
|
||||
.name = "sbi-cpuidle",
|
||||
.sync_state = sbi_cpuidle_domain_sync_state,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sbi_cpuidle_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct platform_device *pdev;
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* The SBI HSM suspend function is only available when:
|
||||
* 1) SBI version is 0.3 or higher
|
||||
* 2) SBI HSM extension is available
|
||||
*/
|
||||
if ((sbi_spec_version < sbi_mk_version(0, 3)) ||
|
||||
sbi_probe_extension(SBI_EXT_HSM) <= 0) {
|
||||
pr_info("HSM suspend not available\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
ret = platform_driver_register(&sbi_cpuidle_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdev = platform_device_register_simple("sbi-cpuidle",
|
||||
-1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
platform_driver_unregister(&sbi_cpuidle_driver);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(sbi_cpuidle_init);
|
||||
178
drivers/cpuidle/dt_idle_genpd.c
Normal file
178
drivers/cpuidle/dt_idle_genpd.c
Normal file
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* PM domains for CPUs via genpd.
|
||||
*
|
||||
* Copyright (C) 2019 Linaro Ltd.
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "dt-idle-genpd: " fmt
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "dt_idle_genpd.h"
|
||||
|
||||
static int pd_parse_state_nodes(
|
||||
int (*parse_state)(struct device_node *, u32 *),
|
||||
struct genpd_power_state *states, int state_count)
|
||||
{
|
||||
int i, ret;
|
||||
u32 state, *state_buf;
|
||||
|
||||
for (i = 0; i < state_count; i++) {
|
||||
ret = parse_state(to_of_node(states[i].fwnode), &state);
|
||||
if (ret)
|
||||
goto free_state;
|
||||
|
||||
state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
|
||||
if (!state_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto free_state;
|
||||
}
|
||||
*state_buf = state;
|
||||
states[i].data = state_buf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_state:
|
||||
i--;
|
||||
for (; i >= 0; i--)
|
||||
kfree(states[i].data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pd_parse_states(struct device_node *np,
|
||||
int (*parse_state)(struct device_node *, u32 *),
|
||||
struct genpd_power_state **states,
|
||||
int *state_count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Parse the domain idle states. */
|
||||
ret = of_genpd_parse_idle_states(np, states, state_count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Fill out the dt specifics for each found state. */
|
||||
ret = pd_parse_state_nodes(parse_state, *states, *state_count);
|
||||
if (ret)
|
||||
kfree(*states);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pd_free_states(struct genpd_power_state *states,
|
||||
unsigned int state_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state_count; i++)
|
||||
kfree(states[i].data);
|
||||
kfree(states);
|
||||
}
|
||||
|
||||
void dt_idle_pd_free(struct generic_pm_domain *pd)
|
||||
{
|
||||
pd_free_states(pd->states, pd->state_count);
|
||||
kfree(pd->name);
|
||||
kfree(pd);
|
||||
}
|
||||
|
||||
struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
|
||||
int (*parse_state)(struct device_node *, u32 *))
|
||||
{
|
||||
struct generic_pm_domain *pd;
|
||||
struct genpd_power_state *states = NULL;
|
||||
int ret, state_count = 0;
|
||||
|
||||
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
|
||||
if (!pd)
|
||||
goto out;
|
||||
|
||||
pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
|
||||
if (!pd->name)
|
||||
goto free_pd;
|
||||
|
||||
/*
|
||||
* Parse the domain idle states and let genpd manage the state selection
|
||||
* for those being compatible with "domain-idle-state".
|
||||
*/
|
||||
ret = pd_parse_states(np, parse_state, &states, &state_count);
|
||||
if (ret)
|
||||
goto free_name;
|
||||
|
||||
pd->free_states = pd_free_states;
|
||||
pd->name = kbasename(pd->name);
|
||||
pd->states = states;
|
||||
pd->state_count = state_count;
|
||||
|
||||
pr_debug("alloc PM domain %s\n", pd->name);
|
||||
return pd;
|
||||
|
||||
free_name:
|
||||
kfree(pd->name);
|
||||
free_pd:
|
||||
kfree(pd);
|
||||
out:
|
||||
pr_err("failed to alloc PM domain %pOF\n", np);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dt_idle_pd_init_topology(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct of_phandle_args child, parent;
|
||||
int ret;
|
||||
|
||||
for_each_child_of_node(np, node) {
|
||||
if (of_parse_phandle_with_args(node, "power-domains",
|
||||
"#power-domain-cells", 0, &parent))
|
||||
continue;
|
||||
|
||||
child.np = node;
|
||||
child.args_count = 0;
|
||||
ret = of_genpd_add_subdomain(&parent, &child);
|
||||
of_node_put(parent.np);
|
||||
if (ret) {
|
||||
of_node_put(node);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct device *dt_idle_attach_cpu(int cpu, const char *name)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), name);
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return dev;
|
||||
|
||||
pm_runtime_irq_safe(dev);
|
||||
if (cpu_online(cpu))
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
dev_pm_syscore_device(dev, true);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void dt_idle_detach_cpu(struct device *dev)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return;
|
||||
|
||||
dev_pm_domain_detach(dev, false);
|
||||
}
|
||||
50
drivers/cpuidle/dt_idle_genpd.h
Normal file
50
drivers/cpuidle/dt_idle_genpd.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __DT_IDLE_GENPD
|
||||
#define __DT_IDLE_GENPD
|
||||
|
||||
struct device_node;
|
||||
struct generic_pm_domain;
|
||||
|
||||
#ifdef CONFIG_DT_IDLE_GENPD
|
||||
|
||||
void dt_idle_pd_free(struct generic_pm_domain *pd);
|
||||
|
||||
struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
|
||||
int (*parse_state)(struct device_node *, u32 *));
|
||||
|
||||
int dt_idle_pd_init_topology(struct device_node *np);
|
||||
|
||||
struct device *dt_idle_attach_cpu(int cpu, const char *name);
|
||||
|
||||
void dt_idle_detach_cpu(struct device *dev);
|
||||
|
||||
#else
|
||||
|
||||
static inline void dt_idle_pd_free(struct generic_pm_domain *pd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct generic_pm_domain *dt_idle_pd_alloc(
|
||||
struct device_node *np,
|
||||
int (*parse_state)(struct device_node *, u32 *))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int dt_idle_pd_init_topology(struct device_node *np)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct device *dt_idle_attach_cpu(int cpu, const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void dt_idle_detach_cpu(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -467,7 +467,7 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
|
||||
}
|
||||
dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan));
|
||||
|
||||
pm_runtime_get(chan->chip->dev);
|
||||
pm_runtime_get_sync(chan->chip->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -492,7 +492,7 @@ static void dma_chan_free_chan_resources(struct dma_chan *dchan)
|
||||
"%s: free resources, descriptor still allocated: %u\n",
|
||||
axi_chan_name(chan), atomic_read(&chan->descs_allocated));
|
||||
|
||||
pm_runtime_put(chan->chip->dev);
|
||||
pm_runtime_put_sync(chan->chip->dev);
|
||||
}
|
||||
|
||||
static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set)
|
||||
@@ -1111,7 +1111,8 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
|
||||
axi_chan_disable(chan);
|
||||
|
||||
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
|
||||
!(val & chan_active), 1000, 10000);
|
||||
!(val & chan_active), 1000, 100000);
|
||||
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(dchan2dev(dchan),
|
||||
"%s failed to stop\n", axi_chan_name(chan));
|
||||
@@ -1141,6 +1142,8 @@ static int dma_chan_pause(struct dma_chan *dchan)
|
||||
unsigned long flags;
|
||||
unsigned int timeout = 20; /* timeout iterations */
|
||||
u32 val;
|
||||
int ret;
|
||||
u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT;
|
||||
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
|
||||
@@ -1168,13 +1171,48 @@ static int dma_chan_pause(struct dma_chan *dchan)
|
||||
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
|
||||
chan->ch_sar = axi_chan_ioread32(chan, CH_SAR);
|
||||
chan->ch_dar = axi_chan_ioread32(chan, CH_DAR);
|
||||
chan->ch_dar_h = axi_chan_ioread32(chan, CH_DAR_H);
|
||||
chan->ch_block_ts = axi_chan_ioread32(chan, CH_BLOCK_TS);
|
||||
chan->ch_ctl_l = axi_chan_ioread32(chan, CH_CTL_L);
|
||||
chan->ch_ctl_h = axi_chan_ioread32(chan, CH_CTL_H);
|
||||
chan->ch_cfg_l = axi_chan_ioread32(chan, CH_CFG_L);
|
||||
chan->ch_cfg_h = axi_chan_ioread32(chan, CH_CFG_H);
|
||||
chan->ch_llp = axi_chan_ioread32(chan, CH_LLP);
|
||||
//printk("%s for %s ch_sar=0x%x ch_dar=0x%x ch_dar_h=0x%x ch_block_ts=0x%x ch_ctl_l=0x%x ch_ctl_h=0x%x ch_cfg_l=0x%x ch_cfg_h=0x%x ch_llp=0x%x\n", __func__,
|
||||
// axi_chan_name(chan), chan->ch_sar, chan->ch_dar, chan->ch_dar_h, chan->ch_block_ts, chan->ch_ctl_l, chan->ch_ctl_h, chan->ch_cfg_l, chan->ch_cfg_h, chan->ch_llp);
|
||||
|
||||
axi_chan_disable(chan);
|
||||
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
|
||||
!(val & chan_active), 1000, 100000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
printk("%s %s failed to stop\n", __func__, axi_chan_name(chan));
|
||||
|
||||
return timeout ? 0 : -EAGAIN;
|
||||
}
|
||||
|
||||
/* Called in chan locked context */
|
||||
static inline void axi_chan_resume(struct axi_dma_chan *chan)
|
||||
{
|
||||
u32 val;
|
||||
u32 val, irq_mask;
|
||||
struct axi_dma_desc *desc = chan->desc;
|
||||
struct axi_dma_hw_desc *hw_desc = desc->hw_desc;
|
||||
|
||||
axi_chan_iowrite32(chan, CH_SAR, chan->ch_sar);
|
||||
axi_chan_iowrite32(chan, CH_DAR, chan->ch_dar);
|
||||
axi_chan_iowrite32(chan, CH_DAR_H, chan->ch_dar_h);
|
||||
axi_chan_iowrite32(chan, CH_BLOCK_TS, chan->ch_block_ts);
|
||||
axi_chan_iowrite32(chan, CH_CTL_L, chan->ch_ctl_l);
|
||||
axi_chan_iowrite32(chan, CH_CTL_H, chan->ch_ctl_h);
|
||||
axi_chan_iowrite32(chan, CH_CFG_L, chan->ch_cfg_l);
|
||||
axi_chan_iowrite32(chan, CH_CFG_H, chan->ch_cfg_h);
|
||||
axi_chan_iowrite32(chan, CH_LLP, chan->ch_llp);
|
||||
irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR;
|
||||
axi_chan_irq_sig_set(chan, irq_mask);
|
||||
/* Generate 'suspend' status but don't generate interrupt */
|
||||
irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
|
||||
axi_chan_irq_set(chan, irq_mask);
|
||||
|
||||
val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
|
||||
if (chan->chip->dw->hdata->reg_map_8_channels) {
|
||||
@@ -1187,7 +1225,11 @@ static inline void axi_chan_resume(struct axi_dma_chan *chan)
|
||||
axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val);
|
||||
}
|
||||
|
||||
axi_chan_enable(chan);
|
||||
|
||||
chan->is_paused = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int dma_chan_resume(struct dma_chan *dchan)
|
||||
@@ -1234,6 +1276,21 @@ static int axi_dma_resume(struct axi_dma_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void axi_dma_dump(struct axi_dma_chip *chip)
|
||||
{
|
||||
struct dw_axi_dma *dw = chip->dw;
|
||||
struct axi_dma_chan *chan;
|
||||
u32 i;
|
||||
struct virt_dma_desc *vd;
|
||||
for (i = 0; i < dw->hdata->nr_channels; i++) {
|
||||
chan = &dw->chan[i];
|
||||
printk("%s chan name %s\n", __func__, axi_chan_name(chan));
|
||||
vd = vchan_next_desc(&chan->vc);
|
||||
axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int __maybe_unused axi_dma_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct axi_dma_chip *chip = dev_get_drvdata(dev);
|
||||
@@ -1248,6 +1305,42 @@ static int __maybe_unused axi_dma_runtime_resume(struct device *dev)
|
||||
return axi_dma_resume(chip);
|
||||
}
|
||||
|
||||
static int __maybe_unused axi_dma_sleep_suspend(struct device *dev)
|
||||
{
|
||||
|
||||
//struct axi_dma_chip *chip = dev_get_drvdata(dev);
|
||||
//axi_dma_irq_disable(chip);
|
||||
//axi_dma_disable(chip);
|
||||
|
||||
//clk_disable_unprepare(chip->core_clk);
|
||||
//clk_disable_unprepare(chip->cfgr_clk);
|
||||
|
||||
|
||||
dev_err(dev, "%s, %d\n", __func__, __LINE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused axi_dma_sleep_resume(struct device *dev)
|
||||
{
|
||||
struct axi_dma_chip *chip = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = clk_prepare_enable(chip->cfgr_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(chip->core_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
axi_dma_enable(chip);
|
||||
axi_dma_irq_enable(chip);
|
||||
dev_err(dev, "%s, %d\n", __func__, __LINE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
@@ -1521,9 +1614,16 @@ static int dw_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct dev_pm_ops dw_axi_dma_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(axi_dma_sleep_suspend, axi_dma_sleep_resume)
|
||||
SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
|
||||
};
|
||||
#else
|
||||
static const struct dev_pm_ops dw_axi_dma_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct of_device_id dw_dma_of_id_table[] = {
|
||||
{ .compatible = "snps,axi-dma-1.01a" },
|
||||
|
||||
@@ -51,6 +51,15 @@ struct axi_dma_chan {
|
||||
bool cyclic;
|
||||
/* these other elements are all protected by vc.lock */
|
||||
bool is_paused;
|
||||
u32 ch_sar;
|
||||
u32 ch_dar;
|
||||
u32 ch_dar_h;
|
||||
u32 ch_block_ts;
|
||||
u32 ch_ctl_l;
|
||||
u32 ch_ctl_h;
|
||||
u32 ch_cfg_l;
|
||||
u32 ch_cfg_h;
|
||||
u32 ch_llp;
|
||||
};
|
||||
|
||||
struct dw_axi_dma {
|
||||
@@ -153,6 +162,7 @@ static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
|
||||
/* DMA channel registers offset */
|
||||
#define CH_SAR 0x000 /* R/W Chan Source Address */
|
||||
#define CH_DAR 0x008 /* R/W Chan Destination Address */
|
||||
#define CH_DAR_H 0x00C
|
||||
#define CH_BLOCK_TS 0x010 /* R/W Chan Block Transfer Size */
|
||||
#define CH_CTL 0x018 /* R/W Chan Control */
|
||||
#define CH_CTL_L 0x018 /* R/W Chan Control 00-31 */
|
||||
|
||||
@@ -230,10 +230,26 @@ static const struct of_device_id light_aon_match[] = {
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
|
||||
static int __maybe_unused light_aon_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct light_aon_chan *aon_chan;
|
||||
int ret;
|
||||
|
||||
aon_chan = &light_aon_ipc_handle->chans;
|
||||
|
||||
complete(&aon_chan->tx_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops light_aon_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL,
|
||||
light_aon_resume_noirq)
|
||||
};
|
||||
static struct platform_driver light_aon_driver = {
|
||||
.driver = {
|
||||
.name = "light-aon",
|
||||
.of_match_table = light_aon_match,
|
||||
.pm = &light_aon_pm_ops,
|
||||
},
|
||||
.probe = light_aon_probe,
|
||||
};
|
||||
|
||||
@@ -1242,12 +1242,20 @@ static const struct of_device_id pca953x_dt_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pca953x_pm_ops, pca953x_suspend, pca953x_resume);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static const struct dev_pm_ops pca953x_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pca953x_suspend,
|
||||
pca953x_resume)
|
||||
};
|
||||
|
||||
#define PCA593X_PM_OPS &pca953x_pm_ops
|
||||
#else
|
||||
#define PCA593X_PM_OPS NULL
|
||||
#endif
|
||||
static struct i2c_driver pca953x_driver = {
|
||||
.driver = {
|
||||
.name = "pca953x",
|
||||
.pm = &pca953x_pm_ops,
|
||||
.pm = PCA593X_PM_OPS,
|
||||
.of_match_table = pca953x_dt_ids,
|
||||
.acpi_match_table = pca953x_acpi_ids,
|
||||
},
|
||||
|
||||
@@ -1380,6 +1380,7 @@ static int gc_poweroff_timeout_show(void* m, void* data)
|
||||
gckGALDEVICE device = galDevice;
|
||||
gckHARDWARE hardware;
|
||||
int len = 0;
|
||||
int i;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void* ptr = m;
|
||||
#else
|
||||
@@ -1389,14 +1390,19 @@ static int gc_poweroff_timeout_show(void* m, void* data)
|
||||
if (!device)
|
||||
return -ENXIO;
|
||||
|
||||
hardware = device->kernels[0]->hardware;
|
||||
|
||||
for (i = 0; i < gcvCORE_COUNT; ++i)
|
||||
{
|
||||
if (!device->kernels[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
hardware = device->kernels[i]->hardware;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
len += fs_printf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout);
|
||||
len += fs_printf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout);
|
||||
#else
|
||||
len += sprintf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout);
|
||||
len += sprintf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout);
|
||||
#endif
|
||||
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <media/cec-notifier.h>
|
||||
|
||||
#include <uapi/linux/media-bus-format.h>
|
||||
@@ -3399,13 +3398,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||||
goto err_res;
|
||||
}
|
||||
|
||||
hdmi->i2s_clk = devm_clk_get_optional(hdmi->dev, "i2s");
|
||||
if (IS_ERR(hdmi->i2s_clk)) {
|
||||
ret = PTR_ERR(hdmi->i2s_clk);
|
||||
dev_err(hdmi->dev, "Unable to get HDMI i2s clk: %d\n", ret);
|
||||
goto err_res;
|
||||
}
|
||||
|
||||
clk_prepare_enable(hdmi->iahb_clk);
|
||||
clk_prepare_enable(hdmi->isfr_clk);
|
||||
|
||||
@@ -3618,16 +3610,15 @@ EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
|
||||
void dw_hdmi_resume(struct dw_hdmi *hdmi)
|
||||
{
|
||||
dw_hdmi_init_hw(hdmi);
|
||||
hdmi_init_clk_regenerator(hdmi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_resume);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int dw_hdmi_runtime_suspend(struct dw_hdmi *hdmi)
|
||||
{
|
||||
clk_disable_unprepare(hdmi->i2s_clk);
|
||||
clk_disable_unprepare(hdmi->pix_clk);
|
||||
clk_disable_unprepare(hdmi->cec_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_runtime_suspend);
|
||||
@@ -3636,13 +3627,12 @@ int dw_hdmi_runtime_resume(struct dw_hdmi *hdmi)
|
||||
{
|
||||
clk_prepare_enable(hdmi->cec_clk);
|
||||
clk_prepare_enable(hdmi->pix_clk);
|
||||
clk_prepare_enable(hdmi->i2s_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_runtime_resume);
|
||||
#endif
|
||||
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
|
||||
MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
|
||||
MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
|
||||
|
||||
@@ -538,4 +538,14 @@ config DRM_PANEL_MINGJUN_070BI30IA2
|
||||
Say Y here if you want to enable support for MingJun 070BI30IA2
|
||||
MIPI DSI panel. The panel support TFT dot matrix LCD with
|
||||
800RGBx1280 dots at maximum.
|
||||
|
||||
config DRM_PANEL_HX8279
|
||||
tristate "HX8279-based panels"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y if you want to enable support for panels based on the
|
||||
HX8279 controller.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -57,3 +57,4 @@ obj-$(CONFIG_DRM_PANEL_ILI9881D) += panel-ili9881d.o
|
||||
obj-$(CONFIG_DRM_PANEL_HX8394) += panel-himax8394.o
|
||||
obj-$(CONFIG_DRM_PANEL_JADARD_JD9365DA_H3) += panel-jadard-jd9365da-h3.o
|
||||
obj-$(CONFIG_DRM_PANEL_MINGJUN_070BI30IA2) += panel-mingjun-070bi30ia2.o
|
||||
obj-$(CONFIG_DRM_PANEL_HX8279) += panel-hx8279.o
|
||||
|
||||
328
drivers/gpu/drm/panel/panel-hx8279.c
Normal file
328
drivers/gpu/drm/panel/panel-hx8279.c
Normal file
@@ -0,0 +1,328 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
struct hx8279_panel_desc {
|
||||
const struct drm_display_mode *display_mode;
|
||||
|
||||
unsigned long mode_flags;
|
||||
enum mipi_dsi_pixel_format format;
|
||||
unsigned int lanes;
|
||||
// const struct jadard_panel_cmd *on_cmds;
|
||||
// unsigned int on_cmds_num;
|
||||
};
|
||||
|
||||
struct panel_info {
|
||||
struct drm_panel base;
|
||||
struct mipi_dsi_device *link;
|
||||
const struct hx8279_panel_desc *desc;
|
||||
|
||||
struct gpio_desc *reset;
|
||||
struct regulator *hsvcc;
|
||||
struct regulator *vspn3v3;
|
||||
|
||||
bool prepared;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline struct panel_info *to_panel_info(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct panel_info, base);
|
||||
}
|
||||
|
||||
static int hx8279_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_info *pinfo = to_panel_info(panel);
|
||||
int err;
|
||||
|
||||
if (!pinfo->enabled)
|
||||
return 0;
|
||||
|
||||
err = mipi_dsi_dcs_set_display_off(pinfo->link);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to set display off: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
pinfo->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx8279_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_info *pinfo = to_panel_info(panel);
|
||||
int err;
|
||||
|
||||
if (!pinfo->prepared)
|
||||
return 0;
|
||||
|
||||
err = mipi_dsi_dcs_set_display_off(pinfo->link);
|
||||
if (err < 0)
|
||||
dev_err(panel->dev, "failed to set display off: %d\n", err);
|
||||
|
||||
err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
|
||||
if (err < 0)
|
||||
dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
|
||||
|
||||
/* sleep_mode_delay: 1ms - 2ms */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
gpiod_set_value(pinfo->reset, 1);
|
||||
regulator_disable(pinfo->hsvcc);
|
||||
regulator_disable(pinfo->vspn3v3);
|
||||
|
||||
pinfo->prepared = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx8279_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_info *pinfo = to_panel_info(panel);
|
||||
int ret;
|
||||
|
||||
if (pinfo->prepared)
|
||||
return 0;
|
||||
gpiod_set_value(pinfo->reset, 0);
|
||||
|
||||
/* Power the panel */
|
||||
ret = regulator_enable(pinfo->hsvcc);
|
||||
if (ret) {
|
||||
dev_err(pinfo->base.dev, "Failed to enable hsvcc supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
ret = regulator_enable(pinfo->vspn3v3);
|
||||
if (ret) {
|
||||
dev_err(pinfo->base.dev, "Failed to enable vspn3v3 supply: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
gpiod_set_value(pinfo->reset, 1);
|
||||
msleep(180);
|
||||
|
||||
pinfo->prepared = true;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gpiod_set_value(pinfo->reset, 1);
|
||||
regulator_disable(pinfo->hsvcc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hx8279_panel_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_info *pinfo = to_panel_info(panel);
|
||||
int ret;
|
||||
u8 id1;
|
||||
|
||||
if (pinfo->enabled)
|
||||
return 0;
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
|
||||
if (ret < 0) {
|
||||
dev_err(panel->dev, "failed to exit sleep mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(120);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(pinfo->link);
|
||||
if (ret < 0) {
|
||||
dev_err(panel->dev, "failed to set display on: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pinfo->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx8279_panel_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct panel_info *pinfo = to_panel_info(panel);
|
||||
const struct drm_display_mode *m = pinfo->desc->display_mode;
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, m);
|
||||
if (!mode) {
|
||||
dev_err(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
|
||||
m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
connector->display_info.width_mm = mode->width_mm;
|
||||
connector->display_info.height_mm = mode->height_mm;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs panel_funcs = {
|
||||
.disable = hx8279_panel_disable,
|
||||
.unprepare = hx8279_panel_unprepare,
|
||||
.prepare = hx8279_panel_prepare,
|
||||
.enable = hx8279_panel_enable,
|
||||
.get_modes = hx8279_panel_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode hx8279_default_mode = {
|
||||
.clock = 196500,
|
||||
.hdisplay = 1200,
|
||||
.hsync_start = 1200 + 96,
|
||||
.hsync_end = 1200 + 96 + 128,
|
||||
.htotal = 1200 + 96 + 128 + 224,
|
||||
|
||||
.vdisplay = 1920,
|
||||
.vsync_start = 1920 + 3,
|
||||
.vsync_end = 1920 + 3 + 10,
|
||||
.vtotal = 1920 + 3 + 10 + 56,
|
||||
|
||||
.width_mm = 62,
|
||||
.height_mm = 110,
|
||||
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
|
||||
};
|
||||
|
||||
static const struct hx8279_panel_desc hx8279_desc = {
|
||||
.display_mode = &hx8279_default_mode,
|
||||
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_BURST,
|
||||
.format = MIPI_DSI_FMT_RGB888,
|
||||
.lanes = 4,
|
||||
// .on_cmds = hx8279_on_cmds,
|
||||
// .on_cmds_num = ARRAY_SIZE(hx8279_on_cmds),
|
||||
};
|
||||
|
||||
static const struct of_device_id panel_of_match[] = {
|
||||
{
|
||||
.compatible = "himax,hx8279",
|
||||
.data = &hx8279_desc,
|
||||
},
|
||||
{
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, panel_of_match);
|
||||
|
||||
static int hx8279_panel_add(struct panel_info *pinfo)
|
||||
{
|
||||
struct device *dev = &pinfo->link->dev;
|
||||
int ret;
|
||||
|
||||
pinfo->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pinfo->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(pinfo->reset),
|
||||
"Couldn't get our reset GPIO\n");
|
||||
|
||||
pinfo->hsvcc = devm_regulator_get(dev, "hsvcc");
|
||||
if (IS_ERR(pinfo->hsvcc))
|
||||
return dev_err_probe(dev, PTR_ERR(pinfo->hsvcc),
|
||||
"Failed to request hsvcc regulator\n");
|
||||
|
||||
pinfo->vspn3v3 = devm_regulator_get(dev, "vspn3v3");
|
||||
if (IS_ERR(pinfo->vspn3v3))
|
||||
return dev_err_probe(dev, PTR_ERR(pinfo->vspn3v3),
|
||||
"Failed to request vspn3v3 regulator\n");
|
||||
|
||||
drm_panel_init(&pinfo->base, dev, &panel_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
|
||||
ret = drm_panel_of_backlight(&pinfo->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_panel_add(&pinfo->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx8279_panel_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct panel_info *pinfo;
|
||||
const struct hx8279_panel_desc *desc;
|
||||
int err;
|
||||
|
||||
pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
desc = of_device_get_match_data(&dsi->dev);
|
||||
dsi->mode_flags = desc->mode_flags;
|
||||
dsi->format = desc->format;
|
||||
dsi->lanes = desc->lanes;
|
||||
pinfo->desc = desc;
|
||||
|
||||
pinfo->link = dsi;
|
||||
mipi_dsi_set_drvdata(dsi, pinfo);
|
||||
|
||||
err = hx8279_panel_add(pinfo);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = mipi_dsi_attach(dsi);
|
||||
if (err < 0)
|
||||
drm_panel_remove(&pinfo->base);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hx8279_panel_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
|
||||
int err;
|
||||
|
||||
err = hx8279_panel_disable(&pinfo->base);
|
||||
if (err < 0)
|
||||
dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
|
||||
|
||||
err = hx8279_panel_unprepare(&pinfo->base);
|
||||
if (err < 0)
|
||||
dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
|
||||
|
||||
err = mipi_dsi_detach(dsi);
|
||||
if (err < 0)
|
||||
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
|
||||
|
||||
drm_panel_remove(&pinfo->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hx8279_panel_shutdown(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
hx8279_panel_disable(&pinfo->base);
|
||||
hx8279_panel_unprepare(&pinfo->base);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_driver panel_driver = {
|
||||
.driver = {
|
||||
.name = "panel-himax8279",
|
||||
.of_match_table = panel_of_match,
|
||||
},
|
||||
.probe = hx8279_panel_probe,
|
||||
.remove = hx8279_panel_remove,
|
||||
.shutdown = hx8279_panel_shutdown,
|
||||
};
|
||||
module_mipi_dsi_driver(panel_driver);
|
||||
@@ -30,7 +30,7 @@ struct jadard_panel_desc {
|
||||
unsigned long mode_flags;
|
||||
enum mipi_dsi_pixel_format format;
|
||||
unsigned int lanes;
|
||||
// const struct jadard_panel_cmd *on_cmds;
|
||||
//const struct jadard_panel_cmd *on_cmds;
|
||||
unsigned int on_cmds_num;
|
||||
};
|
||||
|
||||
@@ -52,20 +52,20 @@ static inline struct panel_info *to_panel_info(struct drm_panel *panel)
|
||||
return container_of(panel, struct panel_info, base);
|
||||
}
|
||||
|
||||
//static int jadard_send_mipi_cmds(struct drm_panel *panel, const struct jadard_panel_cmd *cmds)
|
||||
//{
|
||||
// struct panel_info *pinfo = to_panel_info(panel);
|
||||
// unsigned int i = 0;
|
||||
// int err;
|
||||
//
|
||||
// for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
|
||||
// err = mipi_dsi_dcs_write_buffer(pinfo->link, &(cmds[i].cmddata[0]), cmds[i].cmdlen);
|
||||
// if (err < 0)
|
||||
// return err;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
//}
|
||||
// static int jadard_send_mipi_cmds(struct drm_panel *panel, const struct jadard_panel_cmd *cmds)
|
||||
// {
|
||||
// struct panel_info *pinfo = to_panel_info(panel);
|
||||
// unsigned int i = 0;
|
||||
// int err;
|
||||
|
||||
// for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
|
||||
// err = mipi_dsi_dcs_write_buffer(pinfo->link, &(cmds[i].cmddata[0]), cmds[i].cmdlen);
|
||||
// if (err < 0)
|
||||
// return err;
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
static int jadard_disable(struct drm_panel *panel)
|
||||
{
|
||||
@@ -353,4 +353,4 @@ module_mipi_dsi_driver(jadard_driver);
|
||||
MODULE_AUTHOR("Jagan Teki <jagan@edgeble.ai>");
|
||||
MODULE_AUTHOR("Stephen Chen <stephen@radxa.com>");
|
||||
MODULE_DESCRIPTION("Jadard JD9365DA-H3 WXGA DSI panel");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -54,10 +54,20 @@ static const struct drm_encoder_helper_funcs dw_hdmi_light_encoder_helper_funcs
|
||||
.atomic_check = dw_hdmi_light_encoder_atomic_check,
|
||||
};
|
||||
|
||||
struct dw_hdmi_light_private dw_hdmi_priv_data = {
|
||||
.max_pixclock = 594000,
|
||||
.max_width = 0,
|
||||
.max_height = 0,
|
||||
};
|
||||
|
||||
static int dw_hdmi_light_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 max_pixclock = 0;
|
||||
u16 max_width = 0;
|
||||
u16 max_height = 0;
|
||||
int property_ret = 0;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
@@ -74,6 +84,30 @@ static int dw_hdmi_light_bind(struct device *dev, struct device *master,
|
||||
if (unlikely(!match))
|
||||
return -ENODEV;
|
||||
|
||||
property_ret = of_property_read_u32(np, "max_pixclock", &max_pixclock);
|
||||
if(property_ret == 0){
|
||||
printk(KERN_INFO "Hdmi max pixel clock = %d\n", max_pixclock);
|
||||
if(dw_hdmi_priv_data.max_pixclock > max_pixclock){
|
||||
dw_hdmi_priv_data.max_pixclock = max_pixclock;
|
||||
printk(KERN_INFO"Hdmi max pixel clock adjust to %d\n", max_pixclock);
|
||||
}
|
||||
else{
|
||||
printk(KERN_INFO"Hdmi max pixel clock not take effect\n");
|
||||
}
|
||||
}
|
||||
|
||||
property_ret = of_property_read_u16(np, "max_width", &max_width);
|
||||
if(property_ret == 0){
|
||||
dw_hdmi_priv_data.max_width = max_width;
|
||||
}
|
||||
|
||||
property_ret = of_property_read_u16(np, "max_height", &max_height);
|
||||
if(property_ret == 0){
|
||||
dw_hdmi_priv_data.max_height = max_height;
|
||||
}
|
||||
|
||||
light_hdmi_drv_data.priv_data = (void*)&dw_hdmi_priv_data;
|
||||
|
||||
plat_data = match->data;
|
||||
hdmi->dev = &pdev->dev;
|
||||
encoder = &hdmi->encoder;
|
||||
@@ -90,7 +124,6 @@ static int dw_hdmi_light_bind(struct device *dev, struct device *master,
|
||||
ret = PTR_ERR(hdmi->dw_hdmi);
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return ret;
|
||||
@@ -114,7 +147,6 @@ static const struct component_ops dw_hdmi_light_ops = {
|
||||
static int dw_hdmi_light_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct light_hdmi *hdmi;
|
||||
|
||||
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
||||
if (!hdmi)
|
||||
return -ENOMEM;
|
||||
@@ -146,11 +178,22 @@ static int hdmi_runtime_resume(struct device *dev)
|
||||
return dw_hdmi_runtime_resume(hdmi->dw_hdmi);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int hdmi_resume(struct device *dev)
|
||||
{
|
||||
struct light_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
dev_info(dev,"hdmi resume\n");
|
||||
dw_hdmi_resume(hdmi->dw_hdmi);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static const struct dev_pm_ops dw_hdmi_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,hdmi_resume)
|
||||
#endif
|
||||
};
|
||||
|
||||
struct platform_driver dw_hdmi_light_platform_driver = {
|
||||
|
||||
@@ -126,6 +126,12 @@ struct dw_hdmi_mpll_gen_config{
|
||||
} voltage;
|
||||
};
|
||||
|
||||
struct dw_hdmi_light_private {
|
||||
u32 max_pixclock;
|
||||
u16 max_width;
|
||||
u16 max_height;
|
||||
};
|
||||
|
||||
static const struct dw_hdmi_mpll_gen_config mpll_configs[] = {
|
||||
{
|
||||
.pixelclock = 25175,
|
||||
@@ -587,13 +593,36 @@ dw_hdmi_tx_phy_gen2_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
|
||||
{
|
||||
int i;
|
||||
|
||||
if (mode->clock < 13500)
|
||||
return MODE_CLOCK_LOW;
|
||||
else if (mode->clock > 594000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
u32 max_clock = 594000;
|
||||
u16 max_height = 0;
|
||||
u16 max_width = 0;
|
||||
|
||||
struct dw_hdmi_light_private *dw_hdmi_private = (struct dw_hdmi_light_private*)data;
|
||||
if(dw_hdmi_private != NULL){
|
||||
max_clock = dw_hdmi_private->max_pixclock;
|
||||
max_height = dw_hdmi_private->max_height;
|
||||
max_width = dw_hdmi_private->max_width;
|
||||
}
|
||||
|
||||
if (mode->clock < 13500)
|
||||
{
|
||||
return MODE_CLOCK_LOW;
|
||||
}
|
||||
else if (mode->clock > max_clock)
|
||||
{
|
||||
return MODE_CLOCK_HIGH;
|
||||
}
|
||||
|
||||
if((max_width > 0) && (mode->hdisplay > max_width)){
|
||||
return MODE_H_ILLEGAL;
|
||||
}
|
||||
|
||||
if((max_height > 0) && (mode->vdisplay > max_height)){
|
||||
return MODE_V_ILLEGAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
|
||||
if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
|
||||
if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
|
||||
return MODE_OK;
|
||||
else if (mode->clock < mpll_configs[i].pixelclock)
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
@@ -345,7 +347,7 @@ static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc)
|
||||
display.enable = true;
|
||||
|
||||
if (dc->pix_clk_rate[display.id] != mode->clock) {
|
||||
clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
|
||||
clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
|
||||
dc->pix_clk_rate[display.id] = mode->clock;
|
||||
}
|
||||
|
||||
@@ -970,7 +972,6 @@ static int dc_bind(struct device *dev, struct device *master, void *data)
|
||||
struct vs_plane_info *plane_info;
|
||||
int i, ret;
|
||||
u32 ctrc_mask = 0;
|
||||
|
||||
if (!drm_dev || !dc) {
|
||||
dev_err(dev, "devices are not created.\n");
|
||||
return -ENODEV;
|
||||
@@ -1081,7 +1082,6 @@ static int dc_bind(struct device *dev, struct device *master, void *data)
|
||||
}
|
||||
|
||||
dc->funcs = &dc_funcs;
|
||||
|
||||
vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
|
||||
return 0;
|
||||
|
||||
@@ -1156,6 +1156,7 @@ static int dc_probe(struct platform_device *pdev)
|
||||
struct vs_dc *dc;
|
||||
int irq, ret, i;
|
||||
char pixclk[16];
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
|
||||
if (!dc)
|
||||
@@ -1175,6 +1176,13 @@ static int dc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(dc->hw.mmu_base);
|
||||
#endif
|
||||
|
||||
dc->hw.vosys_regmap = syscon_regmap_lookup_by_phandle(np, "vosys-regmap");
|
||||
if (IS_ERR(dc->hw.vosys_regmap))
|
||||
{
|
||||
dev_err(dev, "Failed to remap vosys\n");
|
||||
return PTR_ERR(dc->hw.vosys_regmap);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
|
||||
if (ret < 0) {
|
||||
@@ -1189,20 +1197,20 @@ static int dc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
for (i = 0; i < DC_DISPLAY_NUM; i++) {
|
||||
snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
|
||||
dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
|
||||
snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
|
||||
dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
|
||||
|
||||
if (IS_ERR(dc->pix_clk[i])) {
|
||||
dev_err(dev, "failed to get pix_clk %d source\n", i);
|
||||
return PTR_ERR(dc->pix_clk[i]);
|
||||
}
|
||||
if (IS_ERR(dc->pix_clk[i])) {
|
||||
dev_err(dev, "failed to get pix_clk %d source\n", i);
|
||||
return PTR_ERR(dc->pix_clk[i]);
|
||||
}
|
||||
|
||||
snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
|
||||
dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
|
||||
if (IS_ERR(dc->pixclk[i])) {
|
||||
dev_err(dev, "failed to get pixclk %d source\n", i);
|
||||
return PTR_ERR(dc->pixclk[i]);
|
||||
}
|
||||
snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
|
||||
dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
|
||||
if (IS_ERR(dc->pixclk[i])) {
|
||||
dev_err(dev, "failed to get pixclk %d source\n", i);
|
||||
return PTR_ERR(dc->pixclk[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
|
||||
@@ -1229,6 +1237,9 @@ static int dc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(dc->dpu1pll_clk);
|
||||
}
|
||||
|
||||
dc->def_pix_clk_rate[0] = clk_get_rate(dc->dpu0pll_clk)/1000;
|
||||
dc->def_pix_clk_rate[1] = clk_get_rate(dc->dpu1pll_clk)/1000;
|
||||
|
||||
dc_get_display_pll(dev, dc);
|
||||
|
||||
dev_set_drvdata(dev, dc);
|
||||
@@ -1259,8 +1270,10 @@ static int dc_runtime_suspend(struct device *dev)
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
clk_disable_unprepare(dc->axi_clk);
|
||||
|
||||
for (i = 0; i < DC_DISPLAY_NUM; i++)
|
||||
for (i = 0; i < DC_DISPLAY_NUM; i++){
|
||||
clk_disable_unprepare(dc->pix_clk[i]);
|
||||
clk_disable_unprepare(dc->pixclk[i]);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(dc->core_clk);
|
||||
|
||||
@@ -1299,11 +1312,16 @@ static int dc_runtime_resume(struct device *dev)
|
||||
|
||||
for (i = 0; i < DC_DISPLAY_NUM; i++) {
|
||||
ret = clk_prepare_enable(dc->pix_clk[i]);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to prepare/enable pix_clk %d\n", i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dc->pixclk[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to prepare/enable pixclk %d\n", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dc->axi_clk);
|
||||
@@ -1315,11 +1333,42 @@ static int dc_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int dc_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct vs_dc *dc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < DC_DISPLAY_NUM; i++){
|
||||
dc->pix_clk_rate[i] = dc->def_pix_clk_rate[i];
|
||||
clk_set_rate(dc->pixclk[i], dc->pix_clk_rate[i] * 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dc_resume(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
struct vs_dc *dc = dev_get_drvdata(dev);
|
||||
dev_info(dev,"dc resume\n");
|
||||
regmap_write(dc->hw.vosys_regmap,0x4,0x7); //release dpu reset
|
||||
|
||||
ret = dc_hw_init(&dc->hw);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to init DC HW\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
static const struct dev_pm_ops dc_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume)
|
||||
#endif
|
||||
};
|
||||
|
||||
struct platform_driver dc_platform_driver = {
|
||||
|
||||
@@ -48,6 +48,7 @@ struct vs_dc {
|
||||
struct clk *dpu1pll_clk;
|
||||
|
||||
unsigned int pix_clk_rate[DC_DISPLAY_NUM]; /* in KHz */
|
||||
unsigned int def_pix_clk_rate[DC_DISPLAY_NUM];
|
||||
|
||||
bool first_frame;
|
||||
bool dpu0pll_on;
|
||||
|
||||
@@ -540,6 +540,7 @@ struct dc_hw {
|
||||
struct dc_hw_qos qos;
|
||||
struct dc_hw_funcs *func;
|
||||
struct vs_dc_info *info;
|
||||
struct regmap *vosys_regmap;
|
||||
};
|
||||
|
||||
int dc_hw_init(struct dc_hw *hw);
|
||||
|
||||
@@ -220,7 +220,6 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
|
||||
n &= SAMPLE_DATA_MSK;
|
||||
/* Convert the N bitstream count into voltage */
|
||||
*val = (PVT_N_CONST * n - PVT_R_CONST) >> PVT_CONV_BITS;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
@@ -654,6 +653,8 @@ static int mr75203_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pvt);
|
||||
|
||||
pvt_chip_info.info = pvt_info;
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, "pvt",
|
||||
pvt,
|
||||
@@ -662,6 +663,31 @@ static int mr75203_probe(struct platform_device *pdev)
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int mr75203_suspend(struct device *dev)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mr75203_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pvt_device *pvt = platform_get_drvdata(pdev);
|
||||
pvt_init(pvt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mr75203_dev_pm_ops = {
|
||||
.suspend = mr75203_suspend,
|
||||
.resume = mr75203_resume,
|
||||
};
|
||||
|
||||
#define MR75203_DEV_PM_OPS (&mr75203_dev_pm_ops)
|
||||
#else
|
||||
#define MR75203_DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct of_device_id moortec_pvt_of_match[] = {
|
||||
{ .compatible = "moortec,mr75203" },
|
||||
@@ -672,6 +698,7 @@ MODULE_DEVICE_TABLE(of, moortec_pvt_of_match);
|
||||
static struct platform_driver moortec_pvt_driver = {
|
||||
.driver = {
|
||||
.name = "moortec-pvt",
|
||||
.pm = MR75203_DEV_PM_OPS,
|
||||
.of_match_table = moortec_pvt_of_match,
|
||||
},
|
||||
.probe = mr75203_probe,
|
||||
|
||||
@@ -202,7 +202,6 @@ int i2c_dw_dma_tx_transfer(struct dw_i2c_dev *dev, unsigned int timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_dw_dma *dma = &dev->dma;
|
||||
unsigned long start_jiffies = 0;
|
||||
u32 stat;
|
||||
|
||||
__dev_vdgb(dev->dev, "%s, %d, enter\n", __func__, __LINE__);
|
||||
|
||||
@@ -206,6 +206,7 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
|
||||
|
||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "going to probe light i2c driver\n");
|
||||
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct i2c_adapter *adap;
|
||||
struct dw_i2c_dev *dev;
|
||||
@@ -377,6 +378,7 @@ static void dw_i2c_plat_complete(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
dev_info(dev, "light i2c suspend\n");
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i_dev->suspended = true;
|
||||
@@ -391,6 +393,35 @@ static int dw_i2c_plat_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_resume(struct device *dev)
|
||||
{
|
||||
dev_info(dev, "light i2c resume\n");
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!i_dev->shared_with_punit)
|
||||
i2c_dw_prepare_clk(i_dev, true);
|
||||
|
||||
i_dev->init(i_dev);
|
||||
i_dev->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i_dev->suspended = true;
|
||||
|
||||
if (i_dev->shared_with_punit)
|
||||
return 0;
|
||||
|
||||
i_dev->disable(i_dev);
|
||||
i2c_dw_prepare_clk(i_dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
@@ -407,7 +438,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||
.prepare = dw_i2c_plat_prepare,
|
||||
.complete = dw_i2c_plat_complete,
|
||||
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)
|
||||
SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
/*
|
||||
@@ -65,6 +66,7 @@ struct plic_priv {
|
||||
struct irq_domain *irqdomain;
|
||||
void __iomem *regs;
|
||||
unsigned int nr_irqs;
|
||||
unsigned long *prio_save;
|
||||
};
|
||||
|
||||
struct plic_handler {
|
||||
@@ -76,8 +78,10 @@ struct plic_handler {
|
||||
*/
|
||||
raw_spinlock_t enable_lock;
|
||||
void __iomem *enable_base;
|
||||
u32 *enable_save;
|
||||
struct plic_priv *priv;
|
||||
};
|
||||
|
||||
static int plic_parent_irq;
|
||||
static bool plic_cpuhp_setup_done;
|
||||
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
|
||||
@@ -183,6 +187,71 @@ static struct irq_chip plic_chip = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int plic_irq_suspend(void)
|
||||
{
|
||||
unsigned int i, cpu;
|
||||
u32 __iomem *reg;
|
||||
struct plic_priv *priv;
|
||||
|
||||
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
|
||||
|
||||
for (i = 0; i < priv->nr_irqs; i++)
|
||||
if (readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID))
|
||||
__set_bit(i, priv->prio_save);
|
||||
else
|
||||
__clear_bit(i, priv->prio_save);
|
||||
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
|
||||
if (!handler->present)
|
||||
continue;
|
||||
|
||||
raw_spin_lock(&handler->enable_lock);
|
||||
for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
|
||||
reg = handler->enable_base + i * sizeof(u32);
|
||||
handler->enable_save[i] = readl(reg);
|
||||
}
|
||||
raw_spin_unlock(&handler->enable_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void plic_irq_resume(void)
|
||||
{
|
||||
unsigned int i, index, cpu;
|
||||
u32 __iomem *reg;
|
||||
struct plic_priv *priv;
|
||||
|
||||
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
|
||||
|
||||
for (i = 0; i < priv->nr_irqs; i++) {
|
||||
index = BIT_WORD(i);
|
||||
writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0,
|
||||
priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID);
|
||||
}
|
||||
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
|
||||
if (!handler->present)
|
||||
continue;
|
||||
|
||||
raw_spin_lock(&handler->enable_lock);
|
||||
for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
|
||||
reg = handler->enable_base + i * sizeof(u32);
|
||||
writel(handler->enable_save[i], reg);
|
||||
}
|
||||
raw_spin_unlock(&handler->enable_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static struct syscore_ops plic_irq_syscore_ops = {
|
||||
.suspend = plic_irq_suspend,
|
||||
.resume = plic_irq_resume,
|
||||
};
|
||||
|
||||
static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
@@ -294,8 +363,10 @@ static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
int error = 0, nr_contexts, nr_handlers = 0, i;
|
||||
u32 nr_irqs;
|
||||
struct plic_priv *priv;
|
||||
struct plic_handler *handler;
|
||||
unsigned int cpu;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -308,19 +379,25 @@ static int __init plic_init(struct device_node *node,
|
||||
}
|
||||
|
||||
error = -EINVAL;
|
||||
of_property_read_u32(node, "riscv,ndev", &priv->nr_irqs);
|
||||
if (WARN_ON(!priv->nr_irqs))
|
||||
of_property_read_u32(node, "riscv,ndev", &nr_irqs);
|
||||
if (WARN_ON(!nr_irqs))
|
||||
goto out_iounmap;
|
||||
|
||||
priv->nr_irqs = nr_irqs;
|
||||
|
||||
priv->prio_save = bitmap_alloc(nr_irqs, GFP_KERNEL);
|
||||
if (!priv->prio_save)
|
||||
goto out_free_priority_reg;
|
||||
|
||||
nr_contexts = of_irq_count(node);
|
||||
if (WARN_ON(!nr_contexts))
|
||||
goto out_iounmap;
|
||||
goto out_free_priority_reg;
|
||||
|
||||
error = -ENOMEM;
|
||||
priv->irqdomain = irq_domain_add_linear(node, priv->nr_irqs + 1,
|
||||
priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
|
||||
&plic_irqdomain_ops, priv);
|
||||
if (WARN_ON(!priv->irqdomain))
|
||||
goto out_iounmap;
|
||||
goto out_free_priority_reg;
|
||||
|
||||
for (i = 0; i < nr_contexts; i++) {
|
||||
struct of_phandle_args parent;
|
||||
@@ -379,6 +456,11 @@ static int __init plic_init(struct device_node *node,
|
||||
handler->enable_base =
|
||||
priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
|
||||
handler->priv = priv;
|
||||
|
||||
handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
|
||||
sizeof(*handler->enable_save), GFP_KERNEL);
|
||||
if (!handler->enable_save)
|
||||
goto out_free_enable_reg;
|
||||
done:
|
||||
nr_handlers++;
|
||||
}
|
||||
@@ -395,10 +477,18 @@ done:
|
||||
plic_cpuhp_setup_done = true;
|
||||
}
|
||||
|
||||
register_syscore_ops(&plic_irq_syscore_ops);
|
||||
pr_info("%pOFP: mapped %d interrupts with %d handlers for"
|
||||
" %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
|
||||
return 0;
|
||||
|
||||
out_free_enable_reg:
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
kfree(handler->enable_save);
|
||||
}
|
||||
out_free_priority_reg:
|
||||
kfree(priv->prio_save);
|
||||
out_iounmap:
|
||||
iounmap(priv->regs);
|
||||
out_free_priv:
|
||||
|
||||
@@ -37,6 +37,13 @@
|
||||
|
||||
#define LIGHT_MBOX_ACK_MAGIC 0xdeadbeaf
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* store MBOX context across system-wide suspend/resume transitions */
|
||||
struct light_mbox_context{
|
||||
u32 intr_mask[LIGHT_MBOX_CHANS - 1];
|
||||
};
|
||||
|
||||
#endif
|
||||
enum light_mbox_chan_type {
|
||||
LIGHT_MBOX_TYPE_TXRX, /* Tx & Rx chan */
|
||||
LIGHT_MBOX_TYPE_DB, /* Tx & Rx doorbell */
|
||||
@@ -70,9 +77,12 @@ struct light_mbox_priv {
|
||||
struct mbox_controller mbox;
|
||||
struct mbox_chan mbox_chans[LIGHT_MBOX_CHANS];
|
||||
|
||||
struct light_mbox_con_priv con_priv[LIGHT_MBOX_CHANS];
|
||||
struct light_mbox_con_priv con_priv[LIGHT_MBOX_CHANS];
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct light_mbox_context *ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct light_mbox_priv *to_light_mbox_priv(struct mbox_controller *mbox)
|
||||
@@ -327,11 +337,17 @@ static const struct mbox_chan_ops light_mbox_ops = {
|
||||
.shutdown = light_mbox_shutdown,
|
||||
};
|
||||
|
||||
static void light_mbox_init_generic(struct light_mbox_priv *priv)
|
||||
static int light_mbox_init_generic(struct light_mbox_priv *priv)
|
||||
{
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
priv->ctx = devm_kzalloc(priv->dev, sizeof(*priv->ctx), GFP_KERNEL);
|
||||
if(!priv->ctx)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
/* Set default configuration */
|
||||
light_mbox_write(priv, 0xff, LIGHT_MBOX_CLR);
|
||||
light_mbox_write(priv, 0x0, LIGHT_MBOX_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mbox_chan *light_mbox_xlate(struct mbox_controller *mbox,
|
||||
@@ -472,7 +488,11 @@ static int light_mbox_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
light_mbox_init_generic(priv);
|
||||
ret = light_mbox_init_generic(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init mailbox context\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_mbox_controller_register(dev, &priv->mbox);
|
||||
}
|
||||
@@ -492,12 +512,75 @@ static const struct of_device_id light_mbox_dt_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, light_mbox_dt_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int __maybe_unused light_mbox_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct light_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
struct light_mbox_context *ctx = priv->ctx;
|
||||
u32 i;
|
||||
/*
|
||||
* ONLY interrupt mask bit should be stored and restores.
|
||||
* INFO data all assumed to be lost.
|
||||
*/
|
||||
for(i = 0 ; i < LIGHT_MBOX_CHANS; i++) {
|
||||
ctx->intr_mask[i] = ioread32(priv->local_icu[i] + LIGHT_MBOX_MASK);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused light_mbox_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct light_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
struct light_mbox_context *ctx = priv->ctx;
|
||||
u32 i;
|
||||
|
||||
for(i = 0 ; i < LIGHT_MBOX_CHANS; i++) {
|
||||
iowrite32(ctx->intr_mask[i], priv->local_icu[i] + LIGHT_MBOX_MASK);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static int __maybe_unused light_mbox_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct light_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused light_mbox_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct light_mbox_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops light_mbox_pm_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(light_mbox_suspend_noirq,
|
||||
light_mbox_resume_noirq)
|
||||
#endif
|
||||
SET_RUNTIME_PM_OPS(light_mbox_runtime_suspend,
|
||||
light_mbox_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
|
||||
static struct platform_driver light_mbox_driver = {
|
||||
.probe = light_mbox_probe,
|
||||
.remove = light_mbox_remove,
|
||||
.driver = {
|
||||
.name = "light_mbox",
|
||||
.of_match_table = light_mbox_dt_ids,
|
||||
.pm = &light_mbox_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(light_mbox_driver);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "stmmac_platform.h"
|
||||
|
||||
@@ -52,11 +53,14 @@ struct thead_dwmac_priv_data {
|
||||
phy_interface_t interface;
|
||||
struct clk *gmac_pll_clk;
|
||||
unsigned long gmac_pll_clk_freq;
|
||||
|
||||
struct clk *gmac_axi_aclk;
|
||||
struct clk *gmac_axi_pclk;
|
||||
const struct thead_dwmac_ops *ops;
|
||||
struct plat_stmmacenet_data *plat_dat;
|
||||
};
|
||||
|
||||
#define pm_debug dev_dbg // for suspend/resume interface debug info show,replace to dev_info
|
||||
|
||||
/* set GMAC PHY interface, 0:MII/GMII, 1:RGMII, 4:RMII */
|
||||
static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
|
||||
{
|
||||
@@ -64,7 +68,7 @@ static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
|
||||
void __iomem *phy_if_reg = thead_plat_dat->phy_if_reg;
|
||||
phy_interface_t interface = thead_plat_dat->interface;
|
||||
struct device *dev = thead_plat_dat->dev;
|
||||
int devid = thead_plat_dat->id;
|
||||
//int devid = thead_plat_dat->id;
|
||||
unsigned int phyif = PHY_INTERFACE_MODE_MII;
|
||||
uint32_t reg;
|
||||
|
||||
@@ -92,8 +96,10 @@ static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
|
||||
};
|
||||
|
||||
reg = readl(phy_if_reg);
|
||||
reg &= ~(DWMAC_PHYIF_MASK << (DWMAC_PHYIF_BIT_WIDTH * devid));
|
||||
reg |= (phyif & DWMAC_PHYIF_MASK) << (DWMAC_PHYIF_BIT_WIDTH * devid);
|
||||
//This reg defined bit not related to devid
|
||||
reg &= ~(DWMAC_PHYIF_MASK );
|
||||
reg |= (phyif & DWMAC_PHYIF_MASK) ;
|
||||
dev_info(dev,"set phy_if_reg val 0x%x \n",reg);
|
||||
writel(reg, phy_if_reg);
|
||||
}
|
||||
|
||||
@@ -428,13 +434,13 @@ static void thead_dwmac_light_enable_clk(struct plat_stmmacenet_data *plat_dat)
|
||||
static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
|
||||
{
|
||||
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
|
||||
struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
|
||||
//struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
void __iomem *ptr;
|
||||
struct clk *clktmp;
|
||||
int ret;
|
||||
//struct clk *clktmp;
|
||||
//int ret;
|
||||
|
||||
thead_plat_dat->id = of_alias_get_id(np, "ethernet");
|
||||
if (thead_plat_dat->id < 0) {
|
||||
@@ -472,7 +478,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
|
||||
} else {
|
||||
thead_plat_dat->gmac_clk_reg = ptr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* get gmac pll clk */
|
||||
clktmp = devm_clk_get(dev, "gmac_pll_clk");
|
||||
if (IS_ERR(clktmp)) {
|
||||
@@ -490,6 +496,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
|
||||
clk_get_rate(thead_plat_dat->gmac_pll_clk);
|
||||
}
|
||||
|
||||
|
||||
thead_dwmac_set_phy_if(plat_dat);
|
||||
thead_dwmac_set_txclk_dir(plat_dat);
|
||||
|
||||
@@ -506,7 +513,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
|
||||
|
||||
if (thead_plat_dat->ops->enable_clk)
|
||||
thead_plat_dat->ops->enable_clk(plat_dat);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -577,6 +584,97 @@ static int dwmac1000_validate_ucast_entries(int ucast_entries)
|
||||
}
|
||||
return x;
|
||||
}
|
||||
static void __maybe_unused thead_dwmac_dump_priv_reg(struct platform_device *pdev, void *bsp_priv)
|
||||
{
|
||||
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
|
||||
unsigned int reg = 0;
|
||||
int i;
|
||||
dev_info(dev,"dump gmac_clk_reg %p\n",gmac_clk_reg);
|
||||
if(gmac_clk_reg == NULL)
|
||||
return ;
|
||||
for(i=0; i< 0x1c; i+=4)
|
||||
{
|
||||
reg = readl(gmac_clk_reg + GMAC_CLK_CFG0+i);
|
||||
pr_info("%08x ",reg);
|
||||
}
|
||||
pr_info("\n");
|
||||
reg = readl(thead_plat_dat->phy_if_reg);
|
||||
pr_info("phy_if_reg %08x ",reg);
|
||||
reg = readl(thead_plat_dat->txclk_dir_reg);
|
||||
pr_info("txclk_dir_reg %08x ",reg);
|
||||
}
|
||||
|
||||
int thead_dwmac_clk_enable(struct platform_device *pdev, void *bsp_priv)
|
||||
{
|
||||
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n");
|
||||
return -1;
|
||||
}
|
||||
ret = clk_prepare_enable(thead_plat_dat->gmac_axi_aclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
|
||||
dev_err(dev, "Failed to enable clk 'gmac_axi_aclk'\n");
|
||||
return -1;
|
||||
}
|
||||
ret = clk_prepare_enable(thead_plat_dat->gmac_axi_pclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
|
||||
dev_err(dev, "Failed to enable clk 'gmac_axi_pclk'\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int thead_dwmac_clk_init(struct platform_device *pdev, void *bsp_priv)
|
||||
{
|
||||
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
|
||||
int ret = 0;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
|
||||
thead_dwmac_set_phy_if(plat_dat);
|
||||
thead_dwmac_set_txclk_dir(plat_dat);
|
||||
|
||||
if (thead_plat_dat->ops->set_clk_source)
|
||||
thead_plat_dat->ops->set_clk_source(plat_dat);
|
||||
|
||||
thead_dwmac_set_clock_delay(plat_dat);
|
||||
|
||||
if (thead_plat_dat->ops->set_clk_pll)
|
||||
thead_plat_dat->ops->set_clk_pll(plat_dat);
|
||||
|
||||
if (thead_plat_dat->ops->set_clk_div)
|
||||
thead_plat_dat->ops->set_clk_div(plat_dat, SPEED_1000);
|
||||
|
||||
if (thead_plat_dat->ops->enable_clk)
|
||||
thead_plat_dat->ops->enable_clk(plat_dat);
|
||||
|
||||
//thead_dwmac_dump_priv_reg(pdev,bsp_priv);
|
||||
return ret;
|
||||
}
|
||||
void thead_dwmac_clk_disable(struct platform_device *pdev, void *bsp_priv)
|
||||
{
|
||||
struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
//thead_dwmac_dump_priv_reg(pdev,bsp_priv);
|
||||
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
|
||||
clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
static int thead_dwmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
@@ -627,17 +725,13 @@ static int thead_dwmac_probe(struct platform_device *pdev)
|
||||
plat_dat->unicast_filter_entries = 1;
|
||||
}
|
||||
|
||||
/* Custom initialisation (if needed) */
|
||||
if (plat_dat->init) {
|
||||
ret = plat_dat->init(pdev, plat_dat->bsp_priv);
|
||||
if (ret)
|
||||
goto err_remove_config_dt;
|
||||
}
|
||||
|
||||
|
||||
/* populate bsp private data */
|
||||
thead_plat_dat->dev = &pdev->dev;
|
||||
plat_dat->bsp_priv = thead_plat_dat;
|
||||
plat_dat->fix_mac_speed = thead_dwmac_fix_speed;
|
||||
plat_dat->init = thead_dwmac_clk_init;
|
||||
of_property_read_u32(np, "max-frame-size", &plat_dat->maxmtu);
|
||||
of_property_read_u32(np, "snps,multicast-filter-bins",
|
||||
&plat_dat->multicast_filter_bins);
|
||||
@@ -651,9 +745,41 @@ static int thead_dwmac_probe(struct platform_device *pdev)
|
||||
plat_dat->pmt = 1;
|
||||
thead_plat_dat->plat_dat = plat_dat;
|
||||
|
||||
/* get gmac pll clk */
|
||||
thead_plat_dat->gmac_pll_clk = devm_clk_get(dev, "gmac_pll_clk");
|
||||
if (IS_ERR(thead_plat_dat->gmac_pll_clk)) {
|
||||
dev_err(dev, "gmac_pll_clk not exist, dts error\n");
|
||||
goto err_remove_config_dt;
|
||||
}
|
||||
|
||||
thead_plat_dat->gmac_axi_aclk = devm_clk_get(dev, "axi_aclk");
|
||||
if (IS_ERR(thead_plat_dat->gmac_axi_aclk)) {
|
||||
dev_err(dev, "gmac axi_aclk not exist, skipped it\n");
|
||||
}
|
||||
thead_plat_dat->gmac_axi_pclk = devm_clk_get(dev, "axi_pclk");
|
||||
if (IS_ERR(thead_plat_dat->gmac_axi_pclk)) {
|
||||
dev_err(dev, "gmac axi_pclk not exist, skipped it\n");
|
||||
}
|
||||
|
||||
|
||||
thead_plat_dat->gmac_pll_clk_freq =
|
||||
clk_get_rate(thead_plat_dat->gmac_pll_clk);
|
||||
dev_info(dev,"get_rate gmac_pll_clk_freq %ld \n",thead_plat_dat->gmac_pll_clk_freq);
|
||||
|
||||
ret = thead_dwmac_init(pdev, plat_dat->bsp_priv);
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
goto err_remove_config_dt;
|
||||
|
||||
ret = thead_dwmac_clk_enable(pdev, plat_dat->bsp_priv);
|
||||
if (ret)
|
||||
goto err_remove_config_dt;
|
||||
|
||||
/* Custom initialisation (if needed) -- init clks*/
|
||||
if (plat_dat->init) {
|
||||
ret = plat_dat->init(pdev, plat_dat->bsp_priv);
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
||||
if (ret)
|
||||
@@ -664,6 +790,7 @@ static int thead_dwmac_probe(struct platform_device *pdev)
|
||||
err_exit:
|
||||
if (plat_dat->exit)
|
||||
plat_dat->exit(pdev, plat_dat->bsp_priv);
|
||||
thead_dwmac_clk_disable(pdev, plat_dat->bsp_priv);
|
||||
err_remove_config_dt:
|
||||
if (pdev->dev.of_node)
|
||||
stmmac_remove_config_dt(pdev, plat_dat);
|
||||
@@ -671,6 +798,130 @@ err_remove_config_dt:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* stmmac_pltfr_suspend
|
||||
* @dev: device pointer
|
||||
* Description: this function is invoked when suspend the driver and it direcly
|
||||
* call the main suspend function and then, if required, on some platform, it
|
||||
* can call an exit helper.
|
||||
*/
|
||||
static int __maybe_unused thead_dwmac_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
ret = stmmac_suspend(dev);
|
||||
if (priv->plat->exit)
|
||||
priv->plat->exit(pdev, priv->plat->bsp_priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* thead_dwmac_resume
|
||||
* @dev: device pointer
|
||||
* Description: this function is invoked when resume the driver before calling
|
||||
* the main resume function, on some platforms, it can call own init helper
|
||||
* if required.
|
||||
*/
|
||||
static int __maybe_unused thead_dwmac_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
|
||||
if (priv->plat->init)
|
||||
priv->plat->init(pdev, priv->plat->bsp_priv);
|
||||
|
||||
return stmmac_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused thead_dwmac_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
stmmac_bus_clks_config(priv, false);
|
||||
thead_dwmac_clk_disable(pdev, priv->plat->bsp_priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused thead_dwmac_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
ret = stmmac_bus_clks_config(priv, true);
|
||||
if(ret)
|
||||
return ret;
|
||||
ret = thead_dwmac_clk_enable(pdev, priv->plat->bsp_priv);
|
||||
if(ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused thead_dwmac_noirq_suspend(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
int ret;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
if (!netif_running(ndev))
|
||||
return 0;
|
||||
|
||||
if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
|
||||
/* Disable clock in case of PWM is off */
|
||||
clk_disable_unprepare(priv->plat->clk_ptp_ref);
|
||||
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused thead_dwmac_noirq_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct stmmac_priv *priv = netdev_priv(ndev);
|
||||
int ret;
|
||||
pm_debug(dev,"enter %s()\n",__func__);
|
||||
if (!netif_running(ndev))
|
||||
return 0;
|
||||
|
||||
if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
|
||||
/* enable the clk previously disabled */
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
|
||||
if (ret < 0) {
|
||||
netdev_warn(priv->dev,
|
||||
"failed to enable PTP reference clock: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*similar with stmmac_pltfr_pm_ops,but clks enable/disable add this drv need */
|
||||
const struct dev_pm_ops thead_dwmac_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(thead_dwmac_suspend, thead_dwmac_resume)
|
||||
SET_RUNTIME_PM_OPS(thead_dwmac_runtime_suspend, thead_dwmac_runtime_resume, NULL)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(thead_dwmac_noirq_suspend, thead_dwmac_noirq_resume)
|
||||
};
|
||||
|
||||
|
||||
static struct thead_dwmac_ops thead_ice_dwmac_data = {
|
||||
.set_clk_source = thead_dwmac_ice_set_clk_source,
|
||||
.set_clk_pll = thead_dwmac_ice_set_pll,
|
||||
@@ -695,7 +946,7 @@ static struct platform_driver thead_dwmac_driver = {
|
||||
.remove = stmmac_pltfr_remove,
|
||||
.driver = {
|
||||
.name = "light_dwmac_eth",
|
||||
.pm = &stmmac_pltfr_pm_ops,
|
||||
.pm = &thead_dwmac_pm_ops,
|
||||
.of_match_table = of_match_ptr(thead_dwmac_match),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ int dwmac_dma_reset(void __iomem *ioaddr)
|
||||
|
||||
return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
|
||||
!(value & DMA_BUS_MODE_SFT_RESET),
|
||||
10000, 200000);
|
||||
10000, 1000000); //inspired from : net: stmmac: increase the timeout for dma reset
|
||||
}
|
||||
|
||||
/* CSR1 enables the transmit DMA to check for new descriptor */
|
||||
|
||||
@@ -738,19 +738,10 @@ int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
|
||||
struct timespec64 now;
|
||||
u32 sec_inc = 0;
|
||||
u64 temp = 0;
|
||||
int ret;
|
||||
|
||||
if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
|
||||
if (ret < 0) {
|
||||
netdev_warn(priv->dev,
|
||||
"failed to enable PTP reference clock: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
|
||||
priv->systime_flags = systime_flags;
|
||||
|
||||
@@ -2881,6 +2872,14 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
|
||||
|
||||
stmmac_mmc_setup(priv);
|
||||
|
||||
if (ptp_register) {
|
||||
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
|
||||
if (ret < 0)
|
||||
netdev_warn(priv->dev,
|
||||
"failed to enable PTP reference clock: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
}
|
||||
|
||||
ret = stmmac_init_ptp(priv);
|
||||
if (ret == -EOPNOTSUPP)
|
||||
netdev_warn(priv->dev, "PTP not supported by HW\n");
|
||||
|
||||
@@ -814,7 +814,13 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stmmac_init_tstamp_counter(priv, priv->systime_flags);
|
||||
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
|
||||
if (ret < 0) {
|
||||
netdev_warn(priv->dev,
|
||||
"failed to enable PTP reference clock: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1005,6 +1005,8 @@ static int __maybe_unused light_efuse_runtime_suspend(struct device *dev)
|
||||
|
||||
dev_dbg(dev, "[%s,%d] ret = %d, pd status: 0x%lx\n", __func__, __LINE__, ret,
|
||||
readl(priv->base + CON) & EFUSE_CON_POWER_MSK);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1012,9 +1014,59 @@ static int __maybe_unused light_efuse_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused light_efuse_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct light_efuse_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "[%s,%d]efuse runtime power on\n", __func__, __LINE__);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get efuse clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return efuse_poweron(priv->base);
|
||||
}
|
||||
|
||||
static int __maybe_unused light_efuse_suspend(struct device *dev)
|
||||
{
|
||||
struct light_efuse_priv *priv = dev_get_drvdata(dev);
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "[%s,%d]efuse suspend enter\n", __func__, __LINE__);
|
||||
|
||||
if (!efuse_poweron_status_get(priv->base))
|
||||
return 0;
|
||||
|
||||
data = readl(priv->base + CON);
|
||||
data |= EFUSE_CON_POWER_MSK;
|
||||
writel(data, priv->base + CON);
|
||||
|
||||
ret = efuse_idle_check(priv->base);
|
||||
|
||||
ret |= efuse_status_check(priv->base);
|
||||
|
||||
dev_dbg(dev, "[%s,%d] ret = %d, pd status: 0x%lx\n", __func__, __LINE__, ret,
|
||||
readl(priv->base + CON) & EFUSE_CON_POWER_MSK);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused light_efuse_resume(struct device *dev)
|
||||
{
|
||||
struct light_efuse_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "[%s,%d]efuse resume enter\n", __func__, __LINE__);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get efuse clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return efuse_poweron(priv->base);
|
||||
}
|
||||
|
||||
@@ -1035,9 +1087,18 @@ static int light_efuse_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
/* optional clock, default open */
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
priv->clk = NULL;
|
||||
priv->clk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR_OR_NULL(priv->clk)) {
|
||||
if (PTR_ERR(priv->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to get efuse clk\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get efuse clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->teesys_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "thead,teesys");
|
||||
if (IS_ERR(priv->teesys_regs)) {
|
||||
@@ -1049,6 +1110,14 @@ static int light_efuse_probe(struct platform_device *pdev)
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get the efuse device(%d)\n", ret);
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&dev->kobj, &dev_attr_efuse_sysfs_group);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create efuse debug sysfs\n");
|
||||
@@ -1070,7 +1139,7 @@ static int light_efuse_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(nvmem))
|
||||
return PTR_ERR_OR_ZERO(nvmem);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
dev_info(dev, "succeed to register light efuse driver\n");
|
||||
|
||||
@@ -1079,6 +1148,7 @@ static int light_efuse_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct dev_pm_ops efuse_runtime_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(light_efuse_runtime_suspend, light_efuse_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(light_efuse_suspend, light_efuse_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver light_efuse_driver = {
|
||||
|
||||
@@ -748,21 +748,35 @@ static int dw_dphy_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dw_dphy_resume(struct device *dev)
|
||||
{
|
||||
struct dw_dphy *dphy = dev_get_drvdata(dev);
|
||||
|
||||
dw_dphy_init(dphy->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_dphy_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_dphy_runtime_suspend, dw_dphy_runtime_resume, NULL)
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,
|
||||
dw_dphy_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_dphy_runtime_suspend, dw_dphy_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#define DW_DPHY_PM_OPS &dw_dphy_pm_ops
|
||||
#else
|
||||
#define DW_DPHY_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
|
||||
static struct platform_driver dw_dphy_driver = {
|
||||
.probe = dw_dphy_probe,
|
||||
.remove = dw_dphy_remove,
|
||||
.driver = {
|
||||
.name = "dw-mipi-dphy",
|
||||
.of_match_table = dw_dphy_of_match,
|
||||
.pm = &dw_dphy_pm_ops,
|
||||
.pm = DW_DPHY_PM_OPS,
|
||||
},
|
||||
};
|
||||
module_platform_driver(dw_dphy_driver);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2021 Alibaba Group Holding Limited.
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
@@ -67,10 +68,29 @@ struct light_pinctrl_soc_info {
|
||||
int (*covert_pin_off)(const unsigned int pin_id);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#define MAX_CFG_REG_NUMS 32
|
||||
#define MAX_MUX_REG_NUMS 8
|
||||
#define LIGHT_PADCTRL0_CFG_REG_NUMS 28
|
||||
#define LIGHT_PADCTRL0_MUX_REG_NUMS 7
|
||||
#define LIGHT_PADCTRL1_CFG_REG_NUMS 32
|
||||
#define LIGHT_PADCTRL1_MUX_REG_NUMS 8
|
||||
#define LIGHT_AUDIO_CFG_REG_NUMS 16
|
||||
#define LIGHT_AUDIO_MUX_REG_NUMS 2
|
||||
#define LIGHT_AUDIO_IO_SEL_IDX 2
|
||||
#define LIGHT_PM_PAD_CFG(idx) (priv->base + priv->info->cfg_off + idx * 4)
|
||||
#define LIGHT_PM_PAD_MUX(idx) (priv->base + priv->info->mux_off + idx * 4)
|
||||
#endif
|
||||
|
||||
struct light_pinctrl {
|
||||
struct device *dev;
|
||||
struct pinctrl_dev *pctl;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
unsigned int cfg_bak[MAX_CFG_REG_NUMS];
|
||||
unsigned int mux_bak[MAX_MUX_REG_NUMS];
|
||||
#endif
|
||||
const struct light_pinctrl_soc_info *info;
|
||||
};
|
||||
|
||||
@@ -598,6 +618,20 @@ static int light_pinctrl_probe(struct platform_device *pdev)
|
||||
|
||||
priv->info = info;
|
||||
priv->dev = info->dev;
|
||||
if ((priv->info->type == LIGHT_FM_LEFT) ||
|
||||
(priv->info->type == LIGHT_FM_RIGHT)) {
|
||||
priv->clk = devm_clk_get_optional(&pdev->dev, "pclk");
|
||||
if(priv->clk == NULL) {
|
||||
dev_err(&pdev->dev, "could not get padctrl clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not enable padctrl clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->pctl = pinctrl_register(info->desc, &pdev->dev, priv);
|
||||
if (!priv->pctl) {
|
||||
@@ -610,6 +644,98 @@ static int light_pinctrl_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int light_pinctrl_backup_regs(struct light_pinctrl *priv, unsigned int cfg_reg_nums,
|
||||
unsigned int mux_reg_nums)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg_reg_nums; i++)
|
||||
priv->cfg_bak[i] = readl(LIGHT_PM_PAD_CFG(i));
|
||||
for (i = 0; i < mux_reg_nums; i++)
|
||||
priv->mux_bak[i] = readl(LIGHT_PM_PAD_MUX(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_pinctrl_restore_regs(struct light_pinctrl *priv, unsigned int cfg_reg_nums,
|
||||
unsigned int mux_reg_nums)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg_reg_nums; i++)
|
||||
writel(priv->cfg_bak[i], LIGHT_PM_PAD_CFG(i));
|
||||
for (i = 0; i < mux_reg_nums; i++)
|
||||
writel(priv->mux_bak[i], LIGHT_PM_PAD_MUX(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_pinctrl_suspend(struct device *dev)
|
||||
{
|
||||
dev_info(dev, "light pinctrl suspend\n");
|
||||
struct light_pinctrl *priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
switch(priv->info->type) {
|
||||
case LIGHT_FM_RIGHT:
|
||||
ret = light_pinctrl_backup_regs(priv, LIGHT_PADCTRL0_CFG_REG_NUMS, LIGHT_PADCTRL0_MUX_REG_NUMS);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
break;
|
||||
case LIGHT_FM_LEFT:
|
||||
ret = light_pinctrl_backup_regs(priv, LIGHT_PADCTRL1_CFG_REG_NUMS, LIGHT_PADCTRL1_MUX_REG_NUMS);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
break;
|
||||
case LIGHT_FM_AON:
|
||||
break;
|
||||
case LIGHT_FM_AUDIO:
|
||||
ret = light_pinctrl_backup_regs(priv, LIGHT_AUDIO_CFG_REG_NUMS, LIGHT_AUDIO_MUX_REG_NUMS);
|
||||
priv->mux_bak[LIGHT_AUDIO_IO_SEL_IDX] = readl(priv->base);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int light_pinctrl_resume(struct device *dev)
|
||||
{
|
||||
dev_info(dev, "light pinctrl resume\n");
|
||||
struct light_pinctrl *priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
switch(priv->info->type) {
|
||||
case LIGHT_FM_RIGHT:
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not enable padctrl clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = light_pinctrl_restore_regs(priv, LIGHT_PADCTRL0_CFG_REG_NUMS, LIGHT_PADCTRL0_MUX_REG_NUMS);
|
||||
break;
|
||||
case LIGHT_FM_LEFT:
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not enable padctrl clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = light_pinctrl_restore_regs(priv, LIGHT_PADCTRL1_CFG_REG_NUMS, LIGHT_PADCTRL1_MUX_REG_NUMS);
|
||||
break;
|
||||
case LIGHT_FM_AON:
|
||||
break;
|
||||
case LIGHT_FM_AUDIO:
|
||||
ret = light_pinctrl_restore_regs(priv, LIGHT_AUDIO_CFG_REG_NUMS, LIGHT_AUDIO_MUX_REG_NUMS);
|
||||
writel(priv->mux_bak[LIGHT_AUDIO_IO_SEL_IDX], priv->base);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif //CONFIG_PM_SLEEP
|
||||
|
||||
/* Pad names for the pinmux subsystem */
|
||||
static const struct pinctrl_pin_desc light_mpw_pinctrl_pads[] = {
|
||||
LIGHT_PINCTRL_PIN(BOOT_SEL0),
|
||||
@@ -977,6 +1103,13 @@ static struct light_pinctrl_soc_info light_fm_audio_pinctrl_info = {
|
||||
.mux_off = 0x4,
|
||||
};
|
||||
|
||||
//static SIMPLE_DEV_PM_OPS(light_pinctrl_dev_pm_ops,
|
||||
// light_pinctrl_suspend, light_pinctrl_resume);
|
||||
|
||||
static const struct dev_pm_ops light_pinctrl_dev_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(light_pinctrl_suspend, light_pinctrl_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id light_pinctrl_of_match[] = {
|
||||
{ .compatible = "thead,light-mpw-pinctrl", .data = &light_mpw_pinctrl_info},
|
||||
{ .compatible = "thead,light-fm-right-pinctrl", .data = &light_fm_right_pinctrl_info},
|
||||
@@ -991,6 +1124,7 @@ static struct platform_driver light_pinctrl_driver = {
|
||||
.driver = {
|
||||
.name = "light-pinctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &light_pinctrl_dev_pm_ops,
|
||||
.of_match_table = light_pinctrl_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
|
||||
@@ -126,6 +126,13 @@ static bool light_rpmsg_notify(struct virtqueue *vq)
|
||||
int ret;
|
||||
struct light_rpmsg_vq_info *rpvq = vq->priv;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if(rpvq->rpdev->sleep_flag) {
|
||||
dev_err(tdev_priv->dev, "dev in deep sleep, Channel cannot do Tx+++\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
mu_rpmsg = rpvq->vq_id << 16;
|
||||
mutex_lock(&rpvq->rpdev->lock);
|
||||
|
||||
@@ -433,7 +440,9 @@ static int light_rpmsg_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_DELAYED_WORK(&(rpdev->rpmsg_work), rpmsg_work_handler);
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&(rpdev->notifier));
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
sema_init(&rpdev->pm_sem, 0);
|
||||
#endif
|
||||
pr_info("light rpmsg: Ready for cross core communication!\n");
|
||||
|
||||
ret = of_property_read_u32(np, "vdev-nums", &rpdev->vdev_nums);
|
||||
@@ -485,28 +494,111 @@ static int light_rpmsg_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int light_rpmsg_suspend(struct device *dev)
|
||||
|
||||
typedef enum {
|
||||
RPMSG_MAILBOX_TYPE_PM = 0xA0,
|
||||
RPMSG_MAILBOX_TYPE_MAX
|
||||
} rpmsg_mailbox_message_type_en;
|
||||
|
||||
typedef enum {
|
||||
RPMSG_PM_CTRL = 0x50,
|
||||
RPMSG_PM_GET,
|
||||
RPMSG_PM_STATUS,
|
||||
RPMSG_PM_MAX
|
||||
} rpmsg_pm_message_type_en;
|
||||
|
||||
typedef enum {
|
||||
LIGHT_PM_DISABLE = 0xA0,
|
||||
LIGHT_PM_OFF,
|
||||
LIGHT_PM_HW_VAD,
|
||||
LIGHT_PM_TYPE_MAX
|
||||
} light_pm_type_en;
|
||||
|
||||
typedef enum {
|
||||
LIGHT_PM_WAKEUP = 0x50,
|
||||
LIGHT_PM_SLEEP,
|
||||
LIGHT_PM_STATUS_MAX
|
||||
} light_pm_status_en;
|
||||
|
||||
#define MAX_PM_NOTIFY_TIME 10
|
||||
#define MAX_PM_ASK_TIME 10
|
||||
|
||||
static int light_rpmsg_sleep_notify(struct virtqueue *vq, light_pm_type_en type)
|
||||
{
|
||||
struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(rpdev->mu_clk);
|
||||
|
||||
int ret;
|
||||
struct light_rpmsg_vq_info *rpvq = vq->priv;
|
||||
uint8_t sleep_ctrl[4] = {RPMSG_MAILBOX_TYPE_PM, RPMSG_PM_CTRL, type, '\n'};
|
||||
mutex_lock(&rpvq->rpdev->lock);
|
||||
ret = mbox_send_message(tdev_priv->tx_channel, sleep_ctrl);
|
||||
if(ret < 0) {
|
||||
pr_err("sleep notify faild %d", ret);
|
||||
mutex_unlock(&rpvq->rpdev->lock);
|
||||
return ret;
|
||||
}
|
||||
mutex_unlock(&rpvq->rpdev->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_rpmsg_sleep_ask(struct virtqueue *vq)
|
||||
{
|
||||
int ret;
|
||||
struct light_rpmsg_vq_info *rpvq = vq->priv;
|
||||
uint8_t sleep_get[3] = {RPMSG_MAILBOX_TYPE_PM, RPMSG_PM_GET, '\n'};
|
||||
mutex_lock(&rpvq->rpdev->lock);
|
||||
ret = mbox_send_message(tdev_priv->tx_channel, sleep_get);
|
||||
if(ret < 0) {
|
||||
pr_err("sleep ask send faild %d", ret);
|
||||
mutex_unlock(&rpvq->rpdev->lock);
|
||||
return ret;
|
||||
}
|
||||
mutex_unlock(&rpvq->rpdev->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_rpmsg_suspend(struct device *dev)
|
||||
|
||||
{
|
||||
int ret;
|
||||
int try_num = 0;
|
||||
struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
|
||||
|
||||
//clk_disable_unprepare(rpdev->mu_clk);
|
||||
printk("%s,%d,enter",__func__,__LINE__);
|
||||
light_rpmsg_sleep_notify(rpdev->ivdev[0].vq[0], LIGHT_PM_OFF);
|
||||
try_num++;
|
||||
down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
|
||||
while(!rpdev->sleep_flag) {
|
||||
light_rpmsg_sleep_notify(rpdev->ivdev[0].vq[0], LIGHT_PM_OFF);
|
||||
down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
|
||||
if(try_num++ > MAX_PM_NOTIFY_TIME) {
|
||||
pr_err("sleep notify faild after try %d time", MAX_PM_NOTIFY_TIME);
|
||||
printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_rpmsg_resume(struct device *dev)
|
||||
{
|
||||
struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(rpdev->mu_clk);
|
||||
if (ret) {
|
||||
pr_err("unable to enable mu clock\n");
|
||||
return ret;
|
||||
struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int try_num = 0;
|
||||
printk("%s,%d,enter",__func__,__LINE__);
|
||||
while(rpdev->sleep_flag) {
|
||||
ret = light_rpmsg_sleep_ask(rpdev->ivdev[0].vq[0]);
|
||||
down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
|
||||
if(try_num++ > MAX_PM_ASK_TIME) {
|
||||
pr_err("sleep status check faild after try %d time", MAX_PM_ASK_TIME);
|
||||
printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(light_rpmsg_pm_ops, light_rpmsg_suspend, light_rpmsg_resume);
|
||||
@@ -643,7 +735,19 @@ static void mbox_client_light_receive_message(struct mbox_client *client,
|
||||
|
||||
//printk("mbox_client receive rpmsg_work\n");
|
||||
schedule_delayed_work(&(pri_rpdev->rpmsg_work), 0);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if(data[0] == RPMSG_MAILBOX_TYPE_PM && data[1] == RPMSG_PM_STATUS) {
|
||||
if(data[2] == LIGHT_PM_WAKEUP) {
|
||||
pri_rpdev->sleep_flag = 0;
|
||||
up(&pri_rpdev->pm_sem);
|
||||
printk("audio wakeup");
|
||||
} else if(data[2] == LIGHT_PM_SLEEP) {
|
||||
pri_rpdev->sleep_flag = 1;
|
||||
up(&pri_rpdev->pm_sem);
|
||||
printk("audio sleep");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_NONE, 16, 1, tdev->rx_buffer, MBOX_MAX_MSG_LEN, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ struct light_iopmp_cur_sys_entry {
|
||||
|
||||
struct light_iopmp_info {
|
||||
struct device *dev;
|
||||
|
||||
int entries;
|
||||
struct light_iopmp_list *iopmp_list;
|
||||
struct light_iopmp_cur_sys_entry cur_entry;
|
||||
@@ -64,35 +63,41 @@ static struct light_iopmp_list {
|
||||
int iopmp_type;
|
||||
resource_size_t offset;
|
||||
void __iomem *base;
|
||||
bool bypass_en;
|
||||
bool is_default_region;
|
||||
u32 dummy_slave;
|
||||
u32 attr;
|
||||
int lock;
|
||||
struct light_iopmp_entry iopmp_entry;
|
||||
} light_iopmp_lists[] = {
|
||||
{IOPMP_EMMC, 0xFFFC028000, NULL},
|
||||
{IOPMP_SDIO0, 0xFFFC029000, NULL},
|
||||
{IOPMP_SDIO1, 0xFFFC02A000, NULL},
|
||||
{IOPMP_USB0, 0xFFFC02E000, NULL},
|
||||
{IOPMP_AO, 0xFFFFC21000, NULL},
|
||||
{IOPMP_AUD, 0xFFFFC22000, NULL},
|
||||
{IOPMP_CHIP_DBG, 0xFFFFC37000, NULL},
|
||||
{IOPMP_EIP120I, 0xFFFF220000, NULL},
|
||||
{IOPMP_EIP120II, 0xFFFF230000, NULL},
|
||||
{IOPMP_EIP120III, 0xFFFF240000, NULL},
|
||||
{IOPMP_ISP0, 0xFFF4080000, NULL},
|
||||
{IOPMP_ISP1, 0xFFF4081000, NULL},
|
||||
{IOPMP_DW200, 0xFFF4082000, NULL},
|
||||
{IOPMP_VIPRE, 0xFFF4083000, NULL},
|
||||
{IOPMP_VENC, 0xFFFCC60000, NULL},
|
||||
{IOPMP_VDEC, 0xFFFCC61000, NULL},
|
||||
{IOPMP_G2D, 0xFFFCC62000, NULL},
|
||||
{IOPMP_FCE, 0xFFFCC63000, NULL},
|
||||
{IOPMP_NPU, 0xFFFF01C000, NULL},
|
||||
{IOPMP0_DPU, 0xFFFF520000, NULL},
|
||||
{IOPMP1_DPU, 0xFFFF521000, NULL},
|
||||
{IOPMP_GPU, 0xFFFF522000, NULL},
|
||||
{IOPMP_GMAC1, 0xFFFC001000, NULL},
|
||||
{IOPMP_GMAC2, 0xFFFC002000, NULL},
|
||||
{IOPMP_DMAC, 0xFFFFC20000, NULL},
|
||||
{IOPMP_TEE_DMAC, 0xFFFF250000, NULL},
|
||||
{IOPMP_DSP0, 0xFFFF058000, NULL},
|
||||
{IOPMP_DSP1, 0xFFFF059000, NULL},
|
||||
{IOPMP_EMMC, 0xFFFC028000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_SDIO0, 0xFFFC029000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_SDIO1, 0xFFFC02A000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_USB0, 0xFFFC02E000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_AO, 0xFFFFC21000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_AUD, 0xFFFFC22000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_CHIP_DBG, 0xFFFFC37000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_EIP120I, 0xFFFF220000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_EIP120II, 0xFFFF230000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_EIP120III, 0xFFFF240000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_ISP0, 0xFFF4080000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_ISP1, 0xFFF4081000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_DW200, 0xFFF4082000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_VIPRE, 0xFFF4083000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_VENC, 0xFFFCC60000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_VDEC, 0xFFFCC61000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_G2D, 0xFFFCC62000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_FCE, 0xFFFCC63000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_NPU, 0xFFFF01C000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP0_DPU, 0xFFFF520000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP1_DPU, 0xFFFF521000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_GPU, 0xFFFF522000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_GMAC1, 0xFFFC001000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_GMAC2, 0xFFFC002000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_DMAC, 0xFFFFC20000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_TEE_DMAC, 0xFFFF250000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_DSP0, 0xFFFF058000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
{IOPMP_DSP1, 0xFFFF059000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
|
||||
};
|
||||
|
||||
static const struct light_iopmp_driver_data {
|
||||
@@ -481,6 +486,35 @@ static ssize_t light_iopmp_set_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static int light_iopmp_config(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct light_iopmp_info *info = platform_get_drvdata(pdev);
|
||||
struct light_iopmp_list *list = info->iopmp_list;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < info->entries; i++) {
|
||||
if (list[i].iopmp_entry.entry_valid_num > 0) {
|
||||
/* config the iopmp entry */
|
||||
light_iopmp_set_attr(info, i,
|
||||
list[i].attr,
|
||||
list[i].dummy_slave,
|
||||
&list[i].iopmp_entry);
|
||||
|
||||
} else {
|
||||
/* config the iopmp entry */
|
||||
if (list[i].bypass_en)
|
||||
light_iopmp_set_attr_bypass(info, i);
|
||||
else if (list[i].is_default_region)
|
||||
light_iopmp_set_attr_default(info, i, list[i].attr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR(light_iopmp_tap, 0644, light_iopmp_tap_show, light_iopmp_tap_store);
|
||||
static DEVICE_ATTR(light_iopmp_start_addr, 0644, light_iopmp_start_addr_show, light_iopmp_start_addr_store);
|
||||
static DEVICE_ATTR(light_iopmp_end_addr, 0644, light_iopmp_end_addr_show, light_iopmp_end_addr_store);
|
||||
@@ -506,19 +540,20 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct light_iopmp_driver_data *drvdata = NULL;
|
||||
struct light_iopmp_list *match;
|
||||
struct light_iopmp_list *list;
|
||||
struct light_iopmp_info *info;
|
||||
struct device_node *entry_np;
|
||||
int size, entries;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
drvdata = device_get_match_data(dev);
|
||||
if (!drvdata) {
|
||||
dev_err(dev, "cannot get driver data\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
match = drvdata->iopmp_list;
|
||||
entries = light_iopmp_get_entries(match);
|
||||
list = drvdata->iopmp_list;
|
||||
entries = light_iopmp_get_entries(list);
|
||||
if (entries <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
@@ -528,7 +563,7 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
info->entries = entries;
|
||||
info->iopmp_list = match;
|
||||
info->iopmp_list = list;
|
||||
info->dev = dev;
|
||||
|
||||
for_each_child_of_node(dev->of_node, entry_np) {
|
||||
@@ -538,7 +573,6 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
bool bypass_en = false;
|
||||
int region_size;
|
||||
int type;
|
||||
int i = 0, j;
|
||||
|
||||
if (!of_device_is_available(entry_np))
|
||||
continue;
|
||||
@@ -566,13 +600,6 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "missing/invalid reg property\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* config the iopmp entry */
|
||||
if (bypass_en)
|
||||
light_iopmp_set_attr_bypass(info, type);
|
||||
else if (is_default_region)
|
||||
light_iopmp_set_attr_default(info, type, attr);
|
||||
|
||||
} else {
|
||||
region_size >>= 1;
|
||||
if (region_size > LIGHT_IOPMP_REGION_NUM) {
|
||||
@@ -582,28 +609,30 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
|
||||
for (j = 0; j < region_size; j++) {
|
||||
ret = of_property_read_u32_index(entry_np, "regions",
|
||||
j << 1, &info->iopmp_entry[i].reg_start[j]);
|
||||
j << 1, &list[type].iopmp_entry.reg_start[j]);
|
||||
if (ret)
|
||||
break;
|
||||
ret = of_property_read_u32_index(entry_np, "regions",
|
||||
(j << 1) + 1, &info->iopmp_entry[i].reg_end[j]);
|
||||
(j << 1) + 1, &list[type].iopmp_entry.reg_end[j]);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* check the region valid, otherwise drop the iopmp setting */
|
||||
if (j && info->iopmp_entry[i].reg_end[j - 1] > info->iopmp_entry[i].reg_start[j])
|
||||
if (j && list[type].iopmp_entry.reg_end[j - 1] > list[type].iopmp_entry.reg_start[j])
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == region_size) {
|
||||
info->iopmp_entry[i].entry_valid_num = region_size;
|
||||
if (j < region_size)
|
||||
continue;
|
||||
|
||||
/* config the iopmp entry */
|
||||
light_iopmp_set_attr(info, type, attr, dummy_slave, &info->iopmp_entry[i]);
|
||||
}
|
||||
list[type].iopmp_entry.entry_valid_num = region_size;
|
||||
}
|
||||
|
||||
i++;
|
||||
// store valid config
|
||||
list[type].attr = attr;
|
||||
list[type].dummy_slave = dummy_slave;
|
||||
list[type].is_default_region = is_default_region;
|
||||
list[type].bypass_en = bypass_en;
|
||||
}
|
||||
|
||||
/* init the cur entry config */
|
||||
@@ -615,6 +644,8 @@ static int light_iopmp_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
light_iopmp_config(&pdev->dev);
|
||||
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_light_iopmp_group);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to create light iopmp debug sysfs.\n");
|
||||
@@ -632,17 +663,17 @@ static int light_iopmp_remove(struct platform_device *pdev)
|
||||
|
||||
static int __maybe_unused light_iopmp_noirq_suspend(struct device *dev)
|
||||
{
|
||||
/* the iopmp clocks depend on module theirself,
|
||||
* we should keep them always enabled in clock's driver
|
||||
*/
|
||||
// nothing to do
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused light_iopmp_noirq_resume(struct device *dev)
|
||||
{
|
||||
/* TBD: restore IOPMP in noirq stage is too late ?
|
||||
* we should restore these registers setting in early arch resume ?
|
||||
*/
|
||||
/* reinit iopmp register setting
|
||||
ensure system clock enabled before here
|
||||
*/
|
||||
light_iopmp_config(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,14 +63,14 @@ static int light_event_aon_reservemem(struct light_event *event)
|
||||
struct light_aon_ipc *ipc = event->ipc_handle;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(light_event->dev, "aon reservemem...\n");
|
||||
dev_dbg(event->dev, "aon reservemem...\n");
|
||||
|
||||
light_event_msg_hdr_fill(&event->msg.hdr, LIGHT_AON_MISC_FUNC_AON_RESERVE_MEM);
|
||||
event->msg.reserve_offset = LIGHT_EVENT_OFFSET;
|
||||
|
||||
ret = light_aon_call_rpc(ipc, &event->msg, true);
|
||||
if (ret)
|
||||
dev_err(light_event->dev, "failed to set aon reservemem\n");
|
||||
dev_err(event->dev, "failed to set aon reservemem\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ int light_event_set_rebootmode(enum light_rebootmode_index mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!light_event->init)
|
||||
if (!light_event || !light_event->init)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_write(light_event->aon_iram, LIGHT_EVENT_OFFSET, mode);
|
||||
@@ -98,7 +98,7 @@ int light_event_get_rebootmode(enum light_rebootmode_index *mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!light_event->init)
|
||||
if (!light_event || !light_event->init)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_read(light_event->aon_iram, LIGHT_EVENT_OFFSET, mode);
|
||||
@@ -215,7 +215,6 @@ static int light_event_probe(struct platform_device *pdev)
|
||||
thead = devm_kzalloc(&pdev->dev, sizeof(*thead), GFP_KERNEL);
|
||||
if (!thead)
|
||||
return -ENOMEM;
|
||||
light_event = thead;
|
||||
|
||||
ret = light_aon_get_handle(&(thead->ipc_handle));
|
||||
if (ret == -EPROBE_DEFER)
|
||||
@@ -238,10 +237,12 @@ static int light_event_probe(struct platform_device *pdev)
|
||||
return -EPERM;
|
||||
}
|
||||
thead->init = true;
|
||||
light_event = thead;
|
||||
|
||||
ret = light_event_check_powerup();
|
||||
if (ret) {
|
||||
dev_err(dev, "check powerup failed!\n");
|
||||
light_event = NULL;
|
||||
return -EPERM;
|
||||
}
|
||||
dev_info(dev, "light-event driver init successfully\n");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2022 Alibaba Group Holding Limited.
|
||||
*/
|
||||
@@ -10,98 +10,186 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <linux/firmware/thead/ipc.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "light-system-suspend" ": " fmt
|
||||
|
||||
#ifdef CONFIG_PLIC_INT_CLEAR
|
||||
void __iomem *hart0_sbase;
|
||||
/*
|
||||
* Each hart context has a set of control registers associated with it. Right
|
||||
* now there's only two: a source priority threshold over which the hart will
|
||||
* take an interrupt, and a register to claim interrupts.
|
||||
*/
|
||||
#define CONTEXT_BASE 0x200000
|
||||
#define CONTEXT_PER_HART 0x1000
|
||||
#define CONTEXT_THRESHOLD 0x00
|
||||
#define CONTEXT_CLAIM 0x04
|
||||
#endif
|
||||
struct rpc_msg_cpu_info{
|
||||
u16 cpu_id;
|
||||
u16 status;
|
||||
u32 reserved[5];
|
||||
} __packed __aligned(4);
|
||||
|
||||
static int light_suspend_prepare_late(void)
|
||||
struct light_aon_msg_pm_ctrl {
|
||||
struct light_aon_rpc_msg_hdr hdr;
|
||||
union rpc_func_t {
|
||||
struct rpc_msg_cpu_info cpu_info;
|
||||
} __packed __aligned(4) rpc;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct light_aon_pm_ctrl {
|
||||
struct device *dev;
|
||||
struct light_aon_ipc *ipc_handle;
|
||||
struct light_aon_msg_pm_ctrl msg;
|
||||
bool suspend_flag;
|
||||
};
|
||||
|
||||
static struct light_aon_pm_ctrl *aon_pm_ctrl;
|
||||
|
||||
static int light_require_state_pm_ctrl(struct light_aon_msg_pm_ctrl *msg, enum light_aon_misc_func func, bool ack)
|
||||
{
|
||||
#ifdef CONFIG_PLIC_INT_CLEAR
|
||||
void __iomem *claim = hart0_sbase + CONTEXT_CLAIM;
|
||||
irq_hw_number_t hwirq;
|
||||
pr_debug("notify aon subsys...\n");
|
||||
struct light_aon_ipc *ipc = aon_pm_ctrl->ipc_handle;
|
||||
struct light_aon_rpc_msg_hdr *hdr = &msg->hdr;
|
||||
|
||||
pr_debug("clear plic pending interrupt\n");
|
||||
pr_debug("claim base = 0x%lx\n", (unsigned long)claim);
|
||||
hdr->ver = LIGHT_AON_RPC_VERSION;
|
||||
hdr->svc = (uint8_t)LIGHT_AON_RPC_SVC_MISC;
|
||||
hdr->func = (uint8_t)func;
|
||||
hdr->size = LIGHT_AON_RPC_MSG_NUM;
|
||||
|
||||
while ((hwirq = readl(claim))) {
|
||||
pr_debug("claim hwirq%ld\n", hwirq);
|
||||
writel(hwirq, claim);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Two-level switch for interrupt control: it needs to disable interrupts in SIE, not just in SSTATUS,
|
||||
* otherwise the pending interrupts will wakup the cpu immediately after wfi.
|
||||
*/
|
||||
csr_write(CSR_IE, 0);
|
||||
return light_aon_call_rpc(ipc, msg, ack);
|
||||
}
|
||||
|
||||
return 0;
|
||||
static int sbi_suspend_finisher(unsigned long suspend_type,
|
||||
unsigned long resume_addr,
|
||||
unsigned long opaque)
|
||||
{
|
||||
struct sbiret ret;
|
||||
|
||||
ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND,
|
||||
suspend_type, resume_addr, opaque, 0, 0, 0);
|
||||
|
||||
return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0;
|
||||
}
|
||||
|
||||
static int light_suspend_enter(suspend_state_t state)
|
||||
{
|
||||
struct light_aon_msg_pm_ctrl msg = {0};
|
||||
unsigned long suspend_type;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PM))
|
||||
return 0;
|
||||
pr_debug("[%s,%d]enter platform system suspend... state:%d\n", __func__, __LINE__, state);
|
||||
|
||||
switch (state) {
|
||||
case PM_SUSPEND_MEM:
|
||||
pr_debug("enter platform system suspend...\n");
|
||||
cpu_do_idle();
|
||||
pr_debug("wakeup from wfi...\n");
|
||||
break;
|
||||
default:
|
||||
if (state == PM_SUSPEND_MEM)
|
||||
suspend_type = SBI_HSM_SUSP_NON_RET_BIT;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpu_suspend(suspend_type, sbi_suspend_finisher);
|
||||
pr_debug("[%s,%d]wakeup from system suspend\n",__func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int light_suspend_prepare(void)
|
||||
{
|
||||
int ret;
|
||||
aon_pm_ctrl->suspend_flag = true;
|
||||
struct light_aon_msg_pm_ctrl msg = {0};
|
||||
ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_REQUIRE_STR, false);
|
||||
if (ret) {
|
||||
pr_err("[%s,%d]failed to initiate Suspend to Ram process to AON subsystem\n",__func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void light_resume_wake(void)
|
||||
{
|
||||
aon_pm_ctrl->suspend_flag = false;
|
||||
}
|
||||
|
||||
static int thead_cpuhp_offline(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
if(!aon_pm_ctrl->suspend_flag)
|
||||
{
|
||||
struct light_aon_msg_pm_ctrl msg = {0};
|
||||
msg.rpc.cpu_info.cpu_id = (u16)cpu;
|
||||
msg.rpc.cpu_info.status = 0;
|
||||
ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_CPUHP, false);
|
||||
if (ret) {
|
||||
pr_info("failed to notify aon subsys with cpuhp...%08x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thead_cpuhp_online(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
if(!aon_pm_ctrl->suspend_flag)
|
||||
{
|
||||
struct light_aon_msg_pm_ctrl msg = {0};
|
||||
msg.rpc.cpu_info.cpu_id = (u16)cpu;
|
||||
msg.rpc.cpu_info.status = 1;
|
||||
ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_CPUHP, false);
|
||||
if (ret) {
|
||||
pr_info("[%s,%d]failed to bring up aon subsys with cpuhp...%08x\n", __func__, __LINE__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id aon_ctrl_ids[] = {
|
||||
{ .compatible = "thead,light-aon-suspend-ctrl" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct platform_suspend_ops light_suspend_ops = {
|
||||
.enter = light_suspend_enter,
|
||||
.valid = suspend_valid_only_mem,
|
||||
.prepare_late = light_suspend_prepare_late,
|
||||
.prepare_late = light_suspend_prepare,
|
||||
.wake = light_resume_wake,
|
||||
};
|
||||
|
||||
static int __init pm_light_init(void)
|
||||
static int light_pm_probe(struct platform_device *pdev)
|
||||
{
|
||||
#ifdef CONFIG_PLIC_INT_CLEAR
|
||||
struct device_node *np;
|
||||
void __iomem *regs;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
struct light_aon_pm_ctrl *pm_ctrl;
|
||||
|
||||
np = of_find_node_by_path("/soc/interrupt-controller@ffd8000000");
|
||||
if (!np) {
|
||||
pr_err("no plic interrupt controller found\n");
|
||||
return -EINVAL;
|
||||
pm_ctrl = devm_kzalloc(&pdev->dev, sizeof(*aon_pm_ctrl), GFP_KERNEL);
|
||||
if (!pm_ctrl)
|
||||
return -ENOMEM;
|
||||
aon_pm_ctrl = pm_ctrl;
|
||||
|
||||
ret = light_aon_get_handle(&(aon_pm_ctrl->ipc_handle));
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
pr_err("[%s, %d]failed to register ipc_handler.\n",__func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regs = of_iomap(np, 0);
|
||||
if (WARN_ON(!regs)) {
|
||||
pr_err("failed to ioremap interrupt regs\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
hart0_sbase = regs + CONTEXT_BASE + 1 * CONTEXT_PER_HART; /* 1 means s mode */
|
||||
|
||||
pr_debug("hart0_sbase = 0x%lx\n", (unsigned long)hart0_sbase);
|
||||
#endif
|
||||
|
||||
pr_info("set system suspend platform callbacks\n");
|
||||
|
||||
suspend_set_ops(&light_suspend_ops);
|
||||
|
||||
ret = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN, "soc/thead:online",
|
||||
thead_cpuhp_online,
|
||||
thead_cpuhp_offline);
|
||||
if(ret < 0) {
|
||||
pr_err("[%s,%d]failed to register hotplug callbacks with err %08x.\n", __func__, __LINE__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
aon_pm_ctrl->dev = &pdev->dev;
|
||||
aon_pm_ctrl->suspend_flag = false;
|
||||
platform_set_drvdata(pdev, aon_pm_ctrl);
|
||||
|
||||
dev_info(&pdev->dev, "Light power management control sys successfully registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(pm_light_init);
|
||||
static struct platform_driver light_pm_driver = {
|
||||
.driver = {
|
||||
.name = "light-pm",
|
||||
.of_match_table = aon_ctrl_ids,
|
||||
},
|
||||
.probe = light_pm_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(light_pm_driver);
|
||||
|
||||
@@ -89,6 +89,52 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void dw_spi_register_suspend(struct dw_spi *dws)
|
||||
{
|
||||
struct dw_spi_context *ctx = &dws->ctx;
|
||||
|
||||
ctx->ctrlr0 = dw_readl(dws, DW_SPI_CTRLR0);
|
||||
ctx->ctrlr1 = dw_readl(dws, DW_SPI_CTRLR1);
|
||||
ctx->ssienr = dw_readl(dws, DW_SPI_SSIENR);
|
||||
ctx->mwcr = dw_readl(dws, DW_SPI_MWCR);
|
||||
ctx->ser = dw_readl(dws, DW_SPI_SER);
|
||||
ctx->baudr = dw_readl(dws, DW_SPI_BAUDR);
|
||||
ctx->txftlr = dw_readl(dws, DW_SPI_TXFTLR);
|
||||
ctx->rxftlr = dw_readl(dws, DW_SPI_RXFTLR);
|
||||
ctx->txflr = dw_readl(dws, DW_SPI_TXFLR);
|
||||
ctx->rxflr = dw_readl(dws, DW_SPI_RXFLR);
|
||||
ctx->imr = dw_readl(dws, DW_SPI_IMR);
|
||||
ctx->dmacr = dw_readl(dws, DW_SPI_DMACR);
|
||||
ctx->dmatdlr = dw_readl(dws, DW_SPI_DMATDLR);
|
||||
ctx->dmardlr = dw_readl(dws, DW_SPI_DMARDLR);
|
||||
ctx->rx_sample_dly = dw_readl(dws, DW_SPI_RX_SAMPLE_DLY);
|
||||
|
||||
}
|
||||
|
||||
static void dw_spi_register_resume(struct dw_spi *dws)
|
||||
{
|
||||
struct dw_spi_context *ctx = &dws->ctx;
|
||||
|
||||
dw_writel(dws, DW_SPI_SSIENR, 0);
|
||||
dw_writel(dws, DW_SPI_CTRLR0, ctx->ctrlr0);
|
||||
dw_writel(dws, DW_SPI_CTRLR1, ctx->ctrlr1);
|
||||
dw_writel(dws, DW_SPI_TXFTLR, ctx->txftlr);
|
||||
dw_writel(dws, DW_SPI_RXFTLR, ctx->rxftlr);
|
||||
dw_writel(dws, DW_SPI_TXFLR, ctx->txflr);
|
||||
dw_writel(dws, DW_SPI_RXFLR, ctx->rxflr);
|
||||
dw_writel(dws, DW_SPI_IMR, ctx->imr);
|
||||
dw_writel(dws, DW_SPI_DMATDLR, ctx->dmatdlr);
|
||||
dw_writel(dws, DW_SPI_DMARDLR, ctx->dmardlr);
|
||||
dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, ctx->rx_sample_dly);
|
||||
dw_writel(dws, DW_SPI_SER, ctx->ser);
|
||||
dw_writel(dws, DW_SPI_BAUDR, ctx->baudr);
|
||||
dw_writel(dws, DW_SPI_MWCR, ctx->mwcr);
|
||||
dw_writel(dws, DW_SPI_DMACR, ctx->dmacr);
|
||||
dw_writel(dws, DW_SPI_SSIENR, ctx->ssienr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void dw_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
|
||||
@@ -940,6 +986,9 @@ int dw_spi_suspend_host(struct dw_spi *dws)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
dw_spi_register_suspend(dws);
|
||||
#endif
|
||||
spi_shutdown_chip(dws);
|
||||
return 0;
|
||||
}
|
||||
@@ -948,6 +997,9 @@ EXPORT_SYMBOL_GPL(dw_spi_suspend_host);
|
||||
int dw_spi_resume_host(struct dw_spi *dws)
|
||||
{
|
||||
spi_hw_init(&dws->master->dev, dws);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
dw_spi_register_resume(dws);
|
||||
#endif
|
||||
return spi_controller_resume(dws->master);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_spi_resume_host);
|
||||
|
||||
@@ -27,9 +27,59 @@ struct dw_qspi_mmio {
|
||||
struct dw_spi dws;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct clk *sclk;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
static int qspi_clk_prepare_enable(struct dw_qspi_mmio *dwsmmio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dwsmmio->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(dwsmmio->sclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qspi_clk_disable_unprepare(struct dw_qspi_mmio *dwsmmio)
|
||||
{
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_qspi_mmio_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_qspi_mmio *dwsmmio = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_qspi_suspend_host(&dwsmmio->dws);
|
||||
|
||||
qspi_clk_disable_unprepare(dwsmmio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_qspi_mmio_resume(struct device *dev)
|
||||
{
|
||||
struct dw_qspi_mmio *dwsmmio = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = qspi_clk_prepare_enable(dwsmmio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable spi clock(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return dw_qspi_resume_host(&dwsmmio->dws);
|
||||
}
|
||||
|
||||
static int dw_qspi_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int (*init_func)(struct platform_device *pdev,
|
||||
@@ -74,6 +124,15 @@ static int dw_qspi_mmio_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
dwsmmio->sclk = devm_clk_get_optional(&pdev->dev, "sclk");
|
||||
if (IS_ERR(dwsmmio->sclk)) {
|
||||
ret = PTR_ERR(dwsmmio->sclk);
|
||||
goto out_clk;
|
||||
}
|
||||
ret = clk_prepare_enable(dwsmmio->sclk);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
/* set bus number */
|
||||
dws->bus_num = pdev->id;
|
||||
|
||||
@@ -113,6 +172,7 @@ static int dw_qspi_mmio_probe(struct platform_device *pdev)
|
||||
|
||||
out:
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
out_clk:
|
||||
clk_disable_unprepare(dwsmmio->clk);
|
||||
return ret;
|
||||
@@ -124,6 +184,7 @@ static int dw_qspi_mmio_remove(struct platform_device *pdev)
|
||||
|
||||
dw_qspi_remove_host(&dwsmmio->dws);
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
clk_disable_unprepare(dwsmmio->clk);
|
||||
|
||||
return 0;
|
||||
@@ -135,12 +196,17 @@ static const struct of_device_id dw_qspi_mmio_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_qspi_mmio_of_match);
|
||||
|
||||
static const struct dev_pm_ops qspi_mmio_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_qspi_mmio_suspend, dw_qspi_mmio_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_qspi_mmio_driver = {
|
||||
.probe = dw_qspi_mmio_probe,
|
||||
.remove = dw_qspi_mmio_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dw_qspi_mmio_of_match,
|
||||
.pm = &qspi_mmio_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(dw_qspi_mmio_driver);
|
||||
|
||||
@@ -29,6 +29,7 @@ struct dw_spi_mmio {
|
||||
struct dw_spi dws;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct clk *sclk;
|
||||
void *priv;
|
||||
struct reset_control *rstc;
|
||||
};
|
||||
@@ -222,6 +223,54 @@ static int dw_spi_keembay_init(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_clk_prepare_enable(struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dwsmmio->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(dwsmmio->sclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_clk_disable_unprepare(struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_spi_mmio_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_spi_suspend_host(&dwsmmio->dws);
|
||||
spi_clk_disable_unprepare(dwsmmio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_spi_mmio_resume(struct device *dev)
|
||||
{
|
||||
struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_clk_prepare_enable(dwsmmio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable spi clock(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return dw_spi_resume_host(&dwsmmio->dws);
|
||||
}
|
||||
|
||||
static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int (*init_func)(struct platform_device *pdev,
|
||||
@@ -267,6 +316,15 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
dwsmmio->sclk = devm_clk_get_optional(&pdev->dev, "sclk");
|
||||
if (IS_ERR(dwsmmio->sclk)) {
|
||||
ret = PTR_ERR(dwsmmio->sclk);
|
||||
goto out_clk;
|
||||
}
|
||||
ret = clk_prepare_enable(dwsmmio->sclk);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
/* find an optional reset controller */
|
||||
dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(dwsmmio->rstc)) {
|
||||
@@ -306,6 +364,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||
out:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
out_clk:
|
||||
clk_disable_unprepare(dwsmmio->clk);
|
||||
reset_control_assert(dwsmmio->rstc);
|
||||
@@ -320,6 +379,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
|
||||
dw_spi_remove_host(&dwsmmio->dws);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->sclk);
|
||||
clk_disable_unprepare(dwsmmio->clk);
|
||||
reset_control_assert(dwsmmio->rstc);
|
||||
|
||||
@@ -347,6 +407,10 @@ static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops spi_mmio_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_spi_mmio_suspend, dw_spi_mmio_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_spi_mmio_driver = {
|
||||
.probe = dw_spi_mmio_probe,
|
||||
.remove = dw_spi_mmio_remove,
|
||||
@@ -354,6 +418,7 @@ static struct platform_driver dw_spi_mmio_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dw_spi_mmio_of_match,
|
||||
.acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match),
|
||||
.pm = &spi_mmio_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(dw_spi_mmio_driver);
|
||||
|
||||
@@ -138,6 +138,57 @@ static inline void dw_qspi_debugfs_remove(struct dw_spi *dws)
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void dw_qspi_register_suspend(struct dw_spi *dws)
|
||||
{
|
||||
struct dw_qspi_context *ctx = &dws->ctx;
|
||||
|
||||
ctx->ctrlr0 = dw_readl(dws, DW_SPI_CTRL0);
|
||||
ctx->ctrlr1 = dw_readl(dws, DW_SPI_CTRL1);
|
||||
ctx->ssienr = dw_readl(dws, DW_SPI_SSIENR);
|
||||
ctx->mwcr = dw_readl(dws, DW_SPI_MWCR);
|
||||
ctx->ser = dw_readl(dws, DW_SPI_SER);
|
||||
ctx->baudr = dw_readl(dws, DW_SPI_BAUDR);
|
||||
ctx->txftlr = dw_readl(dws, DW_SPI_TXFLTR);
|
||||
ctx->rxftlr = dw_readl(dws, DW_SPI_RXFLTR);
|
||||
ctx->txflr = dw_readl(dws, DW_SPI_TXFLR);
|
||||
ctx->rxflr = dw_readl(dws, DW_SPI_RXFLR);
|
||||
ctx->imr = dw_readl(dws, DW_SPI_IMR);
|
||||
ctx->dmacr = dw_readl(dws, DW_SPI_DMACR);
|
||||
ctx->dmatdlr = dw_readl(dws, DW_SPI_DMATDLR);
|
||||
ctx->dmardlr = dw_readl(dws, DW_SPI_DMARDLR);
|
||||
ctx->rx_sample_dly = dw_readl(dws, DW_SPI_RX_SMP_DLY);
|
||||
ctx->spi_ctrlr0 = dw_readl(dws, DW_SPI_SPI_CTRLR0);
|
||||
ctx->txd_drv_edge = dw_readl(dws, DW_SPI_TXD_DRV_EDGE);
|
||||
|
||||
}
|
||||
|
||||
static void dw_qspi_register_resume(struct dw_spi *dws)
|
||||
{
|
||||
struct dw_qspi_context *ctx = &dws->ctx;
|
||||
|
||||
dw_writel(dws, DW_SPI_SSIENR, 0);
|
||||
dw_writel(dws, DW_SPI_CTRL0, ctx->ctrlr0);
|
||||
dw_writel(dws, DW_SPI_CTRL1, ctx->ctrlr1);
|
||||
dw_writel(dws, DW_SPI_TXFLTR, ctx->txftlr);
|
||||
dw_writel(dws, DW_SPI_RXFLTR, ctx->rxftlr);
|
||||
dw_writel(dws, DW_SPI_TXFLR, ctx->txflr);
|
||||
dw_writel(dws, DW_SPI_RXFLR, ctx->rxflr);
|
||||
dw_writel(dws, DW_SPI_IMR, ctx->imr);
|
||||
dw_writel(dws,DW_SPI_DMATDLR, ctx->dmatdlr);
|
||||
dw_writel(dws, DW_SPI_DMARDLR, ctx->dmardlr);
|
||||
dw_writel(dws, DW_SPI_RX_SMP_DLY, ctx->rx_sample_dly);
|
||||
dw_writel(dws, DW_SPI_SPI_CTRLR0, ctx->spi_ctrlr0);
|
||||
dw_writel(dws, DW_SPI_TXD_DRV_EDGE, ctx->txd_drv_edge);
|
||||
dw_writel(dws, DW_SPI_SER, ctx->ser);
|
||||
dw_writel(dws, DW_SPI_BAUDR, ctx->baudr);
|
||||
dw_writel(dws, DW_SPI_MWCR, ctx->mwcr);
|
||||
dw_writel(dws, DW_SPI_DMACR, ctx->dmacr);
|
||||
dw_writel(dws, DW_SPI_SSIENR, ctx->ssienr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void dw_qspi_set_cs(struct spi_device *spi,struct dw_spi *dws, bool enable)
|
||||
{
|
||||
bool enable1 = !enable;
|
||||
@@ -779,6 +830,10 @@ int dw_qspi_suspend_host(struct dw_spi *dws)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
dw_qspi_register_suspend(dws);
|
||||
#endif
|
||||
|
||||
spi_shutdown_chip(dws);
|
||||
return 0;
|
||||
}
|
||||
@@ -787,6 +842,9 @@ EXPORT_SYMBOL_GPL(dw_qspi_suspend_host);
|
||||
int dw_qspi_resume_host(struct dw_spi *dws)
|
||||
{
|
||||
qspi_hw_init(&dws->master->dev, dws);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
dw_qspi_register_resume(dws);
|
||||
#endif
|
||||
return spi_controller_resume(dws->master);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_qspi_resume_host);
|
||||
|
||||
@@ -148,6 +148,28 @@ enum dw_ssi_type {
|
||||
SSI_NS_MICROWIRE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct dw_qspi_context {
|
||||
u32 ctrlr0;
|
||||
u32 ctrlr1;
|
||||
u32 ssienr;
|
||||
u32 mwcr;
|
||||
u32 ser;
|
||||
u32 baudr;
|
||||
u32 txftlr;
|
||||
u32 rxftlr;
|
||||
u32 txflr;
|
||||
u32 rxflr;
|
||||
u32 imr;
|
||||
u32 dmacr;
|
||||
u32 dmatdlr;
|
||||
u32 dmardlr;
|
||||
u32 rx_sample_dly;
|
||||
u32 spi_ctrlr0;
|
||||
u32 txd_drv_edge;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dw_spi;
|
||||
struct dw_spi_dma_ops {
|
||||
int (*dma_init)(struct dw_spi *dws);
|
||||
@@ -212,6 +234,10 @@ struct dw_spi {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct dw_qspi_context ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
|
||||
|
||||
@@ -130,6 +130,26 @@ struct dw_spi_cfg {
|
||||
u32 freq;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct dw_spi_context {
|
||||
u32 ctrlr0;
|
||||
u32 ctrlr1;
|
||||
u32 ssienr;
|
||||
u32 mwcr;
|
||||
u32 ser;
|
||||
u32 baudr;
|
||||
u32 txftlr;
|
||||
u32 rxftlr;
|
||||
u32 txflr;
|
||||
u32 rxflr;
|
||||
u32 imr;
|
||||
u32 dmacr;
|
||||
u32 dmatdlr;
|
||||
u32 dmardlr;
|
||||
u32 rx_sample_dly;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dw_spi;
|
||||
struct dw_spi_dma_ops {
|
||||
int (*dma_init)(struct device *dev, struct dw_spi *dws);
|
||||
@@ -189,6 +209,10 @@ struct dw_spi {
|
||||
struct dentry *debugfs;
|
||||
struct debugfs_regset32 regset;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct dw_spi_context ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#define USB_CLK_GATE_STS 0x0
|
||||
#define USB_LOGIC_ANALYZER_TRACE_STS0 0x4
|
||||
#define USB_LOGIC_ANALYZER_TRACE_STS1 0x8
|
||||
#define USB_GPIO 0xc
|
||||
#define USB_GPIO 0xc
|
||||
#define USB_DEBUG_STS0 0x10
|
||||
#define USB_DEBUG_STS1 0x14
|
||||
#define USB_DEBUG_STS2 0x18
|
||||
@@ -38,10 +38,10 @@
|
||||
#define USBPHY_TEST_CTRL1 0x28
|
||||
#define USBPHY_TEST_CTRL2 0x2c
|
||||
#define USBPHY_TEST_CTRL3 0x30
|
||||
#define USB_SSP_EN 0x34
|
||||
#define USB_SSP_EN 0x34
|
||||
#define USB_HADDR_SEL 0x38
|
||||
#define USB_SYS 0x3c
|
||||
#define USB_HOST_STATUS 0x40
|
||||
#define USB_SYS 0x3c
|
||||
#define USB_HOST_STATUS 0x40
|
||||
#define USB_HOST_CTRL 0x44
|
||||
#define USBPHY_HOST_CTRL 0x48
|
||||
#define USBPHY_HOST_STATUS 0x4c
|
||||
@@ -54,10 +54,10 @@
|
||||
/* USB_SYS */
|
||||
#define TEST_POWERDOWN_SSP BIT(2)
|
||||
#define TEST_POWERDOWN_HSP BIT(1)
|
||||
#define COMMONONN BIT(0)
|
||||
#define COMMONONN BIT(0)
|
||||
|
||||
/* USB_SSP_EN */
|
||||
#define REF_SSP_EN BIT(0)
|
||||
#define REF_SSP_EN BIT(0)
|
||||
|
||||
/* USBPHY_HOST_CTRL */
|
||||
#define HOST_U2_PORT_DISABLE BIT(6)
|
||||
@@ -83,170 +83,64 @@ MODULE_PARM_DESC(usb_role, "USB role");
|
||||
|
||||
struct dwc3_thead {
|
||||
struct device *dev;
|
||||
struct platform_device *dwc3;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clocks;
|
||||
|
||||
struct regmap *usb0_apb;
|
||||
struct regmap *usb3_drd;
|
||||
struct regmap *misc_sysreg;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct extcon_dev *host_edev;
|
||||
struct notifier_block vbus_nb;
|
||||
struct notifier_block host_nb;
|
||||
|
||||
struct gpio_desc *hubswitch;
|
||||
struct regulator *hub1v2;
|
||||
struct regulator *hub5v;
|
||||
struct regulator *vbus;
|
||||
|
||||
enum usb_dr_mode mode;
|
||||
bool is_suspended;
|
||||
bool pm_suspended;
|
||||
};
|
||||
|
||||
static int dwc3_thead_vbus_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
static void dwc3_thead_deassert(struct dwc3_thead *thead)
|
||||
{
|
||||
struct dwc3_thead *thead = container_of(nb, struct dwc3_thead, vbus_nb);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
thead->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int dwc3_thead_host_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dwc3_thead *thead = container_of(nb, struct dwc3_thead, host_nb);
|
||||
|
||||
/* disable vbus override in host mode */
|
||||
thead->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int dwc3_thead_register_extcon(struct dwc3_thead *thead)
|
||||
{
|
||||
struct device *dev = thead->dev;
|
||||
struct extcon_dev *host_edev;
|
||||
int ret;
|
||||
|
||||
if (!of_property_read_bool(dev->of_node, "extcon"))
|
||||
return 0;
|
||||
|
||||
thead->edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(thead->edev))
|
||||
return PTR_ERR(thead->edev);
|
||||
|
||||
thead->vbus_nb.notifier_call = dwc3_thead_vbus_notifier;
|
||||
|
||||
thead->host_edev = extcon_get_edev_by_phandle(dev, 1);
|
||||
if (IS_ERR(thead->host_edev))
|
||||
thead->host_edev = NULL;
|
||||
|
||||
ret = devm_extcon_register_notifier(dev, thead->edev, EXTCON_USB,
|
||||
&thead->vbus_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "VBUS notifier register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (thead->host_edev)
|
||||
host_edev = thead->host_edev;
|
||||
else
|
||||
host_edev = thead->edev;
|
||||
|
||||
thead->host_nb.notifier_call = dwc3_thead_host_notifier;
|
||||
ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST,
|
||||
&thead->host_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Host notifier register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update initial VBUS override based on extcon state */
|
||||
if (extcon_get_state(thead->edev, EXTCON_USB) ||
|
||||
!extcon_get_state(host_edev, EXTCON_USB_HOST))
|
||||
dwc3_thead_vbus_notifier(&thead->vbus_nb, true, thead->edev);
|
||||
else
|
||||
dwc3_thead_vbus_notifier(&thead->vbus_nb, false, thead->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_thead_suspend(struct dwc3_thead *thead)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_thead_resume(struct dwc3_thead *thead)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_thead_of_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_thead *thead = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node, *dwc3_np;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
dwc3_np = of_get_child_by_name(np, "dwc3");
|
||||
if (!dwc3_np) {
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
goto node_put;
|
||||
}
|
||||
|
||||
thead->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
if (!thead->dwc3) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "failed to get dwc3 platform device\n");
|
||||
}
|
||||
|
||||
node_put:
|
||||
of_node_put(dwc3_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_thead_savepower_u3phy(struct dwc3_thead *thead)
|
||||
{
|
||||
struct device *dev = thead->dev;
|
||||
int reg;
|
||||
|
||||
/* config usb top within USB ctrl & PHY reset */
|
||||
/* 1. reset assert */
|
||||
regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
|
||||
USB3_DRD_MASK, USB3_DRD_PRST);
|
||||
|
||||
/*
|
||||
* dwc reg also need to be configed to save power
|
||||
* 1 set USB_SYS[COMMONONN] while GCTL[SOFITPSYNC]=1
|
||||
* notice GCTL[SOFITPSYNC] should be mutex with GFLADJ[LPM_SEL].
|
||||
* 2 enable GUSB3PIPECLT[SUSPENDEN]
|
||||
/*
|
||||
* 2. Common Block Power-Down Control.
|
||||
* Controls the power-down signals in the PLL block
|
||||
* when the USB 3.0 femtoPHY is in Suspend or Sleep mode.
|
||||
*/
|
||||
regmap_update_bits(thead->usb0_apb, USB_SYS,
|
||||
regmap_update_bits(thead->usb3_drd, USB_SYS,
|
||||
COMMONONN, COMMONONN);
|
||||
regmap_update_bits(thead->usb0_apb, USB_SSP_EN,
|
||||
REF_SSP_EN, REF_SSP_EN);
|
||||
regmap_write(thead->usb0_apb, USB_HOST_CTRL, 0x1101);
|
||||
|
||||
/*
|
||||
* 3. Reference Clock Enable for SS function.
|
||||
* Enables the reference clock to the prescaler.
|
||||
* The ref_ssp_en signal must remain de-asserted until
|
||||
* the reference clock is running at the appropriate frequency,
|
||||
* at which point ref_ssp_en can be asserted.
|
||||
* For lower power states, ref_ssp_en can also be de-asserted.
|
||||
*/
|
||||
regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
|
||||
REF_SSP_EN, REF_SSP_EN);
|
||||
|
||||
/* 4. set host ctrl */
|
||||
regmap_write(thead->usb3_drd, USB_HOST_CTRL, 0x1101);
|
||||
|
||||
/* 5. reset deassert */
|
||||
regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
|
||||
USB3_DRD_MASK, USB3_DRD_MASK);
|
||||
|
||||
regmap_read(thead->usb0_apb, USB_SYS, ®);
|
||||
dev_dbg(dev, "usb_sys:0x%x\n", reg);
|
||||
/* 6. wait deassert complete */
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
regmap_read(thead->usb0_apb, USB_HOST_CTRL, ®);
|
||||
dev_dbg(dev, "host_ctrl:0x%x\n", reg);
|
||||
static void dwc3_thead_assert(struct dwc3_thead *thead)
|
||||
{
|
||||
/* close ssp */
|
||||
regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
|
||||
REF_SSP_EN, 0);
|
||||
|
||||
/* reset assert usb */
|
||||
regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
|
||||
USB3_DRD_MASK, 0);
|
||||
|
||||
regmap_read(thead->misc_sysreg, USB3_DRD_SWRST, ®);
|
||||
dev_dbg(dev, "drd_swrst:0x%x\n", reg);
|
||||
}
|
||||
|
||||
static int dwc3_thead_probe(struct platform_device *pdev)
|
||||
@@ -271,111 +165,130 @@ static int dwc3_thead_probe(struct platform_device *pdev)
|
||||
thead->vbus = devm_regulator_get(dev, "vbus");
|
||||
if (IS_ERR(thead->vbus))
|
||||
dev_dbg(dev, "no need to get vbus\n");
|
||||
else
|
||||
regulator_enable(thead->vbus);
|
||||
else {
|
||||
ret = regulator_enable(thead->vbus);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable regulator vbus %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
thead->hub1v2 = devm_regulator_get(dev, "hub1v2");
|
||||
if (IS_ERR(thead->hub1v2))
|
||||
dev_dbg(dev, "no need to set hub1v2\n");
|
||||
else
|
||||
regulator_enable(thead->hub1v2);
|
||||
else {
|
||||
ret = regulator_enable(thead->hub1v2);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
thead->hub5v = devm_regulator_get(dev, "hub5v");
|
||||
if (IS_ERR(thead->hub5v))
|
||||
dev_dbg(dev, "no need to set hub5v\n");
|
||||
else
|
||||
regulator_enable(thead->hub5v);
|
||||
else {
|
||||
ret = regulator_enable(thead->hub5v);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
thead->misc_sysreg = syscon_regmap_lookup_by_phandle(np, "usb3-misc-regmap");
|
||||
if (IS_ERR(thead->misc_sysreg))
|
||||
return PTR_ERR(thead->misc_sysreg);
|
||||
|
||||
thead->usb0_apb = syscon_regmap_lookup_by_phandle(np, "usb3-drd-regmap");
|
||||
if (IS_ERR(thead->usb0_apb))
|
||||
return PTR_ERR(thead->usb0_apb);
|
||||
thead->usb3_drd = syscon_regmap_lookup_by_phandle(np, "usb3-drd-regmap");
|
||||
if (IS_ERR(thead->usb3_drd))
|
||||
return PTR_ERR(thead->usb3_drd);
|
||||
|
||||
dwc3_thead_savepower_u3phy(thead);
|
||||
ret = clk_bulk_get_all(thead->dev, &thead->clks);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = dwc3_thead_of_register_core(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
|
||||
goto depopulate;
|
||||
}
|
||||
thead->num_clocks = ret;
|
||||
|
||||
/* fixme: need to verify because hw can't support detect event */
|
||||
ret = dwc3_thead_register_extcon(thead);
|
||||
ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dwc3_thead_deassert(thead);
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
thead->is_suspended = false;
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
dev_info(dev, "light dwc3 probe ok!\n");
|
||||
|
||||
return 0;
|
||||
|
||||
depopulate:
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
err_clk_put:
|
||||
clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
|
||||
clk_bulk_put_all(thead->num_clocks, thead->clks);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_thead_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_thead *thead = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_disable(dev);
|
||||
dwc3_thead_assert(thead);
|
||||
|
||||
of_platform_depopulate(thead->dev);
|
||||
|
||||
clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
|
||||
clk_bulk_put_all(thead->num_clocks, thead->clks);
|
||||
|
||||
pm_runtime_disable(thead->dev);
|
||||
pm_runtime_set_suspended(thead->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_thead_pm_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_thead_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_thead *thead = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = dwc3_thead_suspend(thead);
|
||||
if (!ret)
|
||||
thead->pm_suspended = true;
|
||||
dwc3_thead_assert(thead);
|
||||
|
||||
return ret;
|
||||
clk_bulk_disable(thead->num_clocks, thead->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_thead_pm_resume(struct device *dev)
|
||||
|
||||
static int dwc3_thead_pm_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_thead *thead = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_thead_resume(thead);
|
||||
if (!ret)
|
||||
thead->pm_suspended = false;
|
||||
ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clk ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3_thead_deassert(thead);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_thead_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_thead *thead = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_thead_suspend(thead);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_thead_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_thead *thead = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_thead_resume(thead);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_thead_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_thead_pm_suspend, dwc3_thead_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_thead_runtime_suspend, dwc3_thead_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
#define DEV_PM_OPS (&dwc3_thead_dev_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct of_device_id dwc3_thead_of_match[] = {
|
||||
{ .compatible = "thead,dwc3" },
|
||||
@@ -388,10 +301,7 @@ static struct platform_driver dwc3_thead_driver = {
|
||||
.remove = dwc3_thead_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-thead",
|
||||
/*
|
||||
* fixme: need to verify because hw can't support plug event
|
||||
*/
|
||||
// .pm = &dwc3_thead_dev_pm_ops,
|
||||
.pm = DEV_PM_OPS,
|
||||
.of_match_table = dwc3_thead_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -102,8 +102,7 @@ static int carveout_buf_setup(void)
|
||||
node = of_find_node_by_name(NULL, "vdmabuf_reserved_memory");
|
||||
if (!node) {
|
||||
ret = -EINVAL;
|
||||
dev_err(drv_info->dev,
|
||||
"failed to find vdmabuf_reserved_memory node\n");
|
||||
dev_info(drv_info->dev, "no vdmabuf_reserved_memory node\n");
|
||||
}
|
||||
|
||||
for (i = 0; i <= VIRTIO_VDMABUF_CARVEOUTS_NUM; i++) {
|
||||
@@ -1745,9 +1744,7 @@ static int __init vhost_vdmabuf_init(void)
|
||||
|
||||
ret = carveout_buf_setup();
|
||||
if (ret < 0)
|
||||
dev_warn(drv_info->dev,
|
||||
"vhost-vdmabuf: carveout bufs setup failed %d\n",
|
||||
ret);
|
||||
dev_info(drv_info->dev, "vhost-vdmabuf: carveout buf not setup %d\n", ret);
|
||||
|
||||
mutex_init(&drv_info->g_mutex);
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@ static int carveout_buf_setup(void)
|
||||
node = of_find_node_by_name(NULL, "vdmabuf_reserved_memory");
|
||||
if (!node) {
|
||||
ret = -EINVAL;
|
||||
dev_err(drv_info->dev,
|
||||
"failed to find vdmabuf_reserved_memory node\n");
|
||||
pr_info("no vdmabuf_reserved_memory node\n");
|
||||
}
|
||||
|
||||
for (i = 0; i <= VIRTIO_VDMABUF_CARVEOUTS_NUM; i++) {
|
||||
@@ -1201,6 +1200,7 @@ static int virtio_vdmabuf_create_dmabuf(struct virtio_vdmabuf *vdmabuf,
|
||||
struct dma_buf *dmabuf;
|
||||
unsigned long irqflags;
|
||||
struct page *page = NULL;
|
||||
unsigned int gfp = GFP_KERNEL;
|
||||
int ret, i = 0, npages, bp_num;
|
||||
|
||||
/* For carveout, buf size is fixed, user don't need specify it */
|
||||
@@ -1226,7 +1226,6 @@ static int virtio_vdmabuf_create_dmabuf(struct virtio_vdmabuf *vdmabuf,
|
||||
exp_info.flags = O_RDWR;
|
||||
exp_info.priv = exp_buf;
|
||||
|
||||
unsigned int gfp = GFP_KERNEL;
|
||||
if(attr->flags & VIRTIO_VDAMBUF_DMA32)
|
||||
{
|
||||
gfp |= __GFP_DMA32;
|
||||
@@ -1652,8 +1651,7 @@ static int __init virtio_vdmabuf_init(void)
|
||||
|
||||
ret = carveout_buf_setup();
|
||||
if (ret < 0)
|
||||
pr_warn("virtio-vdmabuf: carveout bufs setup failed %d\n",
|
||||
ret);
|
||||
pr_info("virtio-vdmabuf: carveout buf not setup %d\n", ret);
|
||||
|
||||
mutex_init(&drv_info->g_mutex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user