/*
* OMAP powerdomain control
*
* Copyright (C) 2007-2008 Texas Instruments, Inc.
* Copyright (C) 2007-2009 Nokia Corporation
*
* Written by Paul Walmsley
*
* Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include <asm/atomic.h>
#include "cm.h"
#include "cm-regbits-34xx.h"
#include "cm-regbits-44xx.h"
#include "prm.h"
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include <plat/cpu.h>
#include <plat/powerdomain.h>
#include <plat/clockdomain.h>
#include <plat/prcm.h>
#include "pm.h"
enum {
PWRDM_STATE_NOW = 0,
PWRDM_STATE_PREV,
};
/* Variable holding value of the CPU dependent PWRSTCTRL Register Offset */
static u16 pwrstctrl_reg_offs;
/* Variable holding value of the CPU dependent PWRSTST Register Offset */
static u16 pwrstst_reg_offs;
/* OMAP3 and OMAP4 specific register bit initialisations
* Notice that the names here are not according to each power
* domain but the bit mapping used applies to all of them
*/
/* OMAP3 and OMAP4 Memory Onstate Masks (common across all power domains) */
#define OMAP_MEM0_ONSTATE_MASK OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK
#define OMAP_MEM1_ONSTATE_MASK OMAP3430_L1FLATMEMONSTATE_MASK
#define OMAP_MEM2_ONSTATE_MASK OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK
#define OMAP_MEM3_ONSTATE_MASK OMAP3430_L2FLATMEMONSTATE_MASK
#define OMAP_MEM4_ONSTATE_MASK OMAP4430_OCP_NRET_BANK_ONSTATE_MASK
/* OMAP3 and OMAP4 Memory Retstate Masks (common across all power domains) */
#define OMAP_MEM0_RETSTATE_MASK OMAP3430_SHAREDL1CACHEFLATRETSTATE
#define OMAP_MEM1_RETSTATE_MASK OMAP3430_L1FLATMEMRETSTATE
#define OMAP_MEM2_RETSTATE_MASK OMAP3430_SHAREDL2CACHEFLATRETSTATE
#define OMAP_MEM3_RETSTATE_MASK OMAP3430_L2FLATMEMRETSTATE
#define OMAP_MEM4_RETSTATE_MASK OMAP4430_OCP_NRET_BANK_RETSTATE_MASK
/* OMAP3 and OMAP4 Memory Status bits */
#define OMAP_MEM0_STATEST_MASK OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK
#define OMAP_MEM1_STATEST_MASK OMAP3430_L1FLATMEMSTATEST_MASK
#define OMAP_MEM2_STATEST_MASK OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK
#define OMAP_MEM3_STATEST_MASK OMAP3430_L2FLATMEMSTATEST_MASK
#define OMAP_MEM4_STATEST_MASK OMAP4430_OCP_NRET_BANK_STATEST_MASK
/* pwrdm_list contains all registered struct powerdomains */
static LIST_HEAD(pwrdm_list);
/* Private functions */
static struct powerdomain *_pwrdm_lookup(const char *name)
{
struct powerdomain *pwrdm, *temp_pwrdm;
pwrdm = NULL;
list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
if (!strcmp(name, temp_pwrdm->name)) {
pwrdm = temp_pwrdm;
break;
}
}
return pwrdm;
}
/**
* _pwrdm_register - register a powerdomain
* @pwrdm: struct powerdomain * to register
*
* Adds a powerdomain to the internal powerdomain list. Returns
* -EINVAL if given a null pointer, -EEXIST if a powerdomain is
* already registered by the provided name, or 0 upon success.
*/
static int _pwrdm_register(struct powerdomain *pwrdm)
{
int i;
if (!pwrdm)
return -EINVAL;
if (!omap_chip_is(pwrdm->omap_chip))
return -EINVAL;
if (_pwrdm_lookup(pwrdm->name))
return -EEXIST;
list_add(&pwrdm->node, &pwrdm_list);
/* Initialize the powerdomain's state counter */
for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
pwrdm->state_counter[i] = 0;
pwrdm->ret_logic_off_counter = 0;
for (i = 0; i < pwrdm->banks; i++)
pwrdm->ret_mem_off_counter[i] = 0;
pwrdm_wait_transition(pwrdm);
pwrdm->state = pwrdm_read_pwrst(pwrdm);
pwrdm->state_counter[pwrdm->state] = 1;
pr_debug("powerdomain: registered %s\n", pwrdm->name);
return 0;
}
static void _update_logic_membank_counters(struct powerdomain *pwrdm)
{
int i;
u8 prev_logic_pwrst, prev_mem_pwrst;
prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm);
if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
(prev_logic_pwrst == PWRDM_POWER_OFF))
pwrdm->ret_logic_off_counter++;
for (i = 0; i < pwrdm->banks; i++) {
prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i);
if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) &&
(prev_mem_pwrst == PWRDM_POWER_OFF))
pwrdm->ret_mem_off_counter[i]++;
}
}
static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
{
int prev;
int state;
if (pwrdm == NULL)
return -EINVAL;
state = pwrdm_read_pwrst(pwrdm);
switch (flag) {
case PWRDM_STATE_NOW:
prev = pwrdm->state;
break;
case PWRDM_STATE_PREV:
prev = pwrdm_read_prev_pwrst(pwrdm);
if (pwrdm->state != prev)
pwrdm->state_counter[prev]++;
if (prev == PWRDM_POWER_RET)