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 v6.4 380 lines 8.8 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/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, &reg); 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 device_node *parent_np; 277 struct reset_control *rstc; 278 struct clk *vpu_clk; 279 struct clk *vapb_clk; 280 bool powered_off; 281 int ret; 282 283 vpu_pd_match = of_device_get_match_data(&pdev->dev); 284 if (!vpu_pd_match) { 285 dev_err(&pdev->dev, "failed to get match data\n"); 286 return -ENODEV; 287 } 288 289 vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); 290 if (!vpu_pd) 291 return -ENOMEM; 292 293 memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); 294 295 parent_np = of_get_parent(pdev->dev.of_node); 296 regmap_ao = syscon_node_to_regmap(parent_np); 297 of_node_put(parent_np); 298 if (IS_ERR(regmap_ao)) { 299 dev_err(&pdev->dev, "failed to get regmap\n"); 300 return PTR_ERR(regmap_ao); 301 } 302 303 regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 304 "amlogic,hhi-sysctrl"); 305 if (IS_ERR(regmap_hhi)) { 306 dev_err(&pdev->dev, "failed to get HHI regmap\n"); 307 return PTR_ERR(regmap_hhi); 308 } 309 310 rstc = devm_reset_control_array_get_exclusive(&pdev->dev); 311 if (IS_ERR(rstc)) 312 return dev_err_probe(&pdev->dev, PTR_ERR(rstc), 313 "failed to get reset lines\n"); 314 315 vpu_clk = devm_clk_get(&pdev->dev, "vpu"); 316 if (IS_ERR(vpu_clk)) { 317 dev_err(&pdev->dev, "vpu clock request failed\n"); 318 return PTR_ERR(vpu_clk); 319 } 320 321 vapb_clk = devm_clk_get(&pdev->dev, "vapb"); 322 if (IS_ERR(vapb_clk)) { 323 dev_err(&pdev->dev, "vapb clock request failed\n"); 324 return PTR_ERR(vapb_clk); 325 } 326 327 vpu_pd->regmap_ao = regmap_ao; 328 vpu_pd->regmap_hhi = regmap_hhi; 329 vpu_pd->rstc = rstc; 330 vpu_pd->vpu_clk = vpu_clk; 331 vpu_pd->vapb_clk = vapb_clk; 332 333 platform_set_drvdata(pdev, vpu_pd); 334 335 powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); 336 337 /* If already powered, sync the clock states */ 338 if (!powered_off) { 339 ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); 340 if (ret) 341 return ret; 342 } 343 344 vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; 345 pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); 346 347 return of_genpd_add_provider_simple(pdev->dev.of_node, 348 &vpu_pd->genpd); 349} 350 351static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) 352{ 353 struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); 354 bool powered_off; 355 356 powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); 357 if (!powered_off) 358 vpu_pd->genpd.power_off(&vpu_pd->genpd); 359} 360 361static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { 362 { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, 363 { 364 .compatible = "amlogic,meson-g12a-pwrc-vpu", 365 .data = &vpu_hdmi_pd_g12a 366 }, 367 { /* sentinel */ } 368}; 369MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); 370 371static struct platform_driver meson_gx_pwrc_vpu_driver = { 372 .probe = meson_gx_pwrc_vpu_probe, 373 .shutdown = meson_gx_pwrc_vpu_shutdown, 374 .driver = { 375 .name = "meson_gx_pwrc_vpu", 376 .of_match_table = meson_gx_pwrc_vpu_match_table, 377 }, 378}; 379module_platform_driver(meson_gx_pwrc_vpu_driver); 380MODULE_LICENSE("GPL v2");