Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soc: amlogic: add Meson GX VPU Domains driver

The Video Processing Unit needs a specific Power Domain powering scheme
this driver handles this as a PM Power Domain driver.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Kevin Hilman <khilman@baylibre.com>

authored by

Neil Armstrong and committed by
Kevin Hilman
75fcb5ca 5e68c0fc

+245
+10
drivers/soc/amlogic/Kconfig
··· 9 9 Say yes to support decoding of Amlogic Meson GX SoC family 10 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 22 config MESON_MX_SOCINFO 13 23 bool "Amlogic Meson MX SoC Information driver" 14 24 depends on ARCH_MESON || COMPILE_TEST
+1
drivers/soc/amlogic/Makefile
··· 1 1 obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o 2 + obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o 2 3 obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
+234
drivers/soc/amlogic/meson-gx-pwrc-vpu.c
··· 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, &reg); 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);