sh: Add support for multiple hwblk counters

Extend the SuperH hwblk code to support more than one counter.
Contains ground work for the future Runtime PM implementation.

Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Magnus Damm 2009-07-17 14:24:55 +00:00 committed by Paul Mundt
parent a61c1a6366
commit 0f8ee1874f
3 changed files with 60 additions and 26 deletions

View File

@ -4,6 +4,9 @@
#include <asm/clock.h> #include <asm/clock.h>
#include <asm/io.h> #include <asm/io.h>
#define HWBLK_CNT_USAGE 0
#define HWBLK_CNT_NR 1
#define HWBLK_AREA_FLAG_PARENT (1 << 0) /* valid parent */ #define HWBLK_AREA_FLAG_PARENT (1 << 0) /* valid parent */
#define HWBLK_AREA(_flags, _parent) \ #define HWBLK_AREA(_flags, _parent) \
@ -13,7 +16,7 @@
} }
struct hwblk_area { struct hwblk_area {
unsigned long cnt; int cnt[HWBLK_CNT_NR];
unsigned char parent; unsigned char parent;
unsigned char flags; unsigned char flags;
}; };
@ -29,7 +32,7 @@ struct hwblk {
void __iomem *mstp; void __iomem *mstp;
unsigned char bit; unsigned char bit;
unsigned char area; unsigned char area;
unsigned long cnt; int cnt[HWBLK_CNT_NR];
}; };
struct hwblk_info { struct hwblk_info {
@ -46,6 +49,12 @@ int arch_hwblk_sleep_mode(void);
int hwblk_register(struct hwblk_info *info); int hwblk_register(struct hwblk_info *info);
int hwblk_init(void); int hwblk_init(void);
void hwblk_enable(struct hwblk_info *info, int hwblk);
void hwblk_disable(struct hwblk_info *info, int hwblk);
void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int cnt);
void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int cnt);
/* allow clocks to enable and disable hardware blocks */ /* allow clocks to enable and disable hardware blocks */
#define SH_HWBLK_CLK(_name, _id, _parent, _hwblk, _flags) \ #define SH_HWBLK_CLK(_name, _id, _parent, _hwblk, _flags) \
{ \ { \

View File

@ -9,38 +9,64 @@
static DEFINE_SPINLOCK(hwblk_lock); static DEFINE_SPINLOCK(hwblk_lock);
static void hwblk_area_inc(struct hwblk_info *info, int area) static void hwblk_area_mod_cnt(struct hwblk_info *info,
int area, int counter, int value, int goal)
{ {
struct hwblk_area *hap = info->areas + area; struct hwblk_area *hap = info->areas + area;
hap->cnt++; hap->cnt[counter] += value;
if (hap->cnt == 1)
if (hap->flags & HWBLK_AREA_FLAG_PARENT) if (hap->cnt[counter] != goal)
hwblk_area_inc(info, hap->parent); return;
if (hap->flags & HWBLK_AREA_FLAG_PARENT)
hwblk_area_mod_cnt(info, hap->parent, counter, value, goal);
} }
static void hwblk_area_dec(struct hwblk_info *info, int area)
static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
int counter, int value, int goal)
{ {
struct hwblk_area *hap = info->areas + area; struct hwblk *hp = info->hwblks + hwblk;
if (hap->cnt == 1) hp->cnt[counter] += value;
if (hap->flags & HWBLK_AREA_FLAG_PARENT) if (hp->cnt[counter] == goal)
hwblk_area_dec(info, hap->parent); hwblk_area_mod_cnt(info, hp->area, counter, value, goal);
hap->cnt--;
return hp->cnt[counter];
} }
static void hwblk_enable(struct hwblk_info *info, int hwblk) static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
int counter, int value, int goal)
{
unsigned long flags;
spin_lock_irqsave(&hwblk_lock, flags);
__hwblk_mod_cnt(info, hwblk, counter, value, goal);
spin_unlock_irqrestore(&hwblk_lock, flags);
}
void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
{
hwblk_mod_cnt(info, hwblk, counter, 1, 1);
}
void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
{
hwblk_mod_cnt(info, hwblk, counter, -1, 0);
}
void hwblk_enable(struct hwblk_info *info, int hwblk)
{ {
struct hwblk *hp = info->hwblks + hwblk; struct hwblk *hp = info->hwblks + hwblk;
unsigned long tmp; unsigned long tmp;
unsigned long flags; unsigned long flags;
int ret;
spin_lock_irqsave(&hwblk_lock, flags); spin_lock_irqsave(&hwblk_lock, flags);
hp->cnt++; ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
if (hp->cnt == 1) { if (ret == 1) {
hwblk_area_inc(info, hp->area);
tmp = __raw_readl(hp->mstp); tmp = __raw_readl(hp->mstp);
tmp &= ~(1 << hp->bit); tmp &= ~(1 << hp->bit);
__raw_writel(tmp, hp->mstp); __raw_writel(tmp, hp->mstp);
@ -49,27 +75,26 @@ static void hwblk_enable(struct hwblk_info *info, int hwblk)
spin_unlock_irqrestore(&hwblk_lock, flags); spin_unlock_irqrestore(&hwblk_lock, flags);
} }
static void hwblk_disable(struct hwblk_info *info, int hwblk) void hwblk_disable(struct hwblk_info *info, int hwblk)
{ {
struct hwblk *hp = info->hwblks + hwblk; struct hwblk *hp = info->hwblks + hwblk;
unsigned long tmp; unsigned long tmp;
unsigned long flags; unsigned long flags;
int ret;
spin_lock_irqsave(&hwblk_lock, flags); spin_lock_irqsave(&hwblk_lock, flags);
if (hp->cnt == 1) { ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
hwblk_area_dec(info, hp->area); if (ret == 0) {
tmp = __raw_readl(hp->mstp); tmp = __raw_readl(hp->mstp);
tmp |= 1 << hp->bit; tmp |= 1 << hp->bit;
__raw_writel(tmp, hp->mstp); __raw_writel(tmp, hp->mstp);
} }
hp->cnt--;
spin_unlock_irqrestore(&hwblk_lock, flags); spin_unlock_irqrestore(&hwblk_lock, flags);
} }
static struct hwblk_info *hwblk_info; struct hwblk_info *hwblk_info;
int __init hwblk_register(struct hwblk_info *info) int __init hwblk_register(struct hwblk_info *info)
{ {

View File

@ -91,10 +91,10 @@ static struct hwblk_info sh7722_hwblk_info = {
int arch_hwblk_sleep_mode(void) int arch_hwblk_sleep_mode(void)
{ {
if (!sh7722_hwblk_area[CORE_AREA].cnt) if (!sh7722_hwblk_area[CORE_AREA].cnt[HWBLK_CNT_USAGE])
return SUSP_SH_STANDBY | SUSP_SH_SF; return SUSP_SH_STANDBY | SUSP_SH_SF;
if (!sh7722_hwblk_area[CORE_AREA_BM].cnt) if (!sh7722_hwblk_area[CORE_AREA_BM].cnt[HWBLK_CNT_USAGE])
return SUSP_SH_SLEEP | SUSP_SH_SF; return SUSP_SH_SLEEP | SUSP_SH_SF;
return SUSP_SH_SLEEP; return SUSP_SH_SLEEP;