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

Commit 4d64d2e3 authored by Mattias Nilsson's avatar Mattias Nilsson Committed by Samuel Ortiz
Browse files

mfd: db8500 OPP and sleep handling update



This updates the operating point handling code by:

- Supporting the DDR OPP retention state.
- Supporting another low operating point named
  APE_50_PARTLY_25_OPP
- Adding an interface to figure out if the sleep state change
  was properly achieved.

Signed-off-by: default avatarShreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>
Signed-off-by: default avatarRabin Vincent <rabin.vincent@stericsson.com>
Signed-off-by: default avatarMattias Nilsson <mattias.i.nilsson@stericsson.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent 992b133a
Loading
Loading
Loading
Loading
+72 −1
Original line number Diff line number Diff line
@@ -343,11 +343,13 @@ static struct {
 * mb1_transfer - state needed for mailbox 1 communication.
 * @lock:	The transaction lock.
 * @work:	The transaction completion structure.
 * @ape_opp:	The current APE OPP.
 * @ack:	Reply ("acknowledge") data.
 */
static struct {
	struct mutex lock;
	struct completion work;
	u8 ape_opp;
	struct {
		u8 header;
		u8 arm_opp;
@@ -816,6 +818,11 @@ int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
	return 0;
}

u8 db8500_prcmu_get_power_state_result(void)
{
	return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
}

/* This function should only be called while mb0_transfer.lock is held. */
static void config_wakeups(void)
{
@@ -965,6 +972,52 @@ int db8500_prcmu_set_ddr_opp(u8 opp)
	return 0;
}

/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
static void request_even_slower_clocks(bool enable)
{
	void __iomem *clock_reg[] = {
		PRCM_ACLK_MGT,
		PRCM_DMACLK_MGT
	};
	unsigned long flags;
	unsigned int i;

	spin_lock_irqsave(&clk_mgt_lock, flags);

	/* Grab the HW semaphore. */
	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
		cpu_relax();

	for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
		u32 val;
		u32 div;

		val = readl(clock_reg[i]);
		div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
		if (enable) {
			if ((div <= 1) || (div > 15)) {
				pr_err("prcmu: Bad clock divider %d in %s\n",
					div, __func__);
				goto unlock_and_return;
			}
			div <<= 1;
		} else {
			if (div <= 2)
				goto unlock_and_return;
			div >>= 1;
		}
		val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
			(div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
		writel(val, clock_reg[i]);
	}

unlock_and_return:
	/* Release the HW semaphore. */
	writel(0, PRCM_SEM);

	spin_unlock_irqrestore(&clk_mgt_lock, flags);
}

/**
 * db8500_set_ape_opp - set the appropriate APE OPP
 * @opp: The new APE operating point to which transition is to be made
@@ -976,14 +1029,24 @@ int db8500_prcmu_set_ape_opp(u8 opp)
{
	int r = 0;

	if (opp == mb1_transfer.ape_opp)
		return 0;

	mutex_lock(&mb1_transfer.lock);

	if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
		request_even_slower_clocks(false);

	if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
		goto skip_message;

	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
		cpu_relax();

	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
	writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
	writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
	writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
		(tcdm_base + PRCM_REQ_MB1_APE_OPP));

	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
	wait_for_completion(&mb1_transfer.work);
@@ -992,6 +1055,13 @@ int db8500_prcmu_set_ape_opp(u8 opp)
		(mb1_transfer.ack.ape_opp != opp))
		r = -EIO;

skip_message:
	if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
		(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
		request_even_slower_clocks(true);
	if (!r)
		mb1_transfer.ape_opp = opp;

	mutex_unlock(&mb1_transfer.lock);

	return r;
@@ -2631,6 +2701,7 @@ void __init db8500_prcmu_early_init(void)
	init_completion(&mb0_transfer.ac_wake_work);
	mutex_init(&mb1_transfer.lock);
	init_completion(&mb1_transfer.work);
	mb1_transfer.ape_opp = APE_NO_CHANGE;
	mutex_init(&mb2_transfer.lock);
	init_completion(&mb2_transfer.work);
	spin_lock_init(&mb2_transfer.auto_pm_lock);
+25 −0
Original line number Diff line number Diff line
@@ -457,6 +457,25 @@ enum hw_acc_dev {
	NUM_HW_ACC
};

/**
 * enum prcmu_power_status - results from set_power_state
 * @PRCMU_SLEEP_OK: Sleep went ok
 * @PRCMU_DEEP_SLEEP_OK: DeepSleep went ok
 * @PRCMU_IDLE_OK: Idle went ok
 * @PRCMU_DEEPIDLE_OK: DeepIdle went ok
 * @PRCMU_PRCMU2ARMPENDINGIT_ER: Pending interrupt detected
 * @PRCMU_ARMPENDINGIT_ER: Pending interrupt detected
 *
 */
enum prcmu_power_status {
	PRCMU_SLEEP_OK			= 0xf3,
	PRCMU_DEEP_SLEEP_OK		= 0xf6,
	PRCMU_IDLE_OK			= 0xf0,
	PRCMU_DEEPIDLE_OK		= 0xe3,
	PRCMU_PRCMU2ARMPENDINGIT_ER	= 0x91,
	PRCMU_ARMPENDINGIT_ER		= 0x93,
};

/*
 * Definitions for autonomous power management configuration.
 */
@@ -544,6 +563,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 val);

void db8500_prcmu_system_reset(u16 reset_code);
int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll);
u8 db8500_prcmu_get_power_state_result(void);
void db8500_prcmu_enable_wakeups(u32 wakeups);
int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state);
int db8500_prcmu_request_clock(u8 clock, bool enable);
@@ -699,6 +719,11 @@ static inline int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk,
	return 0;
}

static inline u8 db8500_prcmu_get_power_state_result(void)
{
	return 0;
}

static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {}

static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+13 −2
Original line number Diff line number Diff line
@@ -185,12 +185,14 @@ enum prcmu_clock {
 * @APE_NO_CHANGE: The APE operating point is unchanged
 * @APE_100_OPP: The new APE operating point is ape100opp
 * @APE_50_OPP: 50%
 * @APE_50_PARTLY_25_OPP: 50%, except some clocks at 25%.
 */
enum ape_opp {
	APE_OPP_INIT = 0x00,
	APE_NO_CHANGE = 0x01,
	APE_100_OPP = 0x02,
	APE_50_OPP = 0x03
	APE_50_OPP = 0x03,
	APE_50_PARTLY_25_OPP = 0xFF,
};

/**
@@ -271,6 +273,14 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
			keep_ap_pll);
}

static inline u8 prcmu_get_power_state_result(void)
{
	if (cpu_is_u5500())
		return -EINVAL;
	else
		return db8500_prcmu_get_power_state_result();
}

static inline int prcmu_set_epod(u16 epod_id, u8 epod_state)
{
	if (cpu_is_u5500())
@@ -663,9 +673,10 @@ static inline int prcmu_stop_temp_sense(void)
/* PRCMU QoS APE OPP class */
#define PRCMU_QOS_APE_OPP 1
#define PRCMU_QOS_DDR_OPP 2
#define PRCMU_QOS_ARM_OPP 3
#define PRCMU_QOS_DEFAULT_VALUE -1

#ifdef CONFIG_UX500_PRCMU_QOS_POWER
#ifdef CONFIG_DBX500_PRCMU_QOS_POWER

unsigned long prcmu_qos_get_cpufreq_opp_delay(void);
void prcmu_qos_set_cpufreq_opp_delay(unsigned long);