meson-gx-pwrc-vpu.c (8768B)
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/of_device.h> 15#include <linux/reset.h> 16#include <linux/clk.h> 17#include <linux/module.h> 18 19/* AO Offsets */ 20 21#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) 22 23#define GEN_PWR_VPU_HDMI BIT(8) 24#define GEN_PWR_VPU_HDMI_ISO BIT(9) 25 26/* HHI Offsets */ 27 28#define HHI_MEM_PD_REG0 (0x40 << 2) 29#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) 30#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) 31#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) 32 33struct meson_gx_pwrc_vpu { 34 struct generic_pm_domain genpd; 35 struct regmap *regmap_ao; 36 struct regmap *regmap_hhi; 37 struct reset_control *rstc; 38 struct clk *vpu_clk; 39 struct clk *vapb_clk; 40}; 41 42static inline 43struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) 44{ 45 return container_of(d, struct meson_gx_pwrc_vpu, genpd); 46} 47 48static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) 49{ 50 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 51 int i; 52 53 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 54 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); 55 udelay(20); 56 57 /* Power Down Memories */ 58 for (i = 0; i < 32; i += 2) { 59 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 60 0x3 << i, 0x3 << i); 61 udelay(5); 62 } 63 for (i = 0; i < 32; i += 2) { 64 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 65 0x3 << i, 0x3 << i); 66 udelay(5); 67 } 68 for (i = 8; i < 16; i++) { 69 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 70 BIT(i), BIT(i)); 71 udelay(5); 72 } 73 udelay(20); 74 75 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 76 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); 77 78 msleep(20); 79 80 clk_disable_unprepare(pd->vpu_clk); 81 clk_disable_unprepare(pd->vapb_clk); 82 83 return 0; 84} 85 86static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) 87{ 88 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 89 int i; 90 91 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 92 GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); 93 udelay(20); 94 95 /* Power Down Memories */ 96 for (i = 0; i < 32; i += 2) { 97 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 98 0x3 << i, 0x3 << i); 99 udelay(5); 100 } 101 for (i = 0; i < 32; i += 2) { 102 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 103 0x3 << i, 0x3 << i); 104 udelay(5); 105 } 106 for (i = 0; i < 32; i += 2) { 107 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, 108 0x3 << i, 0x3 << i); 109 udelay(5); 110 } 111 for (i = 8; i < 16; i++) { 112 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 113 BIT(i), BIT(i)); 114 udelay(5); 115 } 116 udelay(20); 117 118 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 119 GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); 120 121 msleep(20); 122 123 clk_disable_unprepare(pd->vpu_clk); 124 clk_disable_unprepare(pd->vapb_clk); 125 126 return 0; 127} 128 129static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) 130{ 131 int ret; 132 133 ret = clk_prepare_enable(pd->vpu_clk); 134 if (ret) 135 return ret; 136 137 ret = clk_prepare_enable(pd->vapb_clk); 138 if (ret) 139 clk_disable_unprepare(pd->vpu_clk); 140 141 return ret; 142} 143 144static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) 145{ 146 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 147 int ret; 148 int i; 149 150 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 151 GEN_PWR_VPU_HDMI, 0); 152 udelay(20); 153 154 /* Power Up Memories */ 155 for (i = 0; i < 32; i += 2) { 156 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 157 0x3 << i, 0); 158 udelay(5); 159 } 160 161 for (i = 0; i < 32; i += 2) { 162 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 163 0x3 << i, 0); 164 udelay(5); 165 } 166 167 for (i = 8; i < 16; i++) { 168 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 169 BIT(i), 0); 170 udelay(5); 171 } 172 udelay(20); 173 174 ret = reset_control_assert(pd->rstc); 175 if (ret) 176 return ret; 177 178 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 179 GEN_PWR_VPU_HDMI_ISO, 0); 180 181 ret = reset_control_deassert(pd->rstc); 182 if (ret) 183 return ret; 184 185 ret = meson_gx_pwrc_vpu_setup_clk(pd); 186 if (ret) 187 return ret; 188 189 return 0; 190} 191 192static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) 193{ 194 struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); 195 int ret; 196 int i; 197 198 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 199 GEN_PWR_VPU_HDMI, 0); 200 udelay(20); 201 202 /* Power Up Memories */ 203 for (i = 0; i < 32; i += 2) { 204 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, 205 0x3 << i, 0); 206 udelay(5); 207 } 208 209 for (i = 0; i < 32; i += 2) { 210 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, 211 0x3 << i, 0); 212 udelay(5); 213 } 214 215 for (i = 0; i < 32; i += 2) { 216 regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, 217 0x3 << i, 0); 218 udelay(5); 219 } 220 221 for (i = 8; i < 16; i++) { 222 regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, 223 BIT(i), 0); 224 udelay(5); 225 } 226 udelay(20); 227 228 ret = reset_control_assert(pd->rstc); 229 if (ret) 230 return ret; 231 232 regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, 233 GEN_PWR_VPU_HDMI_ISO, 0); 234 235 ret = reset_control_deassert(pd->rstc); 236 if (ret) 237 return ret; 238 239 ret = meson_gx_pwrc_vpu_setup_clk(pd); 240 if (ret) 241 return ret; 242 243 return 0; 244} 245 246static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) 247{ 248 u32 reg; 249 250 regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); 251 252 return (reg & GEN_PWR_VPU_HDMI); 253} 254 255static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { 256 .genpd = { 257 .name = "vpu_hdmi", 258 .power_off = meson_gx_pwrc_vpu_power_off, 259 .power_on = meson_gx_pwrc_vpu_power_on, 260 }, 261}; 262 263static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { 264 .genpd = { 265 .name = "vpu_hdmi", 266 .power_off = meson_g12a_pwrc_vpu_power_off, 267 .power_on = meson_g12a_pwrc_vpu_power_on, 268 }, 269}; 270 271static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) 272{ 273 const struct meson_gx_pwrc_vpu *vpu_pd_match; 274 struct regmap *regmap_ao, *regmap_hhi; 275 struct meson_gx_pwrc_vpu *vpu_pd; 276 struct reset_control *rstc; 277 struct clk *vpu_clk; 278 struct clk *vapb_clk; 279 bool powered_off; 280 int ret; 281 282 vpu_pd_match = of_device_get_match_data(&pdev->dev); 283 if (!vpu_pd_match) { 284 dev_err(&pdev->dev, "failed to get match data\n"); 285 return -ENODEV; 286 } 287 288 vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); 289 if (!vpu_pd) 290 return -ENOMEM; 291 292 memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); 293 294 regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); 295 if (IS_ERR(regmap_ao)) { 296 dev_err(&pdev->dev, "failed to get regmap\n"); 297 return PTR_ERR(regmap_ao); 298 } 299 300 regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 301 "amlogic,hhi-sysctrl"); 302 if (IS_ERR(regmap_hhi)) { 303 dev_err(&pdev->dev, "failed to get HHI regmap\n"); 304 return PTR_ERR(regmap_hhi); 305 } 306 307 rstc = devm_reset_control_array_get_exclusive(&pdev->dev); 308 if (IS_ERR(rstc)) { 309 if (PTR_ERR(rstc) != -EPROBE_DEFER) 310 dev_err(&pdev->dev, "failed to get reset lines\n"); 311 return PTR_ERR(rstc); 312 } 313 314 vpu_clk = devm_clk_get(&pdev->dev, "vpu"); 315 if (IS_ERR(vpu_clk)) { 316 dev_err(&pdev->dev, "vpu clock request failed\n"); 317 return PTR_ERR(vpu_clk); 318 } 319 320 vapb_clk = devm_clk_get(&pdev->dev, "vapb"); 321 if (IS_ERR(vapb_clk)) { 322 dev_err(&pdev->dev, "vapb clock request failed\n"); 323 return PTR_ERR(vapb_clk); 324 } 325 326 vpu_pd->regmap_ao = regmap_ao; 327 vpu_pd->regmap_hhi = regmap_hhi; 328 vpu_pd->rstc = rstc; 329 vpu_pd->vpu_clk = vpu_clk; 330 vpu_pd->vapb_clk = vapb_clk; 331 332 platform_set_drvdata(pdev, vpu_pd); 333 334 powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); 335 336 /* If already powered, sync the clock states */ 337 if (!powered_off) { 338 ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); 339 if (ret) 340 return ret; 341 } 342 343 vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; 344 pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); 345 346 return of_genpd_add_provider_simple(pdev->dev.of_node, 347 &vpu_pd->genpd); 348} 349 350static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) 351{ 352 struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); 353 bool powered_off; 354 355 powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); 356 if (!powered_off) 357 vpu_pd->genpd.power_off(&vpu_pd->genpd); 358} 359 360static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { 361 { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, 362 { 363 .compatible = "amlogic,meson-g12a-pwrc-vpu", 364 .data = &vpu_hdmi_pd_g12a 365 }, 366 { /* sentinel */ } 367}; 368MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); 369 370static struct platform_driver meson_gx_pwrc_vpu_driver = { 371 .probe = meson_gx_pwrc_vpu_probe, 372 .shutdown = meson_gx_pwrc_vpu_shutdown, 373 .driver = { 374 .name = "meson_gx_pwrc_vpu", 375 .of_match_table = meson_gx_pwrc_vpu_match_table, 376 }, 377}; 378module_platform_driver(meson_gx_pwrc_vpu_driver); 379MODULE_LICENSE("GPL v2");