linux/drivers/soc/imx/gpc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
   4 * Copyright 2011-2013 Freescale Semiconductor, Inc.
   5 */
   6
   7#include <linux/clk.h>
   8#include <linux/delay.h>
   9#include <linux/io.h>
  10#include <linux/of_device.h>
  11#include <linux/platform_device.h>
  12#include <linux/pm_domain.h>
  13#include <linux/regmap.h>
  14#include <linux/regulator/consumer.h>
  15
  16#define GPC_CNTR                0x000
  17
  18#define GPC_PGC_CTRL_OFFS       0x0
  19#define GPC_PGC_PUPSCR_OFFS     0x4
  20#define GPC_PGC_PDNSCR_OFFS     0x8
  21#define GPC_PGC_SW2ISO_SHIFT    0x8
  22#define GPC_PGC_SW_SHIFT        0x0
  23
  24#define GPC_PGC_PCI_PDN         0x200
  25#define GPC_PGC_PCI_SR          0x20c
  26
  27#define GPC_PGC_GPU_PDN         0x260
  28#define GPC_PGC_GPU_PUPSCR      0x264
  29#define GPC_PGC_GPU_PDNSCR      0x268
  30#define GPC_PGC_GPU_SR          0x26c
  31
  32#define GPC_PGC_DISP_PDN        0x240
  33#define GPC_PGC_DISP_SR         0x24c
  34
  35#define GPU_VPU_PUP_REQ         BIT(1)
  36#define GPU_VPU_PDN_REQ         BIT(0)
  37
  38#define GPC_CLK_MAX             6
  39
  40#define PGC_DOMAIN_FLAG_NO_PD           BIT(0)
  41
  42struct imx_pm_domain {
  43        struct generic_pm_domain base;
  44        struct regmap *regmap;
  45        struct regulator *supply;
  46        struct clk *clk[GPC_CLK_MAX];
  47        int num_clks;
  48        unsigned int reg_offs;
  49        signed char cntr_pdn_bit;
  50        unsigned int ipg_rate_mhz;
  51};
  52
  53static inline struct imx_pm_domain *
  54to_imx_pm_domain(struct generic_pm_domain *genpd)
  55{
  56        return container_of(genpd, struct imx_pm_domain, base);
  57}
  58
  59static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
  60{
  61        struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
  62        int iso, iso2sw;
  63        u32 val;
  64
  65        /* Read ISO and ISO2SW power down delays */
  66        regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
  67        iso = val & 0x3f;
  68        iso2sw = (val >> 8) & 0x3f;
  69
  70        /* Gate off domain when powered down */
  71        regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
  72                           0x1, 0x1);
  73
  74        /* Request GPC to power down domain */
  75        val = BIT(pd->cntr_pdn_bit);
  76        regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
  77
  78        /* Wait ISO + ISO2SW IPG clock cycles */
  79        udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz));
  80
  81        if (pd->supply)
  82                regulator_disable(pd->supply);
  83
  84        return 0;
  85}
  86
  87static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd)
  88{
  89        struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
  90        int i, ret, sw, sw2iso;
  91        u32 val;
  92
  93        if (pd->supply) {
  94                ret = regulator_enable(pd->supply);
  95                if (ret) {
  96                        pr_err("%s: failed to enable regulator: %d\n",
  97                               __func__, ret);
  98                        return ret;
  99                }
 100        }
 101
 102        /* Enable reset clocks for all devices in the domain */
 103        for (i = 0; i < pd->num_clks; i++)
 104                clk_prepare_enable(pd->clk[i]);
 105
 106        /* Gate off domain when powered down */
 107        regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
 108                           0x1, 0x1);
 109
 110        /* Read ISO and ISO2SW power up delays */
 111        regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val);
 112        sw = val & 0x3f;
 113        sw2iso = (val >> 8) & 0x3f;
 114
 115        /* Request GPC to power up domain */
 116        val = BIT(pd->cntr_pdn_bit + 1);
 117        regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
 118
 119        /* Wait ISO + ISO2SW IPG clock cycles */
 120        udelay(DIV_ROUND_UP(sw + sw2iso, pd->ipg_rate_mhz));
 121
 122        /* Disable reset clocks for all devices in the domain */
 123        for (i = 0; i < pd->num_clks; i++)
 124                clk_disable_unprepare(pd->clk[i]);
 125
 126        return 0;
 127}
 128
 129static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain)
 130{
 131        int i, ret;
 132
 133        for (i = 0; ; i++) {
 134                struct clk *clk = of_clk_get(dev->of_node, i);
 135                if (IS_ERR(clk))
 136                        break;
 137                if (i >= GPC_CLK_MAX) {
 138                        dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
 139                        ret = -EINVAL;
 140                        goto clk_err;
 141                }
 142                domain->clk[i] = clk;
 143        }
 144        domain->num_clks = i;
 145
 146        return 0;
 147
 148clk_err:
 149        while (i--)
 150                clk_put(domain->clk[i]);
 151
 152        return ret;
 153}
 154
 155static void imx_pgc_put_clocks(struct imx_pm_domain *domain)
 156{
 157        int i;
 158
 159        for (i = domain->num_clks - 1; i >= 0; i--)
 160                clk_put(domain->clk[i]);
 161}
 162
 163static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
 164{
 165        /* try to get the domain supply regulator */
 166        domain->supply = devm_regulator_get_optional(dev, "power");
 167        if (IS_ERR(domain->supply)) {
 168                if (PTR_ERR(domain->supply) == -ENODEV)
 169                        domain->supply = NULL;
 170                else
 171                        return PTR_ERR(domain->supply);
 172        }
 173
 174        /* try to get all clocks needed for reset propagation */
 175        return imx_pgc_get_clocks(dev, domain);
 176}
 177
 178static int imx_pgc_power_domain_probe(struct platform_device *pdev)
 179{
 180        struct imx_pm_domain *domain = pdev->dev.platform_data;
 181        struct device *dev = &pdev->dev;
 182        int ret;
 183
 184        /* if this PD is associated with a DT node try to parse it */
 185        if (dev->of_node) {
 186                ret = imx_pgc_parse_dt(dev, domain);
 187                if (ret)
 188                        return ret;
 189        }
 190
 191        /* initially power on the domain */
 192        if (domain->base.power_on)
 193                domain->base.power_on(&domain->base);
 194
 195        if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
 196                pm_genpd_init(&domain->base, NULL, false);
 197                ret = of_genpd_add_provider_simple(dev->of_node, &domain->base);
 198                if (ret)
 199                        goto genpd_err;
 200        }
 201
 202        device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER);
 203
 204        return 0;
 205
 206genpd_err:
 207        pm_genpd_remove(&domain->base);
 208        imx_pgc_put_clocks(domain);
 209
 210        return ret;
 211}
 212
 213static int imx_pgc_power_domain_remove(struct platform_device *pdev)
 214{
 215        struct imx_pm_domain *domain = pdev->dev.platform_data;
 216
 217        if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
 218                of_genpd_del_provider(pdev->dev.of_node);
 219                pm_genpd_remove(&domain->base);
 220                imx_pgc_put_clocks(domain);
 221        }
 222
 223        return 0;
 224}
 225
 226static const struct platform_device_id imx_pgc_power_domain_id[] = {
 227        { "imx-pgc-power-domain"},
 228        { },
 229};
 230
 231static struct platform_driver imx_pgc_power_domain_driver = {
 232        .driver = {
 233                .name = "imx-pgc-pd",
 234        },
 235        .probe = imx_pgc_power_domain_probe,
 236        .remove = imx_pgc_power_domain_remove,
 237        .id_table = imx_pgc_power_domain_id,
 238};
 239builtin_platform_driver(imx_pgc_power_domain_driver)
 240
 241#define GPC_PGC_DOMAIN_ARM      0
 242#define GPC_PGC_DOMAIN_PU       1
 243#define GPC_PGC_DOMAIN_DISPLAY  2
 244#define GPC_PGC_DOMAIN_PCI      3
 245
 246static struct genpd_power_state imx6_pm_domain_pu_state = {
 247        .power_off_latency_ns = 25000,
 248        .power_on_latency_ns = 2000000,
 249};
 250
 251static struct imx_pm_domain imx_gpc_domains[] = {
 252        [GPC_PGC_DOMAIN_ARM] {
 253                .base = {
 254                        .name = "ARM",
 255                        .flags = GENPD_FLAG_ALWAYS_ON,
 256                },
 257        },
 258        [GPC_PGC_DOMAIN_PU] {
 259                .base = {
 260                        .name = "PU",
 261                        .power_off = imx6_pm_domain_power_off,
 262                        .power_on = imx6_pm_domain_power_on,
 263                        .states = &imx6_pm_domain_pu_state,
 264                        .state_count = 1,
 265                },
 266                .reg_offs = 0x260,
 267                .cntr_pdn_bit = 0,
 268        },
 269        [GPC_PGC_DOMAIN_DISPLAY] {
 270                .base = {
 271                        .name = "DISPLAY",
 272                        .power_off = imx6_pm_domain_power_off,
 273                        .power_on = imx6_pm_domain_power_on,
 274                },
 275                .reg_offs = 0x240,
 276                .cntr_pdn_bit = 4,
 277        },
 278        [GPC_PGC_DOMAIN_PCI] {
 279                .base = {
 280                        .name = "PCI",
 281                        .power_off = imx6_pm_domain_power_off,
 282                        .power_on = imx6_pm_domain_power_on,
 283                },
 284                .reg_offs = 0x200,
 285                .cntr_pdn_bit = 6,
 286        },
 287};
 288
 289struct imx_gpc_dt_data {
 290        int num_domains;
 291        bool err009619_present;
 292        bool err006287_present;
 293};
 294
 295static const struct imx_gpc_dt_data imx6q_dt_data = {
 296        .num_domains = 2,
 297        .err009619_present = false,
 298        .err006287_present = false,
 299};
 300
 301static const struct imx_gpc_dt_data imx6qp_dt_data = {
 302        .num_domains = 2,
 303        .err009619_present = true,
 304        .err006287_present = false,
 305};
 306
 307static const struct imx_gpc_dt_data imx6sl_dt_data = {
 308        .num_domains = 3,
 309        .err009619_present = false,
 310        .err006287_present = true,
 311};
 312
 313static const struct imx_gpc_dt_data imx6sx_dt_data = {
 314        .num_domains = 4,
 315        .err009619_present = false,
 316        .err006287_present = false,
 317};
 318
 319static const struct of_device_id imx_gpc_dt_ids[] = {
 320        { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data },
 321        { .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data },
 322        { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data },
 323        { .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data },
 324        { }
 325};
 326
 327static const struct regmap_range yes_ranges[] = {
 328        regmap_reg_range(GPC_CNTR, GPC_CNTR),
 329        regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR),
 330        regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR),
 331        regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR),
 332};
 333
 334static const struct regmap_access_table access_table = {
 335        .yes_ranges     = yes_ranges,
 336        .n_yes_ranges   = ARRAY_SIZE(yes_ranges),
 337};
 338
 339static const struct regmap_config imx_gpc_regmap_config = {
 340        .reg_bits = 32,
 341        .val_bits = 32,
 342        .reg_stride = 4,
 343        .rd_table = &access_table,
 344        .wr_table = &access_table,
 345        .max_register = 0x2ac,
 346};
 347
 348static struct generic_pm_domain *imx_gpc_onecell_domains[] = {
 349        &imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base,
 350        &imx_gpc_domains[GPC_PGC_DOMAIN_PU].base,
 351};
 352
 353static struct genpd_onecell_data imx_gpc_onecell_data = {
 354        .domains = imx_gpc_onecell_domains,
 355        .num_domains = 2,
 356};
 357
 358static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap,
 359                               unsigned int num_domains)
 360{
 361        struct imx_pm_domain *domain;
 362        int i, ret;
 363
 364        for (i = 0; i < num_domains; i++) {
 365                domain = &imx_gpc_domains[i];
 366                domain->regmap = regmap;
 367                domain->ipg_rate_mhz = 66;
 368
 369                if (i == 1) {
 370                        domain->supply = devm_regulator_get(dev, "pu");
 371                        if (IS_ERR(domain->supply))
 372                                return PTR_ERR(domain->supply);
 373
 374                        ret = imx_pgc_get_clocks(dev, domain);
 375                        if (ret)
 376                                goto clk_err;
 377
 378                        domain->base.power_on(&domain->base);
 379                }
 380        }
 381
 382        for (i = 0; i < num_domains; i++)
 383                pm_genpd_init(&imx_gpc_domains[i].base, NULL, false);
 384
 385        if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
 386                ret = of_genpd_add_provider_onecell(dev->of_node,
 387                                                    &imx_gpc_onecell_data);
 388                if (ret)
 389                        goto genpd_err;
 390        }
 391
 392        return 0;
 393
 394genpd_err:
 395        for (i = 0; i < num_domains; i++)
 396                pm_genpd_remove(&imx_gpc_domains[i].base);
 397        imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
 398clk_err:
 399        return ret;
 400}
 401
 402static int imx_gpc_probe(struct platform_device *pdev)
 403{
 404        const struct of_device_id *of_id =
 405                        of_match_device(imx_gpc_dt_ids, &pdev->dev);
 406        const struct imx_gpc_dt_data *of_id_data = of_id->data;
 407        struct device_node *pgc_node;
 408        struct regmap *regmap;
 409        struct resource *res;
 410        void __iomem *base;
 411        int ret;
 412
 413        pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
 414
 415        /* bail out if DT too old and doesn't provide the necessary info */
 416        if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
 417            !pgc_node)
 418                return 0;
 419
 420        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 421        base = devm_ioremap_resource(&pdev->dev, res);
 422        if (IS_ERR(base))
 423                return PTR_ERR(base);
 424
 425        regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
 426                                           &imx_gpc_regmap_config);
 427        if (IS_ERR(regmap)) {
 428                ret = PTR_ERR(regmap);
 429                dev_err(&pdev->dev, "failed to init regmap: %d\n",
 430                        ret);
 431                return ret;
 432        }
 433
 434        /* Disable PU power down in normal operation if ERR009619 is present */
 435        if (of_id_data->err009619_present)
 436                imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |=
 437                                GENPD_FLAG_ALWAYS_ON;
 438
 439        /* Keep DISP always on if ERR006287 is present */
 440        if (of_id_data->err006287_present)
 441                imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |=
 442                                GENPD_FLAG_ALWAYS_ON;
 443
 444        if (!pgc_node) {
 445                ret = imx_gpc_old_dt_init(&pdev->dev, regmap,
 446                                          of_id_data->num_domains);
 447                if (ret)
 448                        return ret;
 449        } else {
 450                struct imx_pm_domain *domain;
 451                struct platform_device *pd_pdev;
 452                struct device_node *np;
 453                struct clk *ipg_clk;
 454                unsigned int ipg_rate_mhz;
 455                int domain_index;
 456
 457                ipg_clk = devm_clk_get(&pdev->dev, "ipg");
 458                if (IS_ERR(ipg_clk))
 459                        return PTR_ERR(ipg_clk);
 460                ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000;
 461
 462                for_each_child_of_node(pgc_node, np) {
 463                        ret = of_property_read_u32(np, "reg", &domain_index);
 464                        if (ret) {
 465                                of_node_put(np);
 466                                return ret;
 467                        }
 468                        if (domain_index >= of_id_data->num_domains)
 469                                continue;
 470
 471                        pd_pdev = platform_device_alloc("imx-pgc-power-domain",
 472                                                        domain_index);
 473                        if (!pd_pdev) {
 474                                of_node_put(np);
 475                                return -ENOMEM;
 476                        }
 477
 478                        ret = platform_device_add_data(pd_pdev,
 479                                                       &imx_gpc_domains[domain_index],
 480                                                       sizeof(imx_gpc_domains[domain_index]));
 481                        if (ret) {
 482                                platform_device_put(pd_pdev);
 483                                of_node_put(np);
 484                                return ret;
 485                        }
 486                        domain = pd_pdev->dev.platform_data;
 487                        domain->regmap = regmap;
 488                        domain->ipg_rate_mhz = ipg_rate_mhz;
 489
 490                        pd_pdev->dev.parent = &pdev->dev;
 491                        pd_pdev->dev.of_node = np;
 492
 493                        ret = platform_device_add(pd_pdev);
 494                        if (ret) {
 495                                platform_device_put(pd_pdev);
 496                                of_node_put(np);
 497                                return ret;
 498                        }
 499                }
 500        }
 501
 502        return 0;
 503}
 504
 505static int imx_gpc_remove(struct platform_device *pdev)
 506{
 507        struct device_node *pgc_node;
 508        int ret;
 509
 510        pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
 511
 512        /* bail out if DT too old and doesn't provide the necessary info */
 513        if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
 514            !pgc_node)
 515                return 0;
 516
 517        /*
 518         * If the old DT binding is used the toplevel driver needs to
 519         * de-register the power domains
 520         */
 521        if (!pgc_node) {
 522                of_genpd_del_provider(pdev->dev.of_node);
 523
 524                ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
 525                if (ret)
 526                        return ret;
 527                imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
 528
 529                ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
 530                if (ret)
 531                        return ret;
 532        }
 533
 534        return 0;
 535}
 536
 537static struct platform_driver imx_gpc_driver = {
 538        .driver = {
 539                .name = "imx-gpc",
 540                .of_match_table = imx_gpc_dt_ids,
 541        },
 542        .probe = imx_gpc_probe,
 543        .remove = imx_gpc_remove,
 544};
 545builtin_platform_driver(imx_gpc_driver)
 546