linux/arch/arm/mach-omap2/prm33xx.c
<<
>>
Prefs
   1/*
   2 * AM33XX PRM functions
   3 *
   4 * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation version 2.
   9 *
  10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 * kind, whether express or implied; without even the implied warranty
  12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/types.h>
  18#include <linux/errno.h>
  19#include <linux/err.h>
  20#include <linux/io.h>
  21
  22#include "powerdomain.h"
  23#include "prm33xx.h"
  24#include "prm-regbits-33xx.h"
  25
  26#define AM33XX_PRM_RSTCTRL_OFFSET               0x0000
  27
  28#define AM33XX_RST_GLOBAL_WARM_SW_MASK          (1 << 0)
  29
  30/* Read a register in a PRM instance */
  31static u32 am33xx_prm_read_reg(s16 inst, u16 idx)
  32{
  33        return readl_relaxed(prm_base.va + inst + idx);
  34}
  35
  36/* Write into a register in a PRM instance */
  37static void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx)
  38{
  39        writel_relaxed(val, prm_base.va + inst + idx);
  40}
  41
  42/* Read-modify-write a register in PRM. Caller must lock */
  43static u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx)
  44{
  45        u32 v;
  46
  47        v = am33xx_prm_read_reg(inst, idx);
  48        v &= ~mask;
  49        v |= bits;
  50        am33xx_prm_write_reg(v, inst, idx);
  51
  52        return v;
  53}
  54
  55/**
  56 * am33xx_prm_is_hardreset_asserted - read the HW reset line state of
  57 * submodules contained in the hwmod module
  58 * @shift: register bit shift corresponding to the reset line to check
  59 * @part: PRM partition, ignored for AM33xx
  60 * @inst: CM instance register offset (*_INST macro)
  61 * @rstctrl_offs: RM_RSTCTRL register address offset for this module
  62 *
  63 * Returns 1 if the (sub)module hardreset line is currently asserted,
  64 * 0 if the (sub)module hardreset line is not currently asserted, or
  65 * -EINVAL upon parameter error.
  66 */
  67static int am33xx_prm_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
  68                                            u16 rstctrl_offs)
  69{
  70        u32 v;
  71
  72        v = am33xx_prm_read_reg(inst, rstctrl_offs);
  73        v &= 1 << shift;
  74        v >>= shift;
  75
  76        return v;
  77}
  78
  79/**
  80 * am33xx_prm_assert_hardreset - assert the HW reset line of a submodule
  81 * @shift: register bit shift corresponding to the reset line to assert
  82 * @part: CM partition, ignored for AM33xx
  83 * @inst: CM instance register offset (*_INST macro)
  84 * @rstctrl_reg: RM_RSTCTRL register address for this module
  85 *
  86 * Some IPs like dsp, ipu or iva contain processors that require an HW
  87 * reset line to be asserted / deasserted in order to fully enable the
  88 * IP.  These modules may have multiple hard-reset lines that reset
  89 * different 'submodules' inside the IP block.  This function will
  90 * place the submodule into reset.  Returns 0 upon success or -EINVAL
  91 * upon an argument error.
  92 */
  93static int am33xx_prm_assert_hardreset(u8 shift, u8 part, s16 inst,
  94                                       u16 rstctrl_offs)
  95{
  96        u32 mask = 1 << shift;
  97
  98        am33xx_prm_rmw_reg_bits(mask, mask, inst, rstctrl_offs);
  99
 100        return 0;
 101}
 102
 103/**
 104 * am33xx_prm_deassert_hardreset - deassert a submodule hardreset line and
 105 * wait
 106 * @shift: register bit shift corresponding to the reset line to deassert
 107 * @st_shift: reset status register bit shift corresponding to the reset line
 108 * @part: PRM partition, not used for AM33xx
 109 * @inst: CM instance register offset (*_INST macro)
 110 * @rstctrl_reg: RM_RSTCTRL register address for this module
 111 * @rstst_reg: RM_RSTST register address for this module
 112 *
 113 * Some IPs like dsp, ipu or iva contain processors that require an HW
 114 * reset line to be asserted / deasserted in order to fully enable the
 115 * IP.  These modules may have multiple hard-reset lines that reset
 116 * different 'submodules' inside the IP block.  This function will
 117 * take the submodule out of reset and wait until the PRCM indicates
 118 * that the reset has completed before returning.  Returns 0 upon success or
 119 * -EINVAL upon an argument error, -EEXIST if the submodule was already out
 120 * of reset, or -EBUSY if the submodule did not exit reset promptly.
 121 */
 122static int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, u8 part,
 123                                         s16 inst, u16 rstctrl_offs,
 124                                         u16 rstst_offs)
 125{
 126        int c;
 127        u32 mask = 1 << st_shift;
 128
 129        /* Check the current status to avoid  de-asserting the line twice */
 130        if (am33xx_prm_is_hardreset_asserted(shift, 0, inst, rstctrl_offs) == 0)
 131                return -EEXIST;
 132
 133        /* Clear the reset status by writing 1 to the status bit */
 134        am33xx_prm_rmw_reg_bits(0xffffffff, mask, inst, rstst_offs);
 135
 136        /* de-assert the reset control line */
 137        mask = 1 << shift;
 138
 139        am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs);
 140
 141        /* wait the status to be set */
 142        omap_test_timeout(am33xx_prm_is_hardreset_asserted(st_shift, 0, inst,
 143                                                           rstst_offs),
 144                          MAX_MODULE_HARDRESET_WAIT, c);
 145
 146        return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
 147}
 148
 149static int am33xx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
 150{
 151        am33xx_prm_rmw_reg_bits(OMAP_POWERSTATE_MASK,
 152                                (pwrst << OMAP_POWERSTATE_SHIFT),
 153                                pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 154        return 0;
 155}
 156
 157static int am33xx_pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
 158{
 159        u32 v;
 160
 161        v = am33xx_prm_read_reg(pwrdm->prcm_offs,  pwrdm->pwrstctrl_offs);
 162        v &= OMAP_POWERSTATE_MASK;
 163        v >>= OMAP_POWERSTATE_SHIFT;
 164
 165        return v;
 166}
 167
 168static int am33xx_pwrdm_read_pwrst(struct powerdomain *pwrdm)
 169{
 170        u32 v;
 171
 172        v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs);
 173        v &= OMAP_POWERSTATEST_MASK;
 174        v >>= OMAP_POWERSTATEST_SHIFT;
 175
 176        return v;
 177}
 178
 179static int am33xx_pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm)
 180{
 181        am33xx_prm_rmw_reg_bits(AM33XX_LOWPOWERSTATECHANGE_MASK,
 182                                (1 << AM33XX_LOWPOWERSTATECHANGE_SHIFT),
 183                                pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 184        return 0;
 185}
 186
 187static int am33xx_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
 188{
 189        am33xx_prm_rmw_reg_bits(AM33XX_LASTPOWERSTATEENTERED_MASK,
 190                                AM33XX_LASTPOWERSTATEENTERED_MASK,
 191                                pwrdm->prcm_offs, pwrdm->pwrstst_offs);
 192        return 0;
 193}
 194
 195static int am33xx_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
 196{
 197        u32 m;
 198
 199        m = pwrdm->logicretstate_mask;
 200        if (!m)
 201                return -EINVAL;
 202
 203        am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)),
 204                                pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 205
 206        return 0;
 207}
 208
 209static int am33xx_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
 210{
 211        u32 v;
 212
 213        v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs);
 214        v &= AM33XX_LOGICSTATEST_MASK;
 215        v >>= AM33XX_LOGICSTATEST_SHIFT;
 216
 217        return v;
 218}
 219
 220static int am33xx_pwrdm_read_logic_retst(struct powerdomain *pwrdm)
 221{
 222        u32 v, m;
 223
 224        m = pwrdm->logicretstate_mask;
 225        if (!m)
 226                return -EINVAL;
 227
 228        v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 229        v &= m;
 230        v >>= __ffs(m);
 231
 232        return v;
 233}
 234
 235static int am33xx_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank,
 236                u8 pwrst)
 237{
 238        u32 m;
 239
 240        m = pwrdm->mem_on_mask[bank];
 241        if (!m)
 242                return -EINVAL;
 243
 244        am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)),
 245                                pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 246
 247        return 0;
 248}
 249
 250static int am33xx_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank,
 251                                        u8 pwrst)
 252{
 253        u32 m;
 254
 255        m = pwrdm->mem_ret_mask[bank];
 256        if (!m)
 257                return -EINVAL;
 258
 259        am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)),
 260                                pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 261
 262        return 0;
 263}
 264
 265static int am33xx_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
 266{
 267        u32 m, v;
 268
 269        m = pwrdm->mem_pwrst_mask[bank];
 270        if (!m)
 271                return -EINVAL;
 272
 273        v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs);
 274        v &= m;
 275        v >>= __ffs(m);
 276
 277        return v;
 278}
 279
 280static int am33xx_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank)
 281{
 282        u32 m, v;
 283
 284        m = pwrdm->mem_retst_mask[bank];
 285        if (!m)
 286                return -EINVAL;
 287
 288        v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstctrl_offs);
 289        v &= m;
 290        v >>= __ffs(m);
 291
 292        return v;
 293}
 294
 295static int am33xx_pwrdm_wait_transition(struct powerdomain *pwrdm)
 296{
 297        u32 c = 0;
 298
 299        /*
 300         * REVISIT: pwrdm_wait_transition() may be better implemented
 301         * via a callback and a periodic timer check -- how long do we expect
 302         * powerdomain transitions to take?
 303         */
 304
 305        /* XXX Is this udelay() value meaningful? */
 306        while ((am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs)
 307                        & OMAP_INTRANSITION_MASK) &&
 308                        (c++ < PWRDM_TRANSITION_BAILOUT))
 309                udelay(1);
 310
 311        if (c > PWRDM_TRANSITION_BAILOUT) {
 312                pr_err("powerdomain: %s: waited too long to complete transition\n",
 313                       pwrdm->name);
 314                return -EAGAIN;
 315        }
 316
 317        pr_debug("powerdomain: completed transition in %d loops\n", c);
 318
 319        return 0;
 320}
 321
 322static int am33xx_check_vcvp(void)
 323{
 324        /* No VC/VP on am33xx devices */
 325        return 0;
 326}
 327
 328/**
 329 * am33xx_prm_global_warm_sw_reset - reboot the device via warm reset
 330 *
 331 * Immediately reboots the device through warm reset.
 332 */
 333static void am33xx_prm_global_warm_sw_reset(void)
 334{
 335        am33xx_prm_rmw_reg_bits(AM33XX_RST_GLOBAL_WARM_SW_MASK,
 336                                AM33XX_RST_GLOBAL_WARM_SW_MASK,
 337                                AM33XX_PRM_DEVICE_MOD,
 338                                AM33XX_PRM_RSTCTRL_OFFSET);
 339
 340        /* OCP barrier */
 341        (void)am33xx_prm_read_reg(AM33XX_PRM_DEVICE_MOD,
 342                                  AM33XX_PRM_RSTCTRL_OFFSET);
 343}
 344
 345static void am33xx_pwrdm_save_context(struct powerdomain *pwrdm)
 346{
 347        pwrdm->context = am33xx_prm_read_reg(pwrdm->prcm_offs,
 348                                                pwrdm->pwrstctrl_offs);
 349        /*
 350         * Do not save LOWPOWERSTATECHANGE, writing a 1 indicates a request,
 351         * reading back a 1 indicates a request in progress.
 352         */
 353        pwrdm->context &= ~AM33XX_LOWPOWERSTATECHANGE_MASK;
 354}
 355
 356static void am33xx_pwrdm_restore_context(struct powerdomain *pwrdm)
 357{
 358        int st, ctrl;
 359
 360        st = am33xx_prm_read_reg(pwrdm->prcm_offs,
 361                                 pwrdm->pwrstst_offs);
 362
 363        am33xx_prm_write_reg(pwrdm->context, pwrdm->prcm_offs,
 364                             pwrdm->pwrstctrl_offs);
 365
 366        /* Make sure we only wait for a transition if there is one */
 367        st &= OMAP_POWERSTATEST_MASK;
 368        ctrl = OMAP_POWERSTATEST_MASK & pwrdm->context;
 369
 370        if (st != ctrl)
 371                am33xx_pwrdm_wait_transition(pwrdm);
 372}
 373
 374struct pwrdm_ops am33xx_pwrdm_operations = {
 375        .pwrdm_set_next_pwrst           = am33xx_pwrdm_set_next_pwrst,
 376        .pwrdm_read_next_pwrst          = am33xx_pwrdm_read_next_pwrst,
 377        .pwrdm_read_pwrst               = am33xx_pwrdm_read_pwrst,
 378        .pwrdm_set_logic_retst          = am33xx_pwrdm_set_logic_retst,
 379        .pwrdm_read_logic_pwrst         = am33xx_pwrdm_read_logic_pwrst,
 380        .pwrdm_read_logic_retst         = am33xx_pwrdm_read_logic_retst,
 381        .pwrdm_clear_all_prev_pwrst     = am33xx_pwrdm_clear_all_prev_pwrst,
 382        .pwrdm_set_lowpwrstchange       = am33xx_pwrdm_set_lowpwrstchange,
 383        .pwrdm_read_mem_pwrst           = am33xx_pwrdm_read_mem_pwrst,
 384        .pwrdm_read_mem_retst           = am33xx_pwrdm_read_mem_retst,
 385        .pwrdm_set_mem_onst             = am33xx_pwrdm_set_mem_onst,
 386        .pwrdm_set_mem_retst            = am33xx_pwrdm_set_mem_retst,
 387        .pwrdm_wait_transition          = am33xx_pwrdm_wait_transition,
 388        .pwrdm_has_voltdm               = am33xx_check_vcvp,
 389        .pwrdm_save_context             = am33xx_pwrdm_save_context,
 390        .pwrdm_restore_context          = am33xx_pwrdm_restore_context,
 391};
 392
 393static struct prm_ll_data am33xx_prm_ll_data = {
 394        .assert_hardreset               = am33xx_prm_assert_hardreset,
 395        .deassert_hardreset             = am33xx_prm_deassert_hardreset,
 396        .is_hardreset_asserted          = am33xx_prm_is_hardreset_asserted,
 397        .reset_system                   = am33xx_prm_global_warm_sw_reset,
 398};
 399
 400int __init am33xx_prm_init(const struct omap_prcm_init_data *data)
 401{
 402        return prm_register(&am33xx_prm_ll_data);
 403}
 404
 405static void __exit am33xx_prm_exit(void)
 406{
 407        prm_unregister(&am33xx_prm_ll_data);
 408}
 409__exitcall(am33xx_prm_exit);
 410