summaryrefslogtreecommitdiff
path: root/drivers/mfd/db5500-prcmu.c
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-07-05 14:40:21 +0200
committerPhilippe Langlais <philippe.langlais@linaro.org>2011-07-22 15:51:08 +0200
commit1b91ff8943719a129cfc33bc9646b3a6122937b3 (patch)
tree8cc79140377f3946c61070be6410f313833a4fab /drivers/mfd/db5500-prcmu.c
parent9f36e7110eb201aafcd60c16d617d139db7fabbb (diff)
ux500: fix PRCMU boot problem, use new MFD driver & code cleanup
Signed-off-by: Philippe Langlais <philippe.langlais@linaro.org>
Diffstat (limited to 'drivers/mfd/db5500-prcmu.c')
-rw-r--r--drivers/mfd/db5500-prcmu.c330
1 files changed, 327 insertions, 3 deletions
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index 9dbb3cab4a6..9cd9da40d87 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -20,9 +20,9 @@
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
-#include <linux/mfd/db5500-prcmu.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
+#include <mach/prcmu.h>
#include <mach/db5500-regs.h>
#include "db5500-prcmu-regs.h"
@@ -64,6 +64,19 @@
#define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
#define PRCM_ACK_MB7 (tcdm_base + 0xF08)
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_EPOD_CLIENT (PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_EPOD_STATE (PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_CLK_CLIENT (PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_CLK_STATE (PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_PLL_CLIENT (PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_PLL_STATE (PRCM_REQ_MB2 + 0x5)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_EPOD_STATUS (PRCM_ACK_MB2 + 0x2)
+#define PRCM_ACK_MB2_CLK_STATUS (PRCM_ACK_MB2 + 0x6)
+#define PRCM_ACK_MB2_PLL_STATUS (PRCM_ACK_MB2 + 0xA)
+
enum mb_return_code {
RC_SUCCESS,
RC_FAIL,
@@ -80,11 +93,29 @@ enum mb0_header {
};
/* Mailbox 5 headers. */
+enum mb2_header {
+ MB2H_EPOD_REQUEST = 1,
+ MB2H_CLK_REQUEST,
+ MB2H_PLL_REQUEST,
+};
+
+/* Mailbox 5 headers. */
enum mb5_header {
MB5H_I2C_WRITE = 1,
MB5H_I2C_READ,
};
+enum epod_state {
+ EPOD_OFF,
+ EPOD_ON,
+};
+enum db5500_prcmu_pll {
+ DB5500_PLL_SOC0,
+ DB5500_PLL_SOC1,
+ DB5500_PLL_DDR,
+ DB5500_NUM_PLL_ID,
+};
+
/* Request mailbox 5 fields. */
#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
@@ -109,15 +140,18 @@ enum mb5_header {
#define PRCMU_DSI_CLOCK_SETTING 0x00000128
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
-#define PRCMU_PLLDSI_FREQ_SETTING 0x0004013C
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
-#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
#define PRCMU_ENABLE_PLLDSI 0x00000001
#define PRCMU_DISABLE_PLLDSI 0x00000000
#define PRCMU_DSI_RESET_SW 0x00000003
+#define PRCMU_RESOUTN0_PIN 0x00000001
+#define PRCMU_RESOUTN1_PIN 0x00000002
+#define PRCMU_RESOUTN2_PIN 0x00000004
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
@@ -130,6 +164,26 @@ static struct {
} mb0_transfer;
/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req: Request data that need to persist between requests.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 epod_states[DB5500_NUM_EPOD_ID];
+ u8 pll_states[DB5500_NUM_PLL_ID];
+ } req;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb2_transfer;
+
+/*
* mb5_transfer - state needed for mailbox 5 communication.
* @lock: The transaction lock.
* @work: The transaction completion structure.
@@ -148,6 +202,159 @@ static struct {
/* PRCMU TCDM base IO address. */
static __iomem void *tcdm_base;
+struct clk_mgt {
+ unsigned int offset;
+ u32 pllsw;
+};
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { \
+ (PRCM_##_name##_MGT_OFF), 0 \
+}
+static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+ CLK_MGT_ENTRY(SGACLK),
+ CLK_MGT_ENTRY(UARTCLK),
+ CLK_MGT_ENTRY(MSP02CLK),
+ CLK_MGT_ENTRY(I2CCLK),
+ CLK_MGT_ENTRY(SDMMCCLK),
+ CLK_MGT_ENTRY(PER1CLK),
+ CLK_MGT_ENTRY(PER2CLK),
+ CLK_MGT_ENTRY(PER3CLK),
+ CLK_MGT_ENTRY(PER5CLK),
+ CLK_MGT_ENTRY(PER6CLK),
+ CLK_MGT_ENTRY(PWMCLK),
+ CLK_MGT_ENTRY(IRDACLK),
+ CLK_MGT_ENTRY(IRRCCLK),
+ CLK_MGT_ENTRY(HDMICLK),
+ CLK_MGT_ENTRY(APEATCLK),
+ CLK_MGT_ENTRY(APETRACECLK),
+ CLK_MGT_ENTRY(MCDECLK),
+ CLK_MGT_ENTRY(DSIALTCLK),
+ CLK_MGT_ENTRY(DMACLK),
+ CLK_MGT_ENTRY(B2R2CLK),
+ CLK_MGT_ENTRY(TVCLK),
+ CLK_MGT_ENTRY(RNGCLK),
+ CLK_MGT_ENTRY(SIACLK),
+ CLK_MGT_ENTRY(SVACLK),
+};
+
+static int request_timclk(bool enable)
+{
+ u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+ if (!enable)
+ val |= PRCM_TCR_STOP_TIMERS;
+ writel(val, (_PRCMU_BASE + PRCM_TCR));
+
+ return 0;
+}
+
+static int request_reg_clock(u8 clock, bool enable)
+{
+ u32 val;
+ unsigned long flags;
+
+ WARN_ON(!clk_mgt[clock].offset);
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ if (enable) {
+ val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+ } else {
+ clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+ val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+ }
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/*
+ * request_pll() - Request for a pll to be enabled or disabled.
+ * @pll: The pll for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+static int request_pll(u8 pll, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(pll >= DB5500_NUM_PLL_ID);
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(PRCM_MBOX_CPU_VAL & MBOX_BIT(2))
+ cpu_relax();
+
+ mb2_transfer.req.pll_states[pll] = enable;
+
+ /* fill in mailbox */
+ writeb(pll, PRCM_REQ_MB2_PLL_CLIENT);
+ writeb(mb2_transfer.req.pll_states[pll], PRCM_REQ_MB2_PLL_STATE);
+
+ writeb(MB2H_PLL_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(500))) {
+ pr_err("prcmu: set_pll() failed.\n"
+ "prcmu: Please check your firmware version.\n");
+ r = -EIO;
+ WARN(1, "Failed to set pll");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_PLL_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+
+ return r;
+}
+
+void prcmu_enable_wakeups(u32 wakeups)
+{
+}
+
+/**
+ * prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock: The clock for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int prcmu_request_clock(u8 clock, bool enable)
+{
+ if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
+ return request_timclk(enable);
+ else if (clock == PRCMU_PLLSOC0)
+ return request_pll(DB5500_PLL_SOC0, enable);
+ else if (clock == PRCMU_PLLSOC1)
+ return request_pll(DB5500_PLL_SOC1, enable);
+ else if (clock == PRCMU_PLLDDR)
+ return request_pll(DB5500_PLL_DDR, enable);
+ else if (clock == PRCMU_SYSCLK)
+ return -EINVAL;
+ else
+ return -EINVAL;
+}
+
/**
* db5500_prcmu_abb_read() - Read register value(s) from the ABB.
* @slave: The I2C slave address.
@@ -230,6 +437,34 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
return r;
}
+int prcmu_resetout(u8 resoutn, u8 state)
+{
+ int offset;
+ int pin = -1;
+
+ offset = state > 0 ? PRCM_RESOUTN_SET_OFFSET : PRCM_RESOUTN_CLR_OFFSET;
+
+ switch (resoutn) {
+ case 0:
+ pin = PRCMU_RESOUTN0_PIN;
+ break;
+ case 1:
+ pin = PRCMU_RESOUTN1_PIN;
+ break;
+ case 2:
+ pin = PRCMU_RESOUTN2_PIN;
+ default:
+ break;
+ }
+
+ if (pin > 0)
+ writel(pin, _PRCMU_BASE + offset);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
int db5500_prcmu_enable_dsipll(void)
{
int i;
@@ -294,6 +529,72 @@ static void ack_dbb_wakeup(void)
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
+int prcmu_set_epod(u16 epod_id, u8 epod_state)
+{
+ int r = 0;
+ bool ram_retention = false;
+
+ /* check argument */
+ BUG_ON(epod_id < DB5500_EPOD_ID_BASE);
+ BUG_ON(epod_state > EPOD_STATE_ON);
+
+ epod_id &= 0xFF;
+ BUG_ON(epod_id > DB5500_NUM_EPOD_ID);
+
+ /* TODO: FW does not take retention for ESRAM12?
+ set flag if retention is possible */
+ switch (epod_id) {
+ case DB5500_EPOD_ID_ESRAM12:
+ ram_retention = true;
+ break;
+ }
+
+ /* check argument */
+ /* BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention); */
+
+ /* get lock */
+ mutex_lock(&mb2_transfer.lock);
+
+ /* wait for mailbox */
+ while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* PRCMU FW can only handle on or off */
+ if (epod_state == EPOD_STATE_ON)
+ mb2_transfer.req.epod_states[epod_id] = EPOD_ON;
+ else if (epod_state == EPOD_STATE_OFF)
+ mb2_transfer.req.epod_states[epod_id] = EPOD_OFF;
+ else {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+
+ /* fill in mailbox */
+ writeb(epod_id, PRCM_REQ_MB2_EPOD_CLIENT);
+ writeb(mb2_transfer.req.epod_states[epod_id], PRCM_REQ_MB2_EPOD_STATE);
+
+ writeb(MB2H_EPOD_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(500))) {
+ pr_err("prcmu: set_epod() failed.\n"
+ "prcmu: Please check your firmware version.\n");
+ r = -EIO;
+ WARN(1, "Failed to set epod");
+ goto unlock_and_return;
+ }
+
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_EPOD_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
static inline void print_unknown_header_warning(u8 n, u8 header)
{
pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
@@ -327,7 +628,28 @@ static bool read_mailbox_1(void)
static bool read_mailbox_2(void)
{
+ u8 header;
+
+ header = readb(PRCM_ACK_MB2_HEADER);
+ mb2_transfer.ack.header = header;
+ switch (header) {
+ case MB2H_EPOD_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_EPOD_STATUS);
+ break;
+ case MB2H_CLK_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_CLK_STATUS);
+ break;
+ case MB2H_PLL_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_PLL_STATUS);
+ break;
+ default:
+ writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: Wrong ACK received for MB2 request \n");
+ return false;
+ break;
+ }
writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR);
+ complete(&mb2_transfer.work);
return false;
}
@@ -418,7 +740,9 @@ void __init db5500_prcmu_early_init(void)
{
tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE);
spin_lock_init(&mb0_transfer.lock);
+ mutex_init(&mb2_transfer.lock);
mutex_init(&mb5_transfer.lock);
+ init_completion(&mb2_transfer.work);
init_completion(&mb5_transfer.work);
}