Skip to content

Commit

Permalink
sh: Add support for multiple hwblk counters
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Magnus Damm authored and Paul Mundt committed Jul 19, 2009
1 parent a61c1a6 commit 0f8ee18
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 26 deletions.
13 changes: 11 additions & 2 deletions arch/sh/include/asm/hwblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <asm/clock.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(_flags, _parent) \
Expand All @@ -13,7 +16,7 @@
}

struct hwblk_area {
unsigned long cnt;
int cnt[HWBLK_CNT_NR];
unsigned char parent;
unsigned char flags;
};
Expand All @@ -29,7 +32,7 @@ struct hwblk {
void __iomem *mstp;
unsigned char bit;
unsigned char area;
unsigned long cnt;
int cnt[HWBLK_CNT_NR];
};

struct hwblk_info {
Expand All @@ -46,6 +49,12 @@ int arch_hwblk_sleep_mode(void);
int hwblk_register(struct hwblk_info *info);
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 */
#define SH_HWBLK_CLK(_name, _id, _parent, _hwblk, _flags) \
{ \
Expand Down
69 changes: 47 additions & 22 deletions arch/sh/kernel/cpu/hwblk.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,64 @@

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;

hap->cnt++;
if (hap->cnt == 1)
if (hap->flags & HWBLK_AREA_FLAG_PARENT)
hwblk_area_inc(info, hap->parent);
hap->cnt[counter] += value;

if (hap->cnt[counter] != goal)
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;

hp->cnt[counter] += value;
if (hp->cnt[counter] == goal)
hwblk_area_mod_cnt(info, hp->area, counter, value, goal);

if (hap->cnt == 1)
if (hap->flags & HWBLK_AREA_FLAG_PARENT)
hwblk_area_dec(info, hap->parent);
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;
unsigned long tmp;
unsigned long flags;
int ret;

spin_lock_irqsave(&hwblk_lock, flags);

hp->cnt++;
if (hp->cnt == 1) {
hwblk_area_inc(info, hp->area);

ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
if (ret == 1) {
tmp = __raw_readl(hp->mstp);
tmp &= ~(1 << hp->bit);
__raw_writel(tmp, hp->mstp);
Expand All @@ -49,27 +75,26 @@ static void hwblk_enable(struct hwblk_info *info, int hwblk)
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;
unsigned long tmp;
unsigned long flags;
int ret;

spin_lock_irqsave(&hwblk_lock, flags);

if (hp->cnt == 1) {
hwblk_area_dec(info, hp->area);

ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
if (ret == 0) {
tmp = __raw_readl(hp->mstp);
tmp |= 1 << hp->bit;
__raw_writel(tmp, hp->mstp);
}
hp->cnt--;

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)
{
Expand Down
4 changes: 2 additions & 2 deletions arch/sh/kernel/cpu/sh4a/hwblk-sh7722.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ static struct hwblk_info sh7722_hwblk_info = {

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;

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;
Expand Down

0 comments on commit 0f8ee18

Please sign in to comment.