Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f8ee187 authored by Magnus Damm's avatar Magnus Damm Committed by Paul Mundt
Browse files

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: default avatarMagnus Damm <damm@igel.co.jp>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent a61c1a63
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -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)		\
@@ -13,7 +16,7 @@
}

struct hwblk_area {
	unsigned long cnt;
	int cnt[HWBLK_CNT_NR];
	unsigned char parent;
	unsigned char flags;
};
@@ -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 {
@@ -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)	\
{							\
+47 −22
Original line number Diff line number Diff line
@@ -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)
	hap->cnt[counter] += value;

	if (hap->cnt[counter] != goal)
		return;

	if (hap->flags & HWBLK_AREA_FLAG_PARENT)
			hwblk_area_inc(info, hap->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)
		if (hap->flags & HWBLK_AREA_FLAG_PARENT)
			hwblk_area_dec(info, hap->parent);
	hap->cnt--;
	hp->cnt[counter] += value;
	if (hp->cnt[counter] == goal)
		hwblk_area_mod_cnt(info, hp->area, counter, value, goal);

	return hp->cnt[counter];
}

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);
}

static void hwblk_enable(struct hwblk_info *info, int hwblk)
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);
@@ -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)
{
+2 −2
Original line number Diff line number Diff line
@@ -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;