.. | .. |
---|
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"); |
---|