blob: e7eca7bdb73ac8bfae757d72fbf0576b6278783e [file] [log] [blame]
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/iopoll.h>
#include <linux/clk.h>
#include <asm/processor.h>
#include <mach/msm_iomap.h>
#include <mach/clk-provider.h>
#include "clock-mdss-8226.h"
#define REG_R(addr) readl_relaxed(addr)
#define REG_W(data, addr) writel_relaxed(data, addr)
#define GDSC_PHYS 0xFD8C2304
#define GDSC_SIZE 0x4
#define DSI_PHY_PHYS 0xFD922800
#define DSI_PHY_SIZE 0x00000800
static unsigned char *mdss_dsi_base;
static unsigned char *gdsc_base;
static int pll_byte_clk_rate;
static int pll_pclk_rate;
static int pll_initialized;
static struct clk *mdss_dsi_ahb_clk;
static unsigned long dsi_pll_rate;
void __init mdss_clk_ctrl_pre_init(struct clk *ahb_clk)
{
BUG_ON(ahb_clk == NULL);
gdsc_base = ioremap(GDSC_PHYS, GDSC_SIZE);
if (!gdsc_base)
pr_err("%s: unable to remap gdsc base", __func__);
mdss_dsi_base = ioremap(DSI_PHY_PHYS, DSI_PHY_SIZE);
if (!mdss_dsi_base)
pr_err("%s: unable to remap dsi base", __func__);
mdss_dsi_ahb_clk = ahb_clk;
}
#define PLL_POLL_MAX_READS 10
#define PLL_POLL_TIMEOUT_US 50
static int mdss_gdsc_enabled(void)
{
if (!gdsc_base)
return 0;
return !!(readl_relaxed(gdsc_base) & BIT(31));
}
static int mdss_dsi_check_pll_lock(void)
{
u32 status;
clk_prepare_enable(mdss_dsi_ahb_clk);
/* poll for PLL ready status */
if (readl_poll_timeout_noirq((mdss_dsi_base + 0x02c0),
status,
((status & BIT(0)) == 1),
PLL_POLL_MAX_READS, PLL_POLL_TIMEOUT_US)) {
pr_err("%s: DSI PLL status=%x failed to Lock\n",
__func__, status);
pll_initialized = 0;
} else {
pll_initialized = 1;
}
clk_disable_unprepare(mdss_dsi_ahb_clk);
return pll_initialized;
}
static long mdss_dsi_pll_byte_round_rate(struct clk *c, unsigned long rate)
{
if (pll_initialized) {
return pll_byte_clk_rate;
} else {
pr_err("%s: DSI PLL not configured\n", __func__);
return -EINVAL;
}
}
static long mdss_dsi_pll_pixel_round_rate(struct clk *c, unsigned long rate)
{
if (pll_initialized) {
return pll_pclk_rate;
} else {
pr_err("%s: Configure Byte clk first\n", __func__);
return -EINVAL;
}
}
static int mdss_dsi_pll_pixel_set_rate(struct clk *c, unsigned long rate)
{
if (pll_initialized) {
pll_pclk_rate = (rate * 3) / 2;
pr_debug("%s: pll_pclk_rate=%d\n", __func__, pll_pclk_rate);
return 0;
} else {
pr_err("%s: Configure Byte clk first\n", __func__);
return -EINVAL;
}
}
static int __mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
{
pr_debug("%s: rate=%ld\n", __func__, rate);
if (pll_initialized)
return 0;
REG_W(0x70, mdss_dsi_base + 0x0230); /* LPFC1 CFG */
REG_W(0x08, mdss_dsi_base + 0x022c); /* LPFR CFG */
REG_W(0x02, mdss_dsi_base + 0x0210); /* VREG CFG */
REG_W(0x00, mdss_dsi_base + 0x0204); /* postDiv1 */
REG_W(0x01, mdss_dsi_base + 0x0200); /* REFCLK CFG */
REG_W(0x03, mdss_dsi_base + 0x0224); /* postDiv2 */
REG_W(0x00, mdss_dsi_base + 0x0238); /* SDM CFG0 */
REG_W(0x0b, mdss_dsi_base + 0x023c); /* SDM CFG1 */
REG_W(0x00, mdss_dsi_base + 0x0240); /* SDM CFG2 */
REG_W(0x6c, mdss_dsi_base + 0x0244); /* SDM CFG3 */
REG_W(0x02, mdss_dsi_base + 0x0208); /* ChgPump */
REG_W(0x31, mdss_dsi_base + 0x020c); /* VCOLPF CFG */
REG_W(0x15, mdss_dsi_base + 0x0234); /* LPFC2 CFG */
REG_W(0x30, mdss_dsi_base + 0x0284); /* CAL CFG6 */
REG_W(0x00, mdss_dsi_base + 0x0288); /* CAL CFG7 */
REG_W(0x60, mdss_dsi_base + 0x028c); /* CAL CFG8 */
REG_W(0x00, mdss_dsi_base + 0x0290); /* CAL CFG9 */
REG_W(0xdd, mdss_dsi_base + 0x0294); /* CAL CFG10 */
REG_W(0x01, mdss_dsi_base + 0x0298); /* CAL CFG11 */
REG_W(0x03, mdss_dsi_base + 0x0228); /* postDiv3 */
REG_W(0x2b, mdss_dsi_base + 0x0278); /* Cal CFG3 */
REG_W(0x66, mdss_dsi_base + 0x027c); /* Cal CFG4 */
REG_W(0x05, mdss_dsi_base + 0x0264); /* LKDET CFG2 */
REG_W(0x00, mdss_dsi_base + 0x0248); /* SDM CFG4 */
REG_W(0x00, mdss_dsi_base + 0x0214); /* PWRGEN CFG */
REG_W(0x0a, mdss_dsi_base + 0x026c); /* CAL CFG0 */
REG_W(0x20, mdss_dsi_base + 0x029c); /* EFUSE CFG */
dsi_pll_rate = rate;
pll_byte_clk_rate = rate;
pr_debug("%s: PLL initialized. bcl=%d\n", __func__, pll_byte_clk_rate);
pll_initialized = 1;
return 0;
}
static int mdss_dsi_pll_byte_set_rate(struct clk *c, unsigned long rate)
{
int ret;
clk_prepare_enable(mdss_dsi_ahb_clk);
ret = __mdss_dsi_pll_byte_set_rate(c, rate);
clk_disable_unprepare(mdss_dsi_ahb_clk);
return ret;
}
static void mdss_dsi_uniphy_pll_lock_detect_setting(void)
{
REG_W(0x04, mdss_dsi_base + 0x0264); /* LKDetect CFG2 */
udelay(100);
REG_W(0x05, mdss_dsi_base + 0x0264); /* LKDetect CFG2 */
udelay(500);
}
static void mdss_dsi_uniphy_pll_sw_reset(void)
{
REG_W(0x01, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
udelay(1);
REG_W(0x00, mdss_dsi_base + 0x0268); /* PLL TEST CFG */
udelay(1);
}
static int __mdss_dsi_pll_enable(struct clk *c)
{
u32 status;
u32 max_reads, timeout_us;
int i;
if (!pll_initialized) {
if (dsi_pll_rate)
__mdss_dsi_pll_byte_set_rate(c, dsi_pll_rate);
else
pr_err("%s: Calling clk_en before set_rate\n",
__func__);
}
mdss_dsi_uniphy_pll_sw_reset();
/* PLL power up */
/* Add HW recommended delay between
register writes for the update to propagate */
REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(20);
REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(100);
REG_W(0x0d, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(20);
REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(200);
for (i = 0; i < 3; i++) {
mdss_dsi_uniphy_pll_lock_detect_setting();
/* poll for PLL ready status */
max_reads = 5;
timeout_us = 100;
if (readl_poll_timeout_noirq((mdss_dsi_base + 0x02c0),
status,
((status & 0x01) == 1),
max_reads, timeout_us)) {
pr_debug("%s: DSI PLL status=%x failed to Lock\n",
__func__, status);
pr_debug("%s:Trying to power UP PLL again\n",
__func__);
} else
break;
mdss_dsi_uniphy_pll_sw_reset();
udelay(1000);
/* Add HW recommended delay between
register writes for the update to propagate */
REG_W(0x01, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(20);
REG_W(0x05, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(100);
REG_W(0x0d, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(20);
REG_W(0x0f, mdss_dsi_base + 0x0220); /* GLB CFG */
udelay(200);
}
if ((status & 0x01) != 1) {
pr_err("%s: DSI PLL status=%x failed to Lock\n",
__func__, status);
return -EINVAL;
}
pr_debug("%s: **** PLL Lock success\n", __func__);
return 0;
}
static void __mdss_dsi_pll_disable(void)
{
writel_relaxed(0x00, mdss_dsi_base + 0x0220); /* GLB CFG */
pr_debug("%s: **** disable pll Initialize\n", __func__);
pll_initialized = 0;
}
static DEFINE_SPINLOCK(dsipll_lock);
static int dsipll_refcount;
static void mdss_dsi_pll_disable(struct clk *c)
{
unsigned long flags;
spin_lock_irqsave(&dsipll_lock, flags);
if (WARN(dsipll_refcount == 0, "DSI PLL clock is unbalanced"))
goto out;
if (dsipll_refcount == 1)
__mdss_dsi_pll_disable();
dsipll_refcount--;
out:
spin_unlock_irqrestore(&dsipll_lock, flags);
}
static int mdss_dsi_pll_enable(struct clk *c)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dsipll_lock, flags);
if (dsipll_refcount == 0) {
ret = __mdss_dsi_pll_enable(c);
if (ret < 0)
goto out;
}
dsipll_refcount++;
out:
spin_unlock_irqrestore(&dsipll_lock, flags);
return ret;
}
/* todo: Adjust these values appropriately */
static enum handoff mdss_dsi_pll_byte_handoff(struct clk *c)
{
if (mdss_gdsc_enabled() && mdss_dsi_check_pll_lock()) {
c->rate = 59000000;
dsi_pll_rate = 59000000;
pll_byte_clk_rate = 59000000;
pll_pclk_rate = 117000000;
dsipll_refcount++;
return HANDOFF_ENABLED_CLK;
}
return HANDOFF_DISABLED_CLK;
}
/* todo: Adjust these values appropriately */
static enum handoff mdss_dsi_pll_pixel_handoff(struct clk *c)
{
if (mdss_gdsc_enabled() && mdss_dsi_check_pll_lock()) {
c->rate = 117000000;
dsipll_refcount++;
return HANDOFF_ENABLED_CLK;
}
return HANDOFF_DISABLED_CLK;
}
struct clk_ops clk_ops_dsi_pixel_pll = {
.enable = mdss_dsi_pll_enable,
.disable = mdss_dsi_pll_disable,
.set_rate = mdss_dsi_pll_pixel_set_rate,
.round_rate = mdss_dsi_pll_pixel_round_rate,
.handoff = mdss_dsi_pll_pixel_handoff,
};
struct clk_ops clk_ops_dsi_byte_pll = {
.enable = mdss_dsi_pll_enable,
.disable = mdss_dsi_pll_disable,
.set_rate = mdss_dsi_pll_byte_set_rate,
.round_rate = mdss_dsi_pll_byte_round_rate,
.handoff = mdss_dsi_pll_byte_handoff,
};