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

Configure Feed

Select the types of activity you want to include in your feed.

at v4.17-rc3 248 lines 5.7 kB view raw
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 30struct 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}; 38 39static inline 40struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) 41{ 42 return container_of(d, struct meson_gx_pwrc_vpu, genpd); 43} 44 45static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) 46{ 47 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 48 int i; 49 50 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 51 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); 52 udelay(20); 53 54 /* Power Down Memories */ 55 for (i = 0; i < 32; i += 2) { 56 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 57 0x2 << i, 0x3 << i); 58 udelay(5); 59 } 60 for (i = 0; i < 32; i += 2) { 61 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 62 0x2 << i, 0x3 << i); 63 udelay(5); 64 } 65 for (i = 8; i < 16; i++) { 66 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 67 BIT(i), BIT(i)); 68 udelay(5); 69 } 70 udelay(20); 71 72 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 73 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); 74 75 msleep(20); 76 77 clk_disable_unprepare(pd->vpu_clk); 78 clk_disable_unprepare(pd->vapb_clk); 79 80 return 0; 81} 82 83static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) 84{ 85 int ret; 86 87 ret = clk_prepare_enable(pd->vpu_clk); 88 if (ret) 89 return ret; 90 91 ret = clk_prepare_enable(pd->vapb_clk); 92 if (ret) 93 clk_disable_unprepare(pd->vpu_clk); 94 95 return ret; 96} 97 98static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) 99{ 100 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 101 int ret; 102 int i; 103 104 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 105 GEN_PWR_VPU_HDMI, 0); 106 udelay(20); 107 108 /* Power Up Memories */ 109 for (i = 0; i < 32; i += 2) { 110 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 111 0x2 << i, 0); 112 udelay(5); 113 } 114 115 for (i = 0; i < 32; i += 2) { 116 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 117 0x2 << i, 0); 118 udelay(5); 119 } 120 121 for (i = 8; i < 16; i++) { 122 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 123 BIT(i), 0); 124 udelay(5); 125 } 126 udelay(20); 127 128 ret = reset_control_assert(pd->rstc); 129 if (ret) 130 return ret; 131 132 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 133 GEN_PWR_VPU_HDMI_ISO, 0); 134 135 ret = reset_control_deassert(pd->rstc); 136 if (ret) 137 return ret; 138 139 ret = meson_gx_pwrc_vpu_setup_clk(pd); 140 if (ret) 141 return ret; 142 143 return 0; 144} 145 146static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) 147{ 148 u32 reg; 149 150 regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg); 151 152 return (reg & GEN_PWR_VPU_HDMI); 153} 154 155static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { 156 .genpd = { 157 .name = "vpu_hdmi", 158 .power_off = meson_gx_pwrc_vpu_power_off, 159 .power_on = meson_gx_pwrc_vpu_power_on, 160 }, 161}; 162 163static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) 164{ 165 struct regmap *regmap_ao, *regmap_hhi; 166 struct reset_control *rstc; 167 struct clk *vpu_clk; 168 struct clk *vapb_clk; 169 bool powered_off; 170 int ret; 171 172 regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); 173 if (IS_ERR(regmap_ao)) { 174 dev_err(&pdev->dev, "failed to get regmap\n"); 175 return PTR_ERR(regmap_ao); 176 } 177 178 regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 179 "amlogic,hhi-sysctrl"); 180 if (IS_ERR(regmap_hhi)) { 181 dev_err(&pdev->dev, "failed to get HHI regmap\n"); 182 return PTR_ERR(regmap_hhi); 183 } 184 185 rstc = devm_reset_control_array_get(&pdev->dev, false, false); 186 if (IS_ERR(rstc)) { 187 if (PTR_ERR(rstc) != -EPROBE_DEFER) 188 dev_err(&pdev->dev, "failed to get reset lines\n"); 189 return PTR_ERR(rstc); 190 } 191 192 vpu_clk = devm_clk_get(&pdev->dev, "vpu"); 193 if (IS_ERR(vpu_clk)) { 194 dev_err(&pdev->dev, "vpu clock request failed\n"); 195 return PTR_ERR(vpu_clk); 196 } 197 198 vapb_clk = devm_clk_get(&pdev->dev, "vapb"); 199 if (IS_ERR(vapb_clk)) { 200 dev_err(&pdev->dev, "vapb clock request failed\n"); 201 return PTR_ERR(vapb_clk); 202 } 203 204 vpu_hdmi_pd.regmap_ao = regmap_ao; 205 vpu_hdmi_pd.regmap_hhi = regmap_hhi; 206 vpu_hdmi_pd.rstc = rstc; 207 vpu_hdmi_pd.vpu_clk = vpu_clk; 208 vpu_hdmi_pd.vapb_clk = vapb_clk; 209 210 powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); 211 212 /* If already powered, sync the clock states */ 213 if (!powered_off) { 214 ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd); 215 if (ret) 216 return ret; 217 } 218 219 pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov, 220 powered_off); 221 222 return of_genpd_add_provider_simple(pdev->dev.of_node, 223 &vpu_hdmi_pd.genpd); 224} 225 226static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) 227{ 228 bool powered_off; 229 230 powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); 231 if (!powered_off) 232 meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); 233} 234 235static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { 236 { .compatible = "amlogic,meson-gx-pwrc-vpu" }, 237 { /* sentinel */ } 238}; 239 240static struct platform_driver meson_gx_pwrc_vpu_driver = { 241 .probe = meson_gx_pwrc_vpu_probe, 242 .shutdown = meson_gx_pwrc_vpu_shutdown, 243 .driver = { 244 .name = "meson_gx_pwrc_vpu", 245 .of_match_table = meson_gx_pwrc_vpu_match_table, 246 }, 247}; 248builtin_platform_driver(meson_gx_pwrc_vpu_driver);