diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2017-11-02 11:36:23 -0400 |
|---|---|---|
| committer | Arnd Bergmann <arnd@arndb.de> | 2017-11-02 11:36:23 -0400 |
| commit | e54a7867f3c6a631ccfdc6db86f33a2fac4e103e (patch) | |
| tree | 765fb101f37e591978952d9bffda791f3c7af485 | |
| parent | 51d3290c3ad6ba691bd66df8ead6e7c19dc995f1 (diff) | |
| parent | 8fca9e95243128961357577ba394ec1d4bd8c92c (diff) | |
Merge tag 'amlogic-drivers-2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic into next/drivers
Pull "Amlogic drivers for v4.15, round 2" from Kevin Hilman:
- add PM domain driver for GX VPU
* tag 'amlogic-drivers-2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic:
dt-bindings: power: add amlogic meson power domain bindings
soc: amlogic: add Meson GX VPU Domains driver
| -rw-r--r-- | Documentation/devicetree/bindings/power/amlogic,meson-gx-pwrc.txt | 61 | ||||
| -rw-r--r-- | drivers/soc/amlogic/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/soc/amlogic/Makefile | 1 | ||||
| -rw-r--r-- | drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 234 |
4 files changed, 306 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-gx-pwrc.txt b/Documentation/devicetree/bindings/power/amlogic,meson-gx-pwrc.txt new file mode 100644 index 000000000000..1cd050b4054c --- /dev/null +++ b/Documentation/devicetree/bindings/power/amlogic,meson-gx-pwrc.txt | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | Amlogic Meson Power Controller | ||
| 2 | ============================== | ||
| 3 | |||
| 4 | The Amlogic Meson SoCs embeds an internal Power domain controller. | ||
| 5 | |||
| 6 | VPU Power Domain | ||
| 7 | ---------------- | ||
| 8 | |||
| 9 | The Video Processing Unit power domain is controlled by this power controller, | ||
| 10 | but the domain requires some external resources to meet the correct power | ||
| 11 | sequences. | ||
| 12 | The bindings must respect the power domain bindings as described in the file | ||
| 13 | power_domain.txt | ||
| 14 | |||
| 15 | Device Tree Bindings: | ||
| 16 | --------------------- | ||
| 17 | |||
| 18 | Required properties: | ||
| 19 | - compatible: should be "amlogic,meson-gx-pwrc-vpu" for the Meson GX SoCs | ||
| 20 | - #power-domain-cells: should be 0 | ||
| 21 | - amlogic,hhi-sysctrl: phandle to the HHI sysctrl node | ||
| 22 | - resets: phandles to the reset lines needed for this power demain sequence | ||
| 23 | as described in ../reset/reset.txt | ||
| 24 | - clocks: from common clock binding: handle to VPU and VAPB clocks | ||
| 25 | - clock-names: from common clock binding: must contain "vpu", "vapb" | ||
| 26 | corresponding to entry in the clocks property. | ||
| 27 | |||
| 28 | Parent node should have the following properties : | ||
| 29 | - compatible: "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd" | ||
| 30 | - reg: base address and size of the AO system control register space. | ||
| 31 | |||
| 32 | Example: | ||
| 33 | ------- | ||
| 34 | |||
| 35 | ao_sysctrl: sys-ctrl@0 { | ||
| 36 | compatible = "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd"; | ||
| 37 | reg = <0x0 0x0 0x0 0x100>; | ||
| 38 | |||
| 39 | pwrc_vpu: power-controller-vpu { | ||
| 40 | compatible = "amlogic,meson-gx-pwrc-vpu"; | ||
| 41 | #power-domain-cells = <0>; | ||
| 42 | amlogic,hhi-sysctrl = <&sysctrl>; | ||
| 43 | resets = <&reset RESET_VIU>, | ||
| 44 | <&reset RESET_VENC>, | ||
| 45 | <&reset RESET_VCBUS>, | ||
| 46 | <&reset RESET_BT656>, | ||
| 47 | <&reset RESET_DVIN_RESET>, | ||
| 48 | <&reset RESET_RDMA>, | ||
| 49 | <&reset RESET_VENCI>, | ||
| 50 | <&reset RESET_VENCP>, | ||
| 51 | <&reset RESET_VDAC>, | ||
| 52 | <&reset RESET_VDI6>, | ||
| 53 | <&reset RESET_VENCL>, | ||
| 54 | <&reset RESET_VID_LOCK>; | ||
| 55 | clocks = <&clkc CLKID_VPU>, | ||
| 56 | <&clkc CLKID_VAPB>; | ||
| 57 | clock-names = "vpu", "vapb"; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | |||
| 61 | |||
diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index ef0b8f6c53e0..1621bb25a1ea 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig | |||
| @@ -9,6 +9,16 @@ config MESON_GX_SOCINFO | |||
| 9 | Say yes to support decoding of Amlogic Meson GX SoC family | 9 | Say yes to support decoding of Amlogic Meson GX SoC family |
| 10 | information about the type, package and version. | 10 | information about the type, package and version. |
| 11 | 11 | ||
| 12 | config MESON_GX_PM_DOMAINS | ||
| 13 | bool "Amlogic Meson GX Power Domains driver" | ||
| 14 | depends on ARCH_MESON || COMPILE_TEST | ||
| 15 | default ARCH_MESON | ||
| 16 | select PM_GENERIC_DOMAINS | ||
| 17 | select PM_GENERIC_DOMAINS_OF | ||
| 18 | help | ||
| 19 | Say yes to expose Amlogic Meson GX Power Domains as | ||
| 20 | Generic Power Domains. | ||
| 21 | |||
| 12 | config MESON_MX_SOCINFO | 22 | config MESON_MX_SOCINFO |
| 13 | bool "Amlogic Meson MX SoC Information driver" | 23 | bool "Amlogic Meson MX SoC Information driver" |
| 14 | depends on ARCH_MESON || COMPILE_TEST | 24 | depends on ARCH_MESON || COMPILE_TEST |
diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index 1f5df50ebd73..8fa321893928 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o | 1 | obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o |
| 2 | obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o | ||
| 2 | obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o | 3 | obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o |
diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c new file mode 100644 index 000000000000..bf5190b65ad9 --- /dev/null +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2017 BayLibre, SAS | ||
| 3 | * Author: Neil Armstrong <narmstrong@baylibre.com> | ||
| 4 | * | ||
| 5 | * SPDX-License-Identifier: GPL-2.0+ | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/of_address.h> | ||
| 9 | #include <linux/platform_device.h> | ||
| 10 | #include <linux/pm_domain.h> | ||
| 11 | #include <linux/bitfield.h> | ||
| 12 | #include <linux/regmap.h> | ||
| 13 | #include <linux/mfd/syscon.h> | ||
| 14 | #include <linux/reset.h> | ||
| 15 | #include <linux/clk.h> | ||
| 16 | |||
| 17 | /* AO Offsets */ | ||
| 18 | |||
| 19 | #define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) | ||
| 20 | |||
| 21 | #define GEN_PWR_VPU_HDMI BIT(8) | ||
| 22 | #define GEN_PWR_VPU_HDMI_ISO BIT(9) | ||
| 23 | |||
| 24 | /* HHI Offsets */ | ||
| 25 | |||
| 26 | #define HHI_MEM_PD_REG0 (0x40 << 2) | ||
| 27 | #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) | ||
| 28 | #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) | ||
| 29 | |||
| 30 | struct meson_gx_pwrc_vpu { | ||
| 31 | struct generic_pm_domain genpd; | ||
| 32 | struct regmap *regmap_ao; | ||
| 33 | struct regmap *regmap_hhi; | ||
| 34 | struct reset_control *rstc; | ||
| 35 | struct clk *vpu_clk; | ||
| 36 | struct clk *vapb_clk; | ||
| 37 | bool powered; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static inline | ||
| 41 | struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) | ||
| 42 | { | ||
| 43 | return container_of(d, struct meson_gx_pwrc_vpu, genpd); | ||
| 44 | } | ||
| 45 | |||
| 46 | static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) | ||
| 47 | { | ||
| 48 | struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); | ||
| 49 | int i; | ||
| 50 | |||
| 51 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 52 | GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); | ||
| 53 | udelay(20); | ||
| 54 | |||
| 55 | /* Power Down Memories */ | ||
| 56 | for (i = 0; i < 32; i += 2) { | ||
| 57 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, | ||
| 58 | 0x2 << i, 0x3 << i); | ||
| 59 | udelay(5); | ||
| 60 | } | ||
| 61 | for (i = 0; i < 32; i += 2) { | ||
| 62 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, | ||
| 63 | 0x2 << i, 0x3 << i); | ||
| 64 | udelay(5); | ||
| 65 | } | ||
| 66 | for (i = 8; i < 16; i++) { | ||
| 67 | regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, | ||
| 68 | BIT(i), BIT(i)); | ||
| 69 | udelay(5); | ||
| 70 | } | ||
| 71 | udelay(20); | ||
| 72 | |||
| 73 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 74 | GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); | ||
| 75 | |||
| 76 | msleep(20); | ||
| 77 | |||
| 78 | clk_disable_unprepare(pd->vpu_clk); | ||
| 79 | clk_disable_unprepare(pd->vapb_clk); | ||
| 80 | |||
| 81 | pd->powered = false; | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) | ||
| 87 | { | ||
| 88 | int ret; | ||
| 89 | |||
| 90 | ret = clk_prepare_enable(pd->vpu_clk); | ||
| 91 | if (ret) | ||
| 92 | return ret; | ||
| 93 | |||
| 94 | return clk_prepare_enable(pd->vapb_clk); | ||
| 95 | } | ||
| 96 | |||
| 97 | static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) | ||
| 98 | { | ||
| 99 | struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); | ||
| 100 | int ret; | ||
| 101 | int i; | ||
| 102 | |||
| 103 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 104 | GEN_PWR_VPU_HDMI, 0); | ||
| 105 | udelay(20); | ||
| 106 | |||
| 107 | /* Power Up Memories */ | ||
| 108 | for (i = 0; i < 32; i += 2) { | ||
| 109 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, | ||
| 110 | 0x2 << i, 0); | ||
| 111 | udelay(5); | ||
| 112 | } | ||
| 113 | |||
| 114 | for (i = 0; i < 32; i += 2) { | ||
| 115 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, | ||
| 116 | 0x2 << i, 0); | ||
| 117 | udelay(5); | ||
| 118 | } | ||
| 119 | |||
| 120 | for (i = 8; i < 16; i++) { | ||
| 121 | regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, | ||
| 122 | BIT(i), 0); | ||
| 123 | udelay(5); | ||
| 124 | } | ||
| 125 | udelay(20); | ||
| 126 | |||
| 127 | ret = reset_control_assert(pd->rstc); | ||
| 128 | if (ret) | ||
| 129 | return ret; | ||
| 130 | |||
| 131 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 132 | GEN_PWR_VPU_HDMI_ISO, 0); | ||
| 133 | |||
| 134 | ret = reset_control_deassert(pd->rstc); | ||
| 135 | if (ret) | ||
| 136 | return ret; | ||
| 137 | |||
| 138 | ret = meson_gx_pwrc_vpu_setup_clk(pd); | ||
| 139 | if (ret) | ||
| 140 | return ret; | ||
| 141 | |||
| 142 | pd->powered = true; | ||
| 143 | |||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) | ||
| 148 | { | ||
| 149 | u32 reg; | ||
| 150 | |||
| 151 | regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); | ||
| 152 | |||
| 153 | return (reg & GEN_PWR_VPU_HDMI); | ||
| 154 | } | ||
| 155 | |||
| 156 | static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { | ||
| 157 | .genpd = { | ||
| 158 | .name = "vpu_hdmi", | ||
| 159 | .power_off = meson_gx_pwrc_vpu_power_off, | ||
| 160 | .power_on = meson_gx_pwrc_vpu_power_on, | ||
| 161 | }, | ||
| 162 | }; | ||
| 163 | |||
| 164 | static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) | ||
| 165 | { | ||
| 166 | struct regmap *regmap_ao, *regmap_hhi; | ||
| 167 | struct reset_control *rstc; | ||
| 168 | struct clk *vpu_clk; | ||
| 169 | struct clk *vapb_clk; | ||
| 170 | |||
| 171 | regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); | ||
| 172 | if (IS_ERR(regmap_ao)) { | ||
| 173 | dev_err(&pdev->dev, "failed to get regmap\n"); | ||
| 174 | return PTR_ERR(regmap_ao); | ||
| 175 | } | ||
| 176 | |||
| 177 | regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | ||
| 178 | "amlogic,hhi-sysctrl"); | ||
| 179 | if (IS_ERR(regmap_hhi)) { | ||
| 180 | dev_err(&pdev->dev, "failed to get HHI regmap\n"); | ||
| 181 | return PTR_ERR(regmap_hhi); | ||
| 182 | } | ||
| 183 | |||
| 184 | rstc = devm_reset_control_array_get(&pdev->dev, false, false); | ||
| 185 | if (IS_ERR(rstc)) { | ||
| 186 | dev_err(&pdev->dev, "failed to get reset lines\n"); | ||
| 187 | return PTR_ERR(rstc); | ||
| 188 | } | ||
| 189 | |||
| 190 | vpu_clk = devm_clk_get(&pdev->dev, "vpu"); | ||
| 191 | if (IS_ERR(vpu_clk)) { | ||
| 192 | dev_err(&pdev->dev, "vpu clock request failed\n"); | ||
| 193 | return PTR_ERR(vpu_clk); | ||
| 194 | } | ||
| 195 | |||
| 196 | vapb_clk = devm_clk_get(&pdev->dev, "vapb"); | ||
| 197 | if (IS_ERR(vapb_clk)) { | ||
| 198 | dev_err(&pdev->dev, "vapb clock request failed\n"); | ||
| 199 | return PTR_ERR(vapb_clk); | ||
| 200 | } | ||
| 201 | |||
| 202 | vpu_hdmi_pd.regmap_ao = regmap_ao; | ||
| 203 | vpu_hdmi_pd.regmap_hhi = regmap_hhi; | ||
| 204 | vpu_hdmi_pd.rstc = rstc; | ||
| 205 | vpu_hdmi_pd.vpu_clk = vpu_clk; | ||
| 206 | vpu_hdmi_pd.vapb_clk = vapb_clk; | ||
| 207 | |||
| 208 | pm_genpd_init(&vpu_hdmi_pd.genpd, &simple_qos_governor, | ||
| 209 | meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd)); | ||
| 210 | |||
| 211 | return of_genpd_add_provider_simple(pdev->dev.of_node, | ||
| 212 | &vpu_hdmi_pd.genpd); | ||
| 213 | } | ||
| 214 | |||
| 215 | static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) | ||
| 216 | { | ||
| 217 | if (vpu_hdmi_pd.powered) | ||
| 218 | meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); | ||
| 219 | } | ||
| 220 | |||
| 221 | static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { | ||
| 222 | { .compatible = "amlogic,meson-gx-pwrc-vpu" }, | ||
| 223 | { /* sentinel */ } | ||
| 224 | }; | ||
| 225 | |||
| 226 | static struct platform_driver meson_gx_pwrc_vpu_driver = { | ||
| 227 | .probe = meson_gx_pwrc_vpu_probe, | ||
| 228 | .shutdown = meson_gx_pwrc_vpu_shutdown, | ||
| 229 | .driver = { | ||
| 230 | .name = "meson_gx_pwrc_vpu", | ||
| 231 | .of_match_table = meson_gx_pwrc_vpu_match_table, | ||
| 232 | }, | ||
| 233 | }; | ||
| 234 | builtin_platform_driver(meson_gx_pwrc_vpu_driver); | ||
