diff options
| -rw-r--r-- | drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 152 |
1 files changed, 140 insertions, 12 deletions
diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 05421d029dff..511b6856225d 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/bitfield.h> | 11 | #include <linux/bitfield.h> |
| 12 | #include <linux/regmap.h> | 12 | #include <linux/regmap.h> |
| 13 | #include <linux/mfd/syscon.h> | 13 | #include <linux/mfd/syscon.h> |
| 14 | #include <linux/of_device.h> | ||
| 14 | #include <linux/reset.h> | 15 | #include <linux/reset.h> |
| 15 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
| 16 | 17 | ||
| @@ -26,6 +27,7 @@ | |||
| 26 | #define HHI_MEM_PD_REG0 (0x40 << 2) | 27 | #define HHI_MEM_PD_REG0 (0x40 << 2) |
| 27 | #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) | 28 | #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) |
| 28 | #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) | 29 | #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) |
| 30 | #define HHI_VPU_MEM_PD_REG2 (0x4d << 2) | ||
| 29 | 31 | ||
| 30 | struct meson_gx_pwrc_vpu { | 32 | struct meson_gx_pwrc_vpu { |
| 31 | struct generic_pm_domain genpd; | 33 | struct generic_pm_domain genpd; |
| @@ -80,6 +82,49 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) | |||
| 80 | return 0; | 82 | return 0; |
| 81 | } | 83 | } |
| 82 | 84 | ||
| 85 | static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) | ||
| 86 | { | ||
| 87 | struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); | ||
| 88 | int i; | ||
| 89 | |||
| 90 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 91 | GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); | ||
| 92 | udelay(20); | ||
| 93 | |||
| 94 | /* Power Down Memories */ | ||
| 95 | for (i = 0; i < 32; i += 2) { | ||
| 96 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, | ||
| 97 | 0x3 << i, 0x3 << i); | ||
| 98 | udelay(5); | ||
| 99 | } | ||
| 100 | for (i = 0; i < 32; i += 2) { | ||
| 101 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, | ||
| 102 | 0x3 << i, 0x3 << i); | ||
| 103 | udelay(5); | ||
| 104 | } | ||
| 105 | for (i = 0; i < 32; i += 2) { | ||
| 106 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, | ||
| 107 | 0x3 << i, 0x3 << i); | ||
| 108 | udelay(5); | ||
| 109 | } | ||
| 110 | for (i = 8; i < 16; i++) { | ||
| 111 | regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, | ||
| 112 | BIT(i), BIT(i)); | ||
| 113 | udelay(5); | ||
| 114 | } | ||
| 115 | udelay(20); | ||
| 116 | |||
| 117 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 118 | GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); | ||
| 119 | |||
| 120 | msleep(20); | ||
| 121 | |||
| 122 | clk_disable_unprepare(pd->vpu_clk); | ||
| 123 | clk_disable_unprepare(pd->vapb_clk); | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 83 | static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) | 128 | static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) |
| 84 | { | 129 | { |
| 85 | int ret; | 130 | int ret; |
| @@ -143,6 +188,60 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) | |||
| 143 | return 0; | 188 | return 0; |
| 144 | } | 189 | } |
| 145 | 190 | ||
| 191 | static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) | ||
| 192 | { | ||
| 193 | struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); | ||
| 194 | int ret; | ||
| 195 | int i; | ||
| 196 | |||
| 197 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 198 | GEN_PWR_VPU_HDMI, 0); | ||
| 199 | udelay(20); | ||
| 200 | |||
| 201 | /* Power Up Memories */ | ||
| 202 | for (i = 0; i < 32; i += 2) { | ||
| 203 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, | ||
| 204 | 0x3 << i, 0); | ||
| 205 | udelay(5); | ||
| 206 | } | ||
| 207 | |||
| 208 | for (i = 0; i < 32; i += 2) { | ||
| 209 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, | ||
| 210 | 0x3 << i, 0); | ||
| 211 | udelay(5); | ||
| 212 | } | ||
| 213 | |||
| 214 | for (i = 0; i < 32; i += 2) { | ||
| 215 | regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, | ||
| 216 | 0x3 << i, 0); | ||
| 217 | udelay(5); | ||
| 218 | } | ||
| 219 | |||
| 220 | for (i = 8; i < 16; i++) { | ||
| 221 | regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, | ||
| 222 | BIT(i), 0); | ||
| 223 | udelay(5); | ||
| 224 | } | ||
| 225 | udelay(20); | ||
| 226 | |||
| 227 | ret = reset_control_assert(pd->rstc); | ||
| 228 | if (ret) | ||
| 229 | return ret; | ||
| 230 | |||
| 231 | regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, | ||
| 232 | GEN_PWR_VPU_HDMI_ISO, 0); | ||
| 233 | |||
| 234 | ret = reset_control_deassert(pd->rstc); | ||
| 235 | if (ret) | ||
| 236 | return ret; | ||
| 237 | |||
| 238 | ret = meson_gx_pwrc_vpu_setup_clk(pd); | ||
| 239 | if (ret) | ||
| 240 | return ret; | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 146 | static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) | 245 | static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) |
| 147 | { | 246 | { |
| 148 | u32 reg; | 247 | u32 reg; |
| @@ -160,15 +259,37 @@ static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { | |||
| 160 | }, | 259 | }, |
| 161 | }; | 260 | }; |
| 162 | 261 | ||
| 262 | static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { | ||
| 263 | .genpd = { | ||
| 264 | .name = "vpu_hdmi", | ||
| 265 | .power_off = meson_g12a_pwrc_vpu_power_off, | ||
| 266 | .power_on = meson_g12a_pwrc_vpu_power_on, | ||
| 267 | }, | ||
| 268 | }; | ||
| 269 | |||
| 163 | static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) | 270 | static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) |
| 164 | { | 271 | { |
| 272 | const struct meson_gx_pwrc_vpu *vpu_pd_match; | ||
| 165 | struct regmap *regmap_ao, *regmap_hhi; | 273 | struct regmap *regmap_ao, *regmap_hhi; |
| 274 | struct meson_gx_pwrc_vpu *vpu_pd; | ||
| 166 | struct reset_control *rstc; | 275 | struct reset_control *rstc; |
| 167 | struct clk *vpu_clk; | 276 | struct clk *vpu_clk; |
| 168 | struct clk *vapb_clk; | 277 | struct clk *vapb_clk; |
| 169 | bool powered_off; | 278 | bool powered_off; |
| 170 | int ret; | 279 | int ret; |
| 171 | 280 | ||
| 281 | vpu_pd_match = of_device_get_match_data(&pdev->dev); | ||
| 282 | if (!vpu_pd_match) { | ||
| 283 | dev_err(&pdev->dev, "failed to get match data\n"); | ||
| 284 | return -ENODEV; | ||
| 285 | } | ||
| 286 | |||
| 287 | vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); | ||
| 288 | if (!vpu_pd) | ||
| 289 | return -ENOMEM; | ||
| 290 | |||
| 291 | memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); | ||
| 292 | |||
| 172 | regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); | 293 | regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); |
| 173 | if (IS_ERR(regmap_ao)) { | 294 | if (IS_ERR(regmap_ao)) { |
| 174 | dev_err(&pdev->dev, "failed to get regmap\n"); | 295 | dev_err(&pdev->dev, "failed to get regmap\n"); |
| @@ -201,39 +322,46 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) | |||
| 201 | return PTR_ERR(vapb_clk); | 322 | return PTR_ERR(vapb_clk); |
| 202 | } | 323 | } |
| 203 | 324 | ||
| 204 | vpu_hdmi_pd.regmap_ao = regmap_ao; | 325 | vpu_pd->regmap_ao = regmap_ao; |
| 205 | vpu_hdmi_pd.regmap_hhi = regmap_hhi; | 326 | vpu_pd->regmap_hhi = regmap_hhi; |
| 206 | vpu_hdmi_pd.rstc = rstc; | 327 | vpu_pd->rstc = rstc; |
| 207 | vpu_hdmi_pd.vpu_clk = vpu_clk; | 328 | vpu_pd->vpu_clk = vpu_clk; |
| 208 | vpu_hdmi_pd.vapb_clk = vapb_clk; | 329 | vpu_pd->vapb_clk = vapb_clk; |
| 330 | |||
| 331 | platform_set_drvdata(pdev, vpu_pd); | ||
| 209 | 332 | ||
| 210 | powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); | 333 | powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); |
| 211 | 334 | ||
| 212 | /* If already powered, sync the clock states */ | 335 | /* If already powered, sync the clock states */ |
| 213 | if (!powered_off) { | 336 | if (!powered_off) { |
| 214 | ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd); | 337 | ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); |
| 215 | if (ret) | 338 | if (ret) |
| 216 | return ret; | 339 | return ret; |
| 217 | } | 340 | } |
| 218 | 341 | ||
| 219 | pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov, | 342 | pm_genpd_init(&vpu_pd->genpd, &pm_domain_always_on_gov, |
| 220 | powered_off); | 343 | powered_off); |
| 221 | 344 | ||
| 222 | return of_genpd_add_provider_simple(pdev->dev.of_node, | 345 | return of_genpd_add_provider_simple(pdev->dev.of_node, |
| 223 | &vpu_hdmi_pd.genpd); | 346 | &vpu_pd->genpd); |
| 224 | } | 347 | } |
| 225 | 348 | ||
| 226 | static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) | 349 | static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) |
| 227 | { | 350 | { |
| 351 | struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); | ||
| 228 | bool powered_off; | 352 | bool powered_off; |
| 229 | 353 | ||
| 230 | powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); | 354 | powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); |
| 231 | if (!powered_off) | 355 | if (!powered_off) |
| 232 | meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); | 356 | vpu_pd->genpd.power_off(&vpu_pd->genpd); |
| 233 | } | 357 | } |
| 234 | 358 | ||
| 235 | static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { | 359 | static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { |
| 236 | { .compatible = "amlogic,meson-gx-pwrc-vpu" }, | 360 | { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, |
| 361 | { | ||
| 362 | .compatible = "amlogic,meson-g12a-pwrc-vpu", | ||
| 363 | .data = &vpu_hdmi_pd_g12a | ||
| 364 | }, | ||
| 237 | { /* sentinel */ } | 365 | { /* sentinel */ } |
| 238 | }; | 366 | }; |
| 239 | 367 | ||
