linux/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
<<
>>
Prefs
   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 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");
 380