| .. | .. |
|---|
| 11 | 11 | #include <linux/bitfield.h> |
|---|
| 12 | 12 | #include <linux/regmap.h> |
|---|
| 13 | 13 | #include <linux/mfd/syscon.h> |
|---|
| 14 | +#include <linux/of_device.h> |
|---|
| 14 | 15 | #include <linux/reset.h> |
|---|
| 15 | 16 | #include <linux/clk.h> |
|---|
| 17 | +#include <linux/module.h> |
|---|
| 16 | 18 | |
|---|
| 17 | 19 | /* AO Offsets */ |
|---|
| 18 | 20 | |
|---|
| .. | .. |
|---|
| 26 | 28 | #define HHI_MEM_PD_REG0 (0x40 << 2) |
|---|
| 27 | 29 | #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) |
|---|
| 28 | 30 | #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) |
|---|
| 31 | +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) |
|---|
| 29 | 32 | |
|---|
| 30 | 33 | struct meson_gx_pwrc_vpu { |
|---|
| 31 | 34 | struct generic_pm_domain genpd; |
|---|
| .. | .. |
|---|
| 59 | 62 | } |
|---|
| 60 | 63 | for (i = 0; i < 32; i += 2) { |
|---|
| 61 | 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 | + |
|---|
| 86 | +static 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, |
|---|
| 62 | 108 | 0x3 << i, 0x3 << i); |
|---|
| 63 | 109 | udelay(5); |
|---|
| 64 | 110 | } |
|---|
| .. | .. |
|---|
| 143 | 189 | return 0; |
|---|
| 144 | 190 | } |
|---|
| 145 | 191 | |
|---|
| 192 | +static 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 | + |
|---|
| 146 | 246 | static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) |
|---|
| 147 | 247 | { |
|---|
| 148 | 248 | u32 reg; |
|---|
| .. | .. |
|---|
| 160 | 260 | }, |
|---|
| 161 | 261 | }; |
|---|
| 162 | 262 | |
|---|
| 263 | +static 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 | + |
|---|
| 163 | 271 | static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) |
|---|
| 164 | 272 | { |
|---|
| 273 | + const struct meson_gx_pwrc_vpu *vpu_pd_match; |
|---|
| 165 | 274 | struct regmap *regmap_ao, *regmap_hhi; |
|---|
| 275 | + struct meson_gx_pwrc_vpu *vpu_pd; |
|---|
| 166 | 276 | struct reset_control *rstc; |
|---|
| 167 | 277 | struct clk *vpu_clk; |
|---|
| 168 | 278 | struct clk *vapb_clk; |
|---|
| 169 | 279 | bool powered_off; |
|---|
| 170 | 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)); |
|---|
| 171 | 293 | |
|---|
| 172 | 294 | regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); |
|---|
| 173 | 295 | if (IS_ERR(regmap_ao)) { |
|---|
| .. | .. |
|---|
| 201 | 323 | return PTR_ERR(vapb_clk); |
|---|
| 202 | 324 | } |
|---|
| 203 | 325 | |
|---|
| 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; |
|---|
| 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; |
|---|
| 209 | 331 | |
|---|
| 210 | | - powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); |
|---|
| 332 | + platform_set_drvdata(pdev, vpu_pd); |
|---|
| 333 | + |
|---|
| 334 | + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); |
|---|
| 211 | 335 | |
|---|
| 212 | 336 | /* If already powered, sync the clock states */ |
|---|
| 213 | 337 | if (!powered_off) { |
|---|
| 214 | | - ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd); |
|---|
| 338 | + ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); |
|---|
| 215 | 339 | if (ret) |
|---|
| 216 | 340 | return ret; |
|---|
| 217 | 341 | } |
|---|
| 218 | 342 | |
|---|
| 219 | | - pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov, |
|---|
| 220 | | - powered_off); |
|---|
| 343 | + vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; |
|---|
| 344 | + pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); |
|---|
| 221 | 345 | |
|---|
| 222 | 346 | return of_genpd_add_provider_simple(pdev->dev.of_node, |
|---|
| 223 | | - &vpu_hdmi_pd.genpd); |
|---|
| 347 | + &vpu_pd->genpd); |
|---|
| 224 | 348 | } |
|---|
| 225 | 349 | |
|---|
| 226 | 350 | static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) |
|---|
| 227 | 351 | { |
|---|
| 352 | + struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); |
|---|
| 228 | 353 | bool powered_off; |
|---|
| 229 | 354 | |
|---|
| 230 | | - powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); |
|---|
| 355 | + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); |
|---|
| 231 | 356 | if (!powered_off) |
|---|
| 232 | | - meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); |
|---|
| 357 | + vpu_pd->genpd.power_off(&vpu_pd->genpd); |
|---|
| 233 | 358 | } |
|---|
| 234 | 359 | |
|---|
| 235 | 360 | static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { |
|---|
| 236 | | - { .compatible = "amlogic,meson-gx-pwrc-vpu" }, |
|---|
| 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 | + }, |
|---|
| 237 | 366 | { /* sentinel */ } |
|---|
| 238 | 367 | }; |
|---|
| 368 | +MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); |
|---|
| 239 | 369 | |
|---|
| 240 | 370 | static struct platform_driver meson_gx_pwrc_vpu_driver = { |
|---|
| 241 | 371 | .probe = meson_gx_pwrc_vpu_probe, |
|---|
| .. | .. |
|---|
| 245 | 375 | .of_match_table = meson_gx_pwrc_vpu_match_table, |
|---|
| 246 | 376 | }, |
|---|
| 247 | 377 | }; |
|---|
| 248 | | -builtin_platform_driver(meson_gx_pwrc_vpu_driver); |
|---|
| 378 | +module_platform_driver(meson_gx_pwrc_vpu_driver); |
|---|
| 379 | +MODULE_LICENSE("GPL v2"); |
|---|