From 3a760d986568b67d1f8411dab64608075817b90d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 29 Jul 2019 15:02:17 +0200 Subject: [PATCH 1/9] soc: amlogic: meson-clk-measure: protect measure with a mutex In order to protect clock measuring when multiple process asks for a measure, protect the main measure function with mutexes. Reviewed-by: Kevin Hilman Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index 19d4cbc93a17..c470e24f1dfa 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -11,6 +11,8 @@ #include #include +static DEFINE_MUTEX(measure_lock); + #define MSR_CLK_DUTY 0x0 #define MSR_CLK_REG0 0x4 #define MSR_CLK_REG1 0x8 @@ -360,6 +362,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int val; int ret; + ret = mutex_lock_interruptible(&measure_lock); + if (ret) + return ret; + regmap_write(priv->regmap, MSR_CLK_REG0, 0); /* Set measurement duration */ @@ -377,8 +383,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0, val, !(val & MSR_BUSY), 10, 10000); - if (ret) + if (ret) { + mutex_unlock(&measure_lock); return ret; + } /* Disable */ regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0); @@ -386,6 +394,8 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, /* Get the value in multiple of gate time counts */ regmap_read(priv->regmap, MSR_CLK_REG2, &val); + mutex_unlock(&measure_lock); + if (val >= MSR_VAL_MASK) return -EINVAL; From c33b2777d01eb0039a53f14f8c0e4cca8df501c7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 29 Jul 2019 15:02:18 +0200 Subject: [PATCH 2/9] soc: amlogic: meson-clk-measure: add G12B second cluster cpu clk Add the G12B second CPU cluster CPU and SYS_PLL measure IDs. These IDs returns 0Hz on G12A. Reviewed-by: Kevin Hilman Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index c470e24f1dfa..f09b404b39d3 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -324,6 +324,8 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(84, "co_tx"), CLK_MSR_ID(89, "hdmi_todig"), CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "sys_cpub_div16"), + CLK_MSR_ID(92, "sys_pll_cpub_div16"), CLK_MSR_ID(94, "eth_phy_rx"), CLK_MSR_ID(95, "eth_phy_pll"), CLK_MSR_ID(96, "vpu_b"), From 0baf212eab4daf8e4882afd624e1403846418610 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Wed, 31 Jul 2019 14:39:55 +0200 Subject: [PATCH 3/9] soc: amlogic: meson-gx-socinfo: add A311D id Add the SoC ID for the A311D Amlogic SoC. Signed-off-by: Christian Hewitt Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index bca34954518e..ff86a75939e8 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -65,6 +65,7 @@ static const struct meson_gx_package_id { { "S905D2", 0x28, 0x10, 0xf0 }, { "S905X2", 0x28, 0x40, 0xf0 }, { "S922X", 0x29, 0x40, 0xf0 }, + { "A311D", 0x29, 0x10, 0xf0 }, }; static inline unsigned int socinfo_to_major(u32 socinfo) From c9cc9bec36d055077094db9fa57dfbf1870cbb73 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:47 +0200 Subject: [PATCH 4/9] soc: amlogic: meson-gx-socinfo: Add SM1 and S905X3 IDs Add the SoC IDs for the S905X3 Amlogic SM1 SoC. Reviewed-by: Martin Blumenstingl Reviewed-by: Jerome Brunet Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index ff86a75939e8..b9f4c6f4fd03 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -39,6 +39,7 @@ static const struct meson_gx_soc_id { { "TXHD", 0x27 }, { "G12A", 0x28 }, { "G12B", 0x29 }, + { "SM1", 0x2b }, }; static const struct meson_gx_package_id { @@ -66,6 +67,7 @@ static const struct meson_gx_package_id { { "S905X2", 0x28, 0x40, 0xf0 }, { "S922X", 0x29, 0x40, 0xf0 }, { "A311D", 0x29, 0x10, 0xf0 }, + { "S905X3", 0x2b, 0x5, 0xf }, }; static inline unsigned int socinfo_to_major(u32 socinfo) From b555cafb766a10846d228346aa71e517a5393c95 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:48 +0200 Subject: [PATCH 5/9] dt-bindings: soc: amlogic: clk-measure: Add SM1 compatible Add the Amlogic SM1 Compatible for the clk-measurer IP. Reviewed-by: Martin Blumenstingl Reviewed-by: Rob Herring Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt index 6bf6b43f8dd8..3dd563cec794 100644 --- a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt +++ b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt @@ -11,6 +11,7 @@ Required properties: "amlogic,meson8b-clk-measure" for Meson8b SoCs "amlogic,meson-axg-clk-measure" for AXG SoCs "amlogic,meson-g12a-clk-measure" for G12a SoCs + "amlogic,meson-sm1-clk-measure" for SM1 SoCs - reg: base address and size of the Clock Measurer register space. Example: From 623699711a39134a2b2cd6bdba81c5167deceec8 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:49 +0200 Subject: [PATCH 6/9] soc: amlogic: clk-measure: Add support for SM1 Add the clk-measurer clocks IDs for the Amlogic SM1 SoC family. Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 134 ++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index f09b404b39d3..0fa47d77577d 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -357,6 +357,136 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(122, "audio_pdm_dclk"), }; +static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = { + CLK_MSR_ID(0, "ring_osc_out_ee_0"), + CLK_MSR_ID(1, "ring_osc_out_ee_1"), + CLK_MSR_ID(2, "ring_osc_out_ee_2"), + CLK_MSR_ID(3, "ring_osc_out_ee_3"), + CLK_MSR_ID(4, "gp0_pll"), + CLK_MSR_ID(5, "gp1_pll"), + CLK_MSR_ID(6, "enci"), + CLK_MSR_ID(7, "clk81"), + CLK_MSR_ID(8, "encp"), + CLK_MSR_ID(9, "encl"), + CLK_MSR_ID(10, "vdac"), + CLK_MSR_ID(11, "eth_tx"), + CLK_MSR_ID(12, "hifi_pll"), + CLK_MSR_ID(13, "mod_tcon"), + CLK_MSR_ID(14, "fec_0"), + CLK_MSR_ID(15, "fec_1"), + CLK_MSR_ID(16, "fec_2"), + CLK_MSR_ID(17, "sys_pll_div16"), + CLK_MSR_ID(18, "sys_cpu_div16"), + CLK_MSR_ID(19, "lcd_an_ph2"), + CLK_MSR_ID(20, "rtc_osc_out"), + CLK_MSR_ID(21, "lcd_an_ph3"), + CLK_MSR_ID(22, "eth_phy_ref"), + CLK_MSR_ID(23, "mpll_50m"), + CLK_MSR_ID(24, "eth_125m"), + CLK_MSR_ID(25, "eth_rmii"), + CLK_MSR_ID(26, "sc_int"), + CLK_MSR_ID(27, "in_mac"), + CLK_MSR_ID(28, "sar_adc"), + CLK_MSR_ID(29, "pcie_inp"), + CLK_MSR_ID(30, "pcie_inn"), + CLK_MSR_ID(31, "mpll_test_out"), + CLK_MSR_ID(32, "vdec"), + CLK_MSR_ID(34, "eth_mpll_50m"), + CLK_MSR_ID(35, "mali"), + CLK_MSR_ID(36, "hdmi_tx_pixel"), + CLK_MSR_ID(37, "cdac"), + CLK_MSR_ID(38, "vdin_meas"), + CLK_MSR_ID(39, "bt656"), + CLK_MSR_ID(40, "arm_ring_osc_out_4"), + CLK_MSR_ID(41, "eth_rx_or_rmii"), + CLK_MSR_ID(42, "mp0_out"), + CLK_MSR_ID(43, "fclk_div5"), + CLK_MSR_ID(44, "pwm_b"), + CLK_MSR_ID(45, "pwm_a"), + CLK_MSR_ID(46, "vpu"), + CLK_MSR_ID(47, "ddr_dpll_pt"), + CLK_MSR_ID(48, "mp1_out"), + CLK_MSR_ID(49, "mp2_out"), + CLK_MSR_ID(50, "mp3_out"), + CLK_MSR_ID(51, "sd_emmc_c"), + CLK_MSR_ID(52, "sd_emmc_b"), + CLK_MSR_ID(53, "sd_emmc_a"), + CLK_MSR_ID(54, "vpu_clkc"), + CLK_MSR_ID(55, "vid_pll_div_out"), + CLK_MSR_ID(56, "wave420l_a"), + CLK_MSR_ID(57, "wave420l_c"), + CLK_MSR_ID(58, "wave420l_b"), + CLK_MSR_ID(59, "hcodec"), + CLK_MSR_ID(60, "arm_ring_osc_out_5"), + CLK_MSR_ID(61, "gpio_msr"), + CLK_MSR_ID(62, "hevcb"), + CLK_MSR_ID(63, "dsi_meas"), + CLK_MSR_ID(64, "spicc_1"), + CLK_MSR_ID(65, "spicc_0"), + CLK_MSR_ID(66, "vid_lock"), + CLK_MSR_ID(67, "dsi_phy"), + CLK_MSR_ID(68, "hdcp22_esm"), + CLK_MSR_ID(69, "hdcp22_skp"), + CLK_MSR_ID(70, "pwm_f"), + CLK_MSR_ID(71, "pwm_e"), + CLK_MSR_ID(72, "pwm_d"), + CLK_MSR_ID(73, "pwm_c"), + CLK_MSR_ID(74, "arm_ring_osc_out_6"), + CLK_MSR_ID(75, "hevcf"), + CLK_MSR_ID(76, "arm_ring_osc_out_7"), + CLK_MSR_ID(77, "rng_ring_osc_0"), + CLK_MSR_ID(78, "rng_ring_osc_1"), + CLK_MSR_ID(79, "rng_ring_osc_2"), + CLK_MSR_ID(80, "rng_ring_osc_3"), + CLK_MSR_ID(81, "vapb"), + CLK_MSR_ID(82, "ge2d"), + CLK_MSR_ID(83, "co_rx"), + CLK_MSR_ID(84, "co_tx"), + CLK_MSR_ID(85, "arm_ring_osc_out_8"), + CLK_MSR_ID(86, "arm_ring_osc_out_9"), + CLK_MSR_ID(87, "mipi_dsi_phy"), + CLK_MSR_ID(88, "cis2_adapt"), + CLK_MSR_ID(89, "hdmi_todig"), + CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "nna_core"), + CLK_MSR_ID(92, "nna_axi"), + CLK_MSR_ID(93, "vad"), + CLK_MSR_ID(94, "eth_phy_rx"), + CLK_MSR_ID(95, "eth_phy_pll"), + CLK_MSR_ID(96, "vpu_b"), + CLK_MSR_ID(97, "cpu_b_tmp"), + CLK_MSR_ID(98, "ts"), + CLK_MSR_ID(99, "arm_ring_osc_out_10"), + CLK_MSR_ID(100, "arm_ring_osc_out_11"), + CLK_MSR_ID(101, "arm_ring_osc_out_12"), + CLK_MSR_ID(102, "arm_ring_osc_out_13"), + CLK_MSR_ID(103, "arm_ring_osc_out_14"), + CLK_MSR_ID(104, "arm_ring_osc_out_15"), + CLK_MSR_ID(105, "arm_ring_osc_out_16"), + CLK_MSR_ID(106, "ephy_test"), + CLK_MSR_ID(107, "au_dac_g128x"), + CLK_MSR_ID(108, "audio_locker_out"), + CLK_MSR_ID(109, "audio_locker_in"), + CLK_MSR_ID(110, "audio_tdmout_c_sclk"), + CLK_MSR_ID(111, "audio_tdmout_b_sclk"), + CLK_MSR_ID(112, "audio_tdmout_a_sclk"), + CLK_MSR_ID(113, "audio_tdmin_lb_sclk"), + CLK_MSR_ID(114, "audio_tdmin_c_sclk"), + CLK_MSR_ID(115, "audio_tdmin_b_sclk"), + CLK_MSR_ID(116, "audio_tdmin_a_sclk"), + CLK_MSR_ID(117, "audio_resample"), + CLK_MSR_ID(118, "audio_pdm_sys"), + CLK_MSR_ID(119, "audio_spdifout_b"), + CLK_MSR_ID(120, "audio_spdifout"), + CLK_MSR_ID(121, "audio_spdifin"), + CLK_MSR_ID(122, "audio_pdm_dclk"), + CLK_MSR_ID(123, "audio_resampled"), + CLK_MSR_ID(124, "earcrx_pll"), + CLK_MSR_ID(125, "earcrx_pll_test"), + CLK_MSR_ID(126, "csi_phy0"), + CLK_MSR_ID(127, "csi2_data"), +}; + static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int duration) { @@ -545,6 +675,10 @@ static const struct of_device_id meson_msr_match_table[] = { .compatible = "amlogic,meson-g12a-clk-measure", .data = (void *)clk_msr_g12a, }, + { + .compatible = "amlogic,meson-sm1-clk-measure", + .data = (void *)clk_msr_sm1, + }, { /* sentinel */ } }; From 49ed86f503be80aac158a567c4cfd31cf1cd181e Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Mon, 19 Aug 2019 12:57:06 +0530 Subject: [PATCH 7/9] soc: amlogic: meson-gx-socinfo: Add of_node_put() before return The variable np in function meson_gx_socinfo_init takes the return value of of_find_compatible_node, which gets a node but does not put it. If this node is not put it may cause a memory leak. Hence put np after its usefulness has been exhausted. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Reviewed-by: Neil Armstrong Fixes: a9daaba2965e ("soc: Add Amlogic SoC Information driver") Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index b9f4c6f4fd03..6d0d04f163cb 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -141,8 +141,10 @@ static int __init meson_gx_socinfo_init(void) } /* check if chip-id is available */ - if (!of_property_read_bool(np, "amlogic,has-chip-id")) + if (!of_property_read_bool(np, "amlogic,has-chip-id")) { + of_node_put(np); return -ENODEV; + } /* node should be a syscon */ regmap = syscon_node_to_regmap(np); From eef3c2ba0a42a6aa709828e968b64bd11f4aeb19 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 23 Aug 2019 11:04:15 +0200 Subject: [PATCH 8/9] soc: amlogic: Add support for Everything-Else power domains controller Add support for the General Purpose Amlogic Everything-Else Power controller, with the first support for G12A and SM1 SoCs dedicated to the VPU, PCIe, USB, NNA, GE2D and Ethernet Power Domains. Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/Kconfig | 11 + drivers/soc/amlogic/Makefile | 1 + drivers/soc/amlogic/meson-ee-pwrc.c | 492 ++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 drivers/soc/amlogic/meson-ee-pwrc.c diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 23bfb8ef4fdb..bc2c912949bd 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -37,6 +37,17 @@ config MESON_GX_PM_DOMAINS Say yes to expose Amlogic Meson GX Power Domains as Generic Power Domains. +config MESON_EE_PM_DOMAINS + bool "Amlogic Meson Everything-Else Power Domains driver" + depends on ARCH_MESON || COMPILE_TEST + depends on PM && OF + default ARCH_MESON + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + Say yes to expose Amlogic Meson Everything-Else Power Domains as + Generic Power Domains. + config MESON_MX_SOCINFO bool "Amlogic Meson MX SoC Information driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index f2e4ed171297..de79d044b545 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o +obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o diff --git a/drivers/soc/amlogic/meson-ee-pwrc.c b/drivers/soc/amlogic/meson-ee-pwrc.c new file mode 100644 index 000000000000..5823f5b67d16 --- /dev/null +++ b/drivers/soc/amlogic/meson-ee-pwrc.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) +#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) +#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) +#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) +#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +struct meson_ee_pwrc; +struct meson_ee_pwrc_domain; + +struct meson_ee_pwrc_mem_domain { + unsigned int reg; + unsigned int mask; +}; + +struct meson_ee_pwrc_top_domain { + unsigned int sleep_reg; + unsigned int sleep_mask; + unsigned int iso_reg; + unsigned int iso_mask; +}; + +struct meson_ee_pwrc_domain_desc { + char *name; + unsigned int reset_names_count; + unsigned int clk_names_count; + struct meson_ee_pwrc_top_domain *top_pd; + unsigned int mem_pd_count; + struct meson_ee_pwrc_mem_domain *mem_pd; + bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain); +}; + +struct meson_ee_pwrc_domain_data { + unsigned int count; + struct meson_ee_pwrc_domain_desc *domains; +}; + +/* TOP Power Domains */ + +static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +#define SM1_EE_PD(__bit) \ + { \ + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_mask = BIT(__bit), \ + .iso_reg = AO_RTI_GEN_PWR_ISO0, \ + .iso_mask = BIT(__bit), \ + } + +static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); + +/* Memory PD Domains */ + +#define VPU_MEMPD(__reg) \ + { __reg, GENMASK(1, 0) }, \ + { __reg, GENMASK(3, 2) }, \ + { __reg, GENMASK(5, 4) }, \ + { __reg, GENMASK(7, 6) }, \ + { __reg, GENMASK(9, 8) }, \ + { __reg, GENMASK(11, 10) }, \ + { __reg, GENMASK(13, 12) }, \ + { __reg, GENMASK(15, 14) }, \ + { __reg, GENMASK(17, 16) }, \ + { __reg, GENMASK(19, 18) }, \ + { __reg, GENMASK(21, 20) }, \ + { __reg, GENMASK(23, 22) }, \ + { __reg, GENMASK(25, 24) }, \ + { __reg, GENMASK(27, 26) }, \ + { __reg, GENMASK(29, 28) }, \ + { __reg, GENMASK(31, 30) } + +#define VPU_HHI_MEMPD(__reg) \ + { __reg, BIT(8) }, \ + { __reg, BIT(9) }, \ + { __reg, BIT(10) }, \ + { __reg, BIT(11) }, \ + { __reg, BIT(12) }, \ + { __reg, BIT(13) }, \ + { __reg, BIT(14) }, \ + { __reg, BIT(15) } + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = { + { HHI_MEM_PD_REG0, GENMASK(3, 2) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_MEMPD(HHI_VPU_MEM_PD_REG3), + { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { + { HHI_NANOQ_MEM_PD_REG0, 0xff }, + { HHI_NANOQ_MEM_PD_REG1, 0xff }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { + { HHI_MEM_PD_REG0, GENMASK(31, 30) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { + { HHI_MEM_PD_REG0, GENMASK(29, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { + { HHI_MEM_PD_REG0, GENMASK(25, 18) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, +}; + +#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \ + { \ + .name = __name, \ + .reset_names_count = __resets, \ + .clk_names_count = __clks, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define TOP_PD(__name, __top_pd, __mem, __get_power) \ + { \ + .name = __name, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define MEM_PD(__name, __mem) \ + TOP_PD(__name, NULL, __mem, NULL) + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain); + +static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { + [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, + pwrc_ee_get_power), + [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, + pwrc_ee_get_power), + [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, + pwrc_ee_get_power), + [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, + pwrc_ee_get_power), + [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +struct meson_ee_pwrc_domain { + struct generic_pm_domain base; + bool enabled; + struct meson_ee_pwrc *pwrc; + struct meson_ee_pwrc_domain_desc desc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *rstc; + int num_rstc; +}; + +struct meson_ee_pwrc { + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct meson_ee_pwrc_domain *domains; + struct genpd_onecell_data xlate; +}; + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain) +{ + u32 reg; + + regmap_read(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, ®); + + return (reg & pwrc_domain->desc.top_pd->sleep_mask); +} + +static int meson_ee_pwrc_off(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, + pwrc_domain->desc.top_pd->sleep_mask); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, + pwrc_domain->desc.mem_pd[i].mask); + + udelay(20); + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, + pwrc_domain->desc.top_pd->iso_mask); + + if (pwrc_domain->num_clks) { + msleep(20); + clk_bulk_disable_unprepare(pwrc_domain->num_clks, + pwrc_domain->clks); + } + + return 0; +} + +static int meson_ee_pwrc_on(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i, ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, 0); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, 0); + + udelay(20); + + ret = reset_control_assert(pwrc_domain->rstc); + if (ret) + return ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, 0); + + ret = reset_control_deassert(pwrc_domain->rstc); + if (ret) + return ret; + + return clk_bulk_prepare_enable(pwrc_domain->num_clks, + pwrc_domain->clks); +} + +static int meson_ee_pwrc_init_domain(struct platform_device *pdev, + struct meson_ee_pwrc *pwrc, + struct meson_ee_pwrc_domain *dom) +{ + dom->pwrc = pwrc; + dom->num_rstc = dom->desc.reset_names_count; + dom->num_clks = dom->desc.clk_names_count; + + if (dom->num_rstc) { + int count = reset_control_get_count(&pdev->dev); + + if (count != dom->num_rstc) + dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n", + count, dom->desc.name); + + dom->rstc = devm_reset_control_array_get(&pdev->dev, false, + false); + if (IS_ERR(dom->rstc)) + return PTR_ERR(dom->rstc); + } + + if (dom->num_clks) { + int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks); + if (ret < 0) + return ret; + + if (dom->num_clks != ret) { + dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n", + ret, dom->desc.name); + dom->num_clks = ret; + } + } + + dom->base.name = dom->desc.name; + dom->base.power_on = meson_ee_pwrc_on; + dom->base.power_off = meson_ee_pwrc_off; + + /* + * TOFIX: This is a special case for the VPU power domain, which can + * be enabled previously by the bootloader. In this case the VPU + * pipeline may be functional but no driver maybe never attach + * to this power domain, and if the domain is disabled it could + * cause system errors. This is why the pm_domain_always_on_gov + * is used here. + * For the same reason, the clocks should be enabled in case + * we need to power the domain off, otherwise the internal clocks + * prepare/enable counters won't be in sync. + */ + if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) { + int ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks); + if (ret) + return ret; + + pm_genpd_init(&dom->base, &pm_domain_always_on_gov, false); + } else + pm_genpd_init(&dom->base, NULL, + (dom->desc.get_power ? + dom->desc.get_power(dom) : true)); + + return 0; +} + +static int meson_ee_pwrc_probe(struct platform_device *pdev) +{ + const struct meson_ee_pwrc_domain_data *match; + struct regmap *regmap_ao, *regmap_hhi; + struct meson_ee_pwrc *pwrc; + int i, ret; + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) + return -ENOMEM; + + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->xlate.domains), + GFP_KERNEL); + if (!pwrc->xlate.domains) + return -ENOMEM; + + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->domains), GFP_KERNEL); + if (!pwrc->domains) + return -ENOMEM; + + pwrc->xlate.num_domains = match->count; + + regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-sysctrl"); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get AO regmap\n"); + return PTR_ERR(regmap_ao); + } + + pwrc->regmap_ao = regmap_ao; + pwrc->regmap_hhi = regmap_hhi; + + platform_set_drvdata(pdev, pwrc); + + for (i = 0 ; i < match->count ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc)); + + ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom); + if (ret) + return ret; + + pwrc->xlate.domains[i] = &dom->base; + } + + of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); + + return 0; +} + +static void meson_ee_pwrc_shutdown(struct platform_device *pdev) +{ + struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev); + int i; + + for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + if (dom->desc.get_power && !dom->desc.get_power(dom)) + meson_ee_pwrc_off(&dom->base); + } +} + +static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { + .count = ARRAY_SIZE(g12a_pwrc_domains), + .domains = g12a_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { + .count = ARRAY_SIZE(sm1_pwrc_domains), + .domains = sm1_pwrc_domains, +}; + +static const struct of_device_id meson_ee_pwrc_match_table[] = { + { + .compatible = "amlogic,meson-g12a-pwrc", + .data = &meson_ee_g12a_pwrc_data, + }, + { + .compatible = "amlogic,meson-sm1-pwrc", + .data = &meson_ee_sm1_pwrc_data, + }, + { /* sentinel */ } +}; + +static struct platform_driver meson_ee_pwrc_driver = { + .probe = meson_ee_pwrc_probe, + .shutdown = meson_ee_pwrc_shutdown, + .driver = { + .name = "meson_ee_pwrc", + .of_match_table = meson_ee_pwrc_match_table, + }, +}; +builtin_platform_driver(meson_ee_pwrc_driver); From bd9eccf14008732f8ea4a7efc7839911e56053a1 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 23 Aug 2019 11:04:14 +0200 Subject: [PATCH 9/9] dt-bindings: power: add Amlogic Everything-Else power domains bindings Add the bindings for the Amlogic Everything-Else power domains, controlling the Everything-Else peripherals power domains. The bindings targets the Amlogic G12A and SM1 compatible SoCs, support for earlier SoCs will be added later. Signed-off-by: Neil Armstrong Reviewed-by: Rob Herring Signed-off-by: Kevin Hilman --- .../bindings/power/amlogic,meson-ee-pwrc.yaml | 93 +++++++++++++++++++ include/dt-bindings/power/meson-g12a-power.h | 13 +++ include/dt-bindings/power/meson-sm1-power.h | 18 ++++ 3 files changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml create mode 100644 include/dt-bindings/power/meson-g12a-power.h create mode 100644 include/dt-bindings/power/meson-sm1-power.h diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml new file mode 100644 index 000000000000..aab70e8b681e --- /dev/null +++ b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 BayLibre, SAS +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/amlogic,meson-ee-pwrc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic Meson Everything-Else Power Domains + +maintainers: + - Neil Armstrong + +description: |+ + The Everything-Else Power Domains node should be the child of a syscon + node with the required property: + + - compatible: Should be the following: + "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon" + + Refer to the the bindings described in + Documentation/devicetree/bindings/mfd/syscon.txt + +properties: + compatible: + enum: + - amlogic,meson-g12a-pwrc + - amlogic,meson-sm1-pwrc + + clocks: + minItems: 2 + + clock-names: + items: + - const: vpu + - const: vapb + + resets: + minItems: 11 + + reset-names: + items: + - const: viu + - const: venc + - const: vcbus + - const: bt656 + - const: rdma + - const: venci + - const: vencp + - const: vdac + - const: vdi6 + - const: vencl + - const: vid_lock + + "#power-domain-cells": + const: 1 + + amlogic,ao-sysctrl: + description: phandle to the AO sysctrl node + allOf: + - $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - clocks + - clock-names + - resets + - reset-names + - "#power-domain-cells" + - amlogic,ao-sysctrl + +examples: + - | + pwrc: power-controller { + compatible = "amlogic,meson-sm1-pwrc"; + #power-domain-cells = <1>; + amlogic,ao-sysctrl = <&rti>; + resets = <&reset_viu>, + <&reset_venc>, + <&reset_vcbus>, + <&reset_bt656>, + <&reset_rdma>, + <&reset_venci>, + <&reset_vencp>, + <&reset_vdac>, + <&reset_vdi6>, + <&reset_vencl>, + <&reset_vid_lock>; + reset-names = "viu", "venc", "vcbus", "bt656", + "rdma", "venci", "vencp", "vdac", + "vdi6", "vencl", "vid_lock"; + clocks = <&clk_vpu>, <&clk_vapb>; + clock-names = "vpu", "vapb"; + }; diff --git a/include/dt-bindings/power/meson-g12a-power.h b/include/dt-bindings/power/meson-g12a-power.h new file mode 100644 index 000000000000..bb5e67a842de --- /dev/null +++ b/include/dt-bindings/power/meson-g12a-power.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef _DT_BINDINGS_MESON_G12A_POWER_H +#define _DT_BINDINGS_MESON_G12A_POWER_H + +#define PWRC_G12A_VPU_ID 0 +#define PWRC_G12A_ETH_ID 1 + +#endif diff --git a/include/dt-bindings/power/meson-sm1-power.h b/include/dt-bindings/power/meson-sm1-power.h new file mode 100644 index 000000000000..a020ab00c134 --- /dev/null +++ b/include/dt-bindings/power/meson-sm1-power.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef _DT_BINDINGS_MESON_SM1_POWER_H +#define _DT_BINDINGS_MESON_SM1_POWER_H + +#define PWRC_SM1_VPU_ID 0 +#define PWRC_SM1_NNA_ID 1 +#define PWRC_SM1_USB_ID 2 +#define PWRC_SM1_PCIE_ID 3 +#define PWRC_SM1_GE2D_ID 4 +#define PWRC_SM1_AUDIO_ID 5 +#define PWRC_SM1_ETH_ID 6 + +#endif