linux/arch/powerpc/sysdev/fsl_rcpm.c
<<
>>
Prefs
   1/*
   2 * RCPM(Run Control/Power Management) support
   3 *
   4 * Copyright 2012-2015 Freescale Semiconductor Inc.
   5 *
   6 * Author: Chenhui Zhao <chenhui.zhao@freescale.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#define pr_fmt(fmt) "%s: " fmt, __func__
  15
  16#include <linux/types.h>
  17#include <linux/errno.h>
  18#include <linux/of_address.h>
  19#include <linux/export.h>
  20
  21#include <asm/io.h>
  22#include <linux/fsl/guts.h>
  23#include <asm/cputhreads.h>
  24#include <asm/fsl_pm.h>
  25#include <asm/smp.h>
  26
  27static struct ccsr_rcpm_v1 __iomem *rcpm_v1_regs;
  28static struct ccsr_rcpm_v2 __iomem *rcpm_v2_regs;
  29static unsigned int fsl_supported_pm_modes;
  30
  31static void rcpm_v1_irq_mask(int cpu)
  32{
  33        int hw_cpu = get_hard_smp_processor_id(cpu);
  34        unsigned int mask = 1 << hw_cpu;
  35
  36        setbits32(&rcpm_v1_regs->cpmimr, mask);
  37        setbits32(&rcpm_v1_regs->cpmcimr, mask);
  38        setbits32(&rcpm_v1_regs->cpmmcmr, mask);
  39        setbits32(&rcpm_v1_regs->cpmnmimr, mask);
  40}
  41
  42static void rcpm_v2_irq_mask(int cpu)
  43{
  44        int hw_cpu = get_hard_smp_processor_id(cpu);
  45        unsigned int mask = 1 << hw_cpu;
  46
  47        setbits32(&rcpm_v2_regs->tpmimr0, mask);
  48        setbits32(&rcpm_v2_regs->tpmcimr0, mask);
  49        setbits32(&rcpm_v2_regs->tpmmcmr0, mask);
  50        setbits32(&rcpm_v2_regs->tpmnmimr0, mask);
  51}
  52
  53static void rcpm_v1_irq_unmask(int cpu)
  54{
  55        int hw_cpu = get_hard_smp_processor_id(cpu);
  56        unsigned int mask = 1 << hw_cpu;
  57
  58        clrbits32(&rcpm_v1_regs->cpmimr, mask);
  59        clrbits32(&rcpm_v1_regs->cpmcimr, mask);
  60        clrbits32(&rcpm_v1_regs->cpmmcmr, mask);
  61        clrbits32(&rcpm_v1_regs->cpmnmimr, mask);
  62}
  63
  64static void rcpm_v2_irq_unmask(int cpu)
  65{
  66        int hw_cpu = get_hard_smp_processor_id(cpu);
  67        unsigned int mask = 1 << hw_cpu;
  68
  69        clrbits32(&rcpm_v2_regs->tpmimr0, mask);
  70        clrbits32(&rcpm_v2_regs->tpmcimr0, mask);
  71        clrbits32(&rcpm_v2_regs->tpmmcmr0, mask);
  72        clrbits32(&rcpm_v2_regs->tpmnmimr0, mask);
  73}
  74
  75static void rcpm_v1_set_ip_power(bool enable, u32 mask)
  76{
  77        if (enable)
  78                setbits32(&rcpm_v1_regs->ippdexpcr, mask);
  79        else
  80                clrbits32(&rcpm_v1_regs->ippdexpcr, mask);
  81}
  82
  83static void rcpm_v2_set_ip_power(bool enable, u32 mask)
  84{
  85        if (enable)
  86                setbits32(&rcpm_v2_regs->ippdexpcr[0], mask);
  87        else
  88                clrbits32(&rcpm_v2_regs->ippdexpcr[0], mask);
  89}
  90
  91static void rcpm_v1_cpu_enter_state(int cpu, int state)
  92{
  93        int hw_cpu = get_hard_smp_processor_id(cpu);
  94        unsigned int mask = 1 << hw_cpu;
  95
  96        switch (state) {
  97        case E500_PM_PH10:
  98                setbits32(&rcpm_v1_regs->cdozcr, mask);
  99                break;
 100        case E500_PM_PH15:
 101                setbits32(&rcpm_v1_regs->cnapcr, mask);
 102                break;
 103        default:
 104                pr_warn("Unknown cpu PM state (%d)\n", state);
 105                break;
 106        }
 107}
 108
 109static void rcpm_v2_cpu_enter_state(int cpu, int state)
 110{
 111        int hw_cpu = get_hard_smp_processor_id(cpu);
 112        u32 mask = 1 << cpu_core_index_of_thread(cpu);
 113
 114        switch (state) {
 115        case E500_PM_PH10:
 116                /* one bit corresponds to one thread for PH10 of 6500 */
 117                setbits32(&rcpm_v2_regs->tph10setr0, 1 << hw_cpu);
 118                break;
 119        case E500_PM_PH15:
 120                setbits32(&rcpm_v2_regs->pcph15setr, mask);
 121                break;
 122        case E500_PM_PH20:
 123                setbits32(&rcpm_v2_regs->pcph20setr, mask);
 124                break;
 125        case E500_PM_PH30:
 126                setbits32(&rcpm_v2_regs->pcph30setr, mask);
 127                break;
 128        default:
 129                pr_warn("Unknown cpu PM state (%d)\n", state);
 130        }
 131}
 132
 133static void rcpm_v1_cpu_die(int cpu)
 134{
 135        rcpm_v1_cpu_enter_state(cpu, E500_PM_PH15);
 136}
 137
 138#ifdef CONFIG_PPC64
 139static void qoriq_disable_thread(int cpu)
 140{
 141        int thread = cpu_thread_in_core(cpu);
 142
 143        book3e_stop_thread(thread);
 144}
 145#endif
 146
 147static void rcpm_v2_cpu_die(int cpu)
 148{
 149#ifdef CONFIG_PPC64
 150        int primary;
 151
 152        if (threads_per_core == 2) {
 153                primary = cpu_first_thread_sibling(cpu);
 154                if (cpu_is_offline(primary) && cpu_is_offline(primary + 1)) {
 155                        /* if both threads are offline, put the cpu in PH20 */
 156                        rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20);
 157                } else {
 158                        /* if only one thread is offline, disable the thread */
 159                        qoriq_disable_thread(cpu);
 160                }
 161        }
 162#endif
 163
 164        if (threads_per_core == 1)
 165                rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20);
 166}
 167
 168static void rcpm_v1_cpu_exit_state(int cpu, int state)
 169{
 170        int hw_cpu = get_hard_smp_processor_id(cpu);
 171        unsigned int mask = 1 << hw_cpu;
 172
 173        switch (state) {
 174        case E500_PM_PH10:
 175                clrbits32(&rcpm_v1_regs->cdozcr, mask);
 176                break;
 177        case E500_PM_PH15:
 178                clrbits32(&rcpm_v1_regs->cnapcr, mask);
 179                break;
 180        default:
 181                pr_warn("Unknown cpu PM state (%d)\n", state);
 182                break;
 183        }
 184}
 185
 186static void rcpm_v1_cpu_up_prepare(int cpu)
 187{
 188        rcpm_v1_cpu_exit_state(cpu, E500_PM_PH15);
 189        rcpm_v1_irq_unmask(cpu);
 190}
 191
 192static void rcpm_v2_cpu_exit_state(int cpu, int state)
 193{
 194        int hw_cpu = get_hard_smp_processor_id(cpu);
 195        u32 mask = 1 << cpu_core_index_of_thread(cpu);
 196
 197        switch (state) {
 198        case E500_PM_PH10:
 199                setbits32(&rcpm_v2_regs->tph10clrr0, 1 << hw_cpu);
 200                break;
 201        case E500_PM_PH15:
 202                setbits32(&rcpm_v2_regs->pcph15clrr, mask);
 203                break;
 204        case E500_PM_PH20:
 205                setbits32(&rcpm_v2_regs->pcph20clrr, mask);
 206                break;
 207        case E500_PM_PH30:
 208                setbits32(&rcpm_v2_regs->pcph30clrr, mask);
 209                break;
 210        default:
 211                pr_warn("Unknown cpu PM state (%d)\n", state);
 212        }
 213}
 214
 215static void rcpm_v2_cpu_up_prepare(int cpu)
 216{
 217        rcpm_v2_cpu_exit_state(cpu, E500_PM_PH20);
 218        rcpm_v2_irq_unmask(cpu);
 219}
 220
 221static int rcpm_v1_plat_enter_state(int state)
 222{
 223        u32 *pmcsr_reg = &rcpm_v1_regs->powmgtcsr;
 224        int ret = 0;
 225        int result;
 226
 227        switch (state) {
 228        case PLAT_PM_SLEEP:
 229                setbits32(pmcsr_reg, RCPM_POWMGTCSR_SLP);
 230
 231                /* Upon resume, wait for RCPM_POWMGTCSR_SLP bit to be clear. */
 232                result = spin_event_timeout(
 233                  !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_SLP), 10000, 10);
 234                if (!result) {
 235                        pr_err("timeout waiting for SLP bit to be cleared\n");
 236                        ret = -ETIMEDOUT;
 237                }
 238                break;
 239        default:
 240                pr_warn("Unknown platform PM state (%d)", state);
 241                ret = -EINVAL;
 242        }
 243
 244        return ret;
 245}
 246
 247static int rcpm_v2_plat_enter_state(int state)
 248{
 249        u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr;
 250        int ret = 0;
 251        int result;
 252
 253        switch (state) {
 254        case PLAT_PM_LPM20:
 255                /* clear previous LPM20 status */
 256                setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST);
 257                /* enter LPM20 status */
 258                setbits32(pmcsr_reg, RCPM_POWMGTCSR_LPM20_RQ);
 259
 260                /* At this point, the device is in LPM20 status. */
 261
 262                /* resume ... */
 263                result = spin_event_timeout(
 264                  !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_LPM20_ST), 10000, 10);
 265                if (!result) {
 266                        pr_err("timeout waiting for LPM20 bit to be cleared\n");
 267                        ret = -ETIMEDOUT;
 268                }
 269                break;
 270        default:
 271                pr_warn("Unknown platform PM state (%d)\n", state);
 272                ret = -EINVAL;
 273        }
 274
 275        return ret;
 276}
 277
 278static int rcpm_v1_plat_enter_sleep(void)
 279{
 280        return rcpm_v1_plat_enter_state(PLAT_PM_SLEEP);
 281}
 282
 283static int rcpm_v2_plat_enter_sleep(void)
 284{
 285        return rcpm_v2_plat_enter_state(PLAT_PM_LPM20);
 286}
 287
 288static void rcpm_common_freeze_time_base(u32 *tben_reg, int freeze)
 289{
 290        static u32 mask;
 291
 292        if (freeze) {
 293                mask = in_be32(tben_reg);
 294                clrbits32(tben_reg, mask);
 295        } else {
 296                setbits32(tben_reg, mask);
 297        }
 298
 299        /* read back to push the previous write */
 300        in_be32(tben_reg);
 301}
 302
 303static void rcpm_v1_freeze_time_base(bool freeze)
 304{
 305        rcpm_common_freeze_time_base(&rcpm_v1_regs->ctbenr, freeze);
 306}
 307
 308static void rcpm_v2_freeze_time_base(bool freeze)
 309{
 310        rcpm_common_freeze_time_base(&rcpm_v2_regs->pctbenr, freeze);
 311}
 312
 313static unsigned int rcpm_get_pm_modes(void)
 314{
 315        return fsl_supported_pm_modes;
 316}
 317
 318static const struct fsl_pm_ops qoriq_rcpm_v1_ops = {
 319        .irq_mask = rcpm_v1_irq_mask,
 320        .irq_unmask = rcpm_v1_irq_unmask,
 321        .cpu_enter_state = rcpm_v1_cpu_enter_state,
 322        .cpu_exit_state = rcpm_v1_cpu_exit_state,
 323        .cpu_up_prepare = rcpm_v1_cpu_up_prepare,
 324        .cpu_die = rcpm_v1_cpu_die,
 325        .plat_enter_sleep = rcpm_v1_plat_enter_sleep,
 326        .set_ip_power = rcpm_v1_set_ip_power,
 327        .freeze_time_base = rcpm_v1_freeze_time_base,
 328        .get_pm_modes = rcpm_get_pm_modes,
 329};
 330
 331static const struct fsl_pm_ops qoriq_rcpm_v2_ops = {
 332        .irq_mask = rcpm_v2_irq_mask,
 333        .irq_unmask = rcpm_v2_irq_unmask,
 334        .cpu_enter_state = rcpm_v2_cpu_enter_state,
 335        .cpu_exit_state = rcpm_v2_cpu_exit_state,
 336        .cpu_up_prepare = rcpm_v2_cpu_up_prepare,
 337        .cpu_die = rcpm_v2_cpu_die,
 338        .plat_enter_sleep = rcpm_v2_plat_enter_sleep,
 339        .set_ip_power = rcpm_v2_set_ip_power,
 340        .freeze_time_base = rcpm_v2_freeze_time_base,
 341        .get_pm_modes = rcpm_get_pm_modes,
 342};
 343
 344static const struct of_device_id rcpm_matches[] = {
 345        {
 346                .compatible = "fsl,qoriq-rcpm-1.0",
 347                .data = &qoriq_rcpm_v1_ops,
 348        },
 349        {
 350                .compatible = "fsl,qoriq-rcpm-2.0",
 351                .data = &qoriq_rcpm_v2_ops,
 352        },
 353        {
 354                .compatible = "fsl,qoriq-rcpm-2.1",
 355                .data = &qoriq_rcpm_v2_ops,
 356        },
 357        {},
 358};
 359
 360int __init fsl_rcpm_init(void)
 361{
 362        struct device_node *np;
 363        const struct of_device_id *match;
 364        void __iomem *base;
 365
 366        np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
 367        if (!np)
 368                return 0;
 369
 370        base = of_iomap(np, 0);
 371        of_node_put(np);
 372        if (!base) {
 373                pr_err("of_iomap() error.\n");
 374                return -ENOMEM;
 375        }
 376
 377        rcpm_v1_regs = base;
 378        rcpm_v2_regs = base;
 379
 380        /* support sleep by default */
 381        fsl_supported_pm_modes = FSL_PM_SLEEP;
 382
 383        qoriq_pm_ops = match->data;
 384
 385        return 0;
 386}
 387