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
  18/* AO Offsets */
  19
  20#define AO_RTI_GEN_PWR_SLEEP0           (0x3a << 2)
  21
  22#define GEN_PWR_VPU_HDMI                BIT(8)
  23#define GEN_PWR_VPU_HDMI_ISO            BIT(9)
  24
  25/* HHI Offsets */
  26
  27#define HHI_MEM_PD_REG0                 (0x40 << 2)
  28#define HHI_VPU_MEM_PD_REG0             (0x41 << 2)
  29#define HHI_VPU_MEM_PD_REG1             (0x42 << 2)
  30#define HHI_VPU_MEM_PD_REG2             (0x4d << 2)
  31
  32struct meson_gx_pwrc_vpu {
  33        struct generic_pm_domain genpd;
  34        struct regmap *regmap_ao;
  35        struct regmap *regmap_hhi;
  36        struct reset_control *rstc;
  37        struct clk *vpu_clk;
  38        struct clk *vapb_clk;
  39};
  40
  41static inline
  42struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
  43{
  44        return container_of(d, struct meson_gx_pwrc_vpu, genpd);
  45}
  46
  47static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
  48{
  49        struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
  50        int i;
  51
  52        regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  53                           GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
  54        udelay(20);
  55
  56        /* Power Down Memories */
  57        for (i = 0; i < 32; i += 2) {
  58                regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
  59                                   0x3 << i, 0x3 << i);
  60                udelay(5);
  61        }
  62        for (i = 0; i < 32; i += 2) {
  63                regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
  64                                   0x3 << i, 0x3 << i);
  65                udelay(5);
  66        }
  67        for (i = 8; i < 16; i++) {
  68                regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
  69                                   BIT(i), BIT(i));
  70                udelay(5);
  71        }
  72        udelay(20);
  73
  74        regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
  75                           GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
  76
  77        msleep(20);
  78
  79        clk_disable_unprepare(pd->vpu_clk);
  80        clk_disable_unprepare(pd->vapb_clk);
  81
  82        return 0;
  83}
  84
  85static 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
 128static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
 129{
 130        int ret;
 131
 132        ret = clk_prepare_enable(pd->vpu_clk);
 133        if (ret)
 134                return ret;
 135
 136        ret = clk_prepare_enable(pd->vapb_clk);
 137        if (ret)
 138                clk_disable_unprepare(pd->vpu_clk);
 139
 140        return ret;
 141}
 142
 143static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
 144{
 145        struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
 146        int ret;
 147        int i;
 148
 149        regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
 150                           GEN_PWR_VPU_HDMI, 0);
 151        udelay(20);
 152
 153        /* Power Up Memories */
 154        for (i = 0; i < 32; i += 2) {
 155                regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
 156                                   0x3 << i, 0);
 157                udelay(5);
 158        }
 159
 160        for (i = 0; i < 32; i += 2) {
 161                regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
 162                                   0x3 << i, 0);
 163                udelay(5);
 164        }
 165
 166        for (i = 8; i < 16; i++) {
 167                regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
 168                                   BIT(i), 0);
 169                udelay(5);
 170        }
 171        udelay(20);
 172
 173        ret = reset_control_assert(pd->rstc);
 174        if (ret)
 175                return ret;
 176
 177        regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
 178                           GEN_PWR_VPU_HDMI_ISO, 0);
 179
 180        ret = reset_control_deassert(pd->rstc);
 181        if (ret)
 182                return ret;
 183
 184        ret = meson_gx_pwrc_vpu_setup_clk(pd);
 185        if (ret)
 186                return ret;
 187
 188        return 0;
 189}
 190
 191static 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
 245static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
 246{
 247        u32 reg;
 248
 249        regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
 250
 251        return (reg & GEN_PWR_VPU_HDMI);
 252}
 253
 254static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
 255        .genpd = {
 256                .name = "vpu_hdmi",
 257                .power_off = meson_gx_pwrc_vpu_power_off,
 258                .power_on = meson_gx_pwrc_vpu_power_on,
 259        },
 260};
 261
 262static 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
 270static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
 271{
 272        const struct meson_gx_pwrc_vpu *vpu_pd_match;
 273        struct regmap *regmap_ao, *regmap_hhi;
 274        struct meson_gx_pwrc_vpu *vpu_pd;
 275        struct reset_control *rstc;
 276        struct clk *vpu_clk;
 277        struct clk *vapb_clk;
 278        bool powered_off;
 279        int ret;
 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
 293        regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
 294        if (IS_ERR(regmap_ao)) {
 295                dev_err(&pdev->dev, "failed to get regmap\n");
 296                return PTR_ERR(regmap_ao);
 297        }
 298
 299        regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 300                                                     "amlogic,hhi-sysctrl");
 301        if (IS_ERR(regmap_hhi)) {
 302                dev_err(&pdev->dev, "failed to get HHI regmap\n");
 303                return PTR_ERR(regmap_hhi);
 304        }
 305
 306        rstc = devm_reset_control_array_get(&pdev->dev, false, false);
 307        if (IS_ERR(rstc)) {
 308                if (PTR_ERR(rstc) != -EPROBE_DEFER)
 309                        dev_err(&pdev->dev, "failed to get reset lines\n");
 310                return PTR_ERR(rstc);
 311        }
 312
 313        vpu_clk = devm_clk_get(&pdev->dev, "vpu");
 314        if (IS_ERR(vpu_clk)) {
 315                dev_err(&pdev->dev, "vpu clock request failed\n");
 316                return PTR_ERR(vpu_clk);
 317        }
 318
 319        vapb_clk = devm_clk_get(&pdev->dev, "vapb");
 320        if (IS_ERR(vapb_clk)) {
 321                dev_err(&pdev->dev, "vapb clock request failed\n");
 322                return PTR_ERR(vapb_clk);
 323        }
 324
 325        vpu_pd->regmap_ao = regmap_ao;
 326        vpu_pd->regmap_hhi = regmap_hhi;
 327        vpu_pd->rstc = rstc;
 328        vpu_pd->vpu_clk = vpu_clk;
 329        vpu_pd->vapb_clk = vapb_clk;
 330
 331        platform_set_drvdata(pdev, vpu_pd);
 332
 333        powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
 334
 335        /* If already powered, sync the clock states */
 336        if (!powered_off) {
 337                ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd);
 338                if (ret)
 339                        return ret;
 340        }
 341
 342        pm_genpd_init(&vpu_pd->genpd, &pm_domain_always_on_gov,
 343                      powered_off);
 344
 345        return of_genpd_add_provider_simple(pdev->dev.of_node,
 346                                            &vpu_pd->genpd);
 347}
 348
 349static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
 350{
 351        struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev);
 352        bool powered_off;
 353
 354        powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd);
 355        if (!powered_off)
 356                vpu_pd->genpd.power_off(&vpu_pd->genpd);
 357}
 358
 359static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
 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        },
 365        { /* sentinel */ }
 366};
 367
 368static struct platform_driver meson_gx_pwrc_vpu_driver = {
 369        .probe  = meson_gx_pwrc_vpu_probe,
 370        .shutdown = meson_gx_pwrc_vpu_shutdown,
 371        .driver = {
 372                .name           = "meson_gx_pwrc_vpu",
 373                .of_match_table = meson_gx_pwrc_vpu_match_table,
 374        },
 375};
 376builtin_platform_driver(meson_gx_pwrc_vpu_driver);
 377