blob: 9d9481b7a123ffc7a3da4d0131bdd8f723cc1fbb [file] [log] [blame]
/*
* Copyright (C) 2009-2011 ST-Ericsson SA
* Copyright (C) 2009 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mfd/ab8500/sysctrl.h>
#include <linux/workqueue.h>
#include <linux/regulator/consumer.h>
#include <asm/mach-types.h>
#include <plat/pincfg.h>
#include <mach/hardware.h>
#include <mach/prcmu.h>
#include "clock.h"
#include "pins-db8500.h"
#define PRCM_SDMMCCLK_MGT 0x024
#define PRCM_TCR 0x1C8
#define PRCM_TCR_STOPPED (1 << 16)
#define PRCM_TCR_DOZE_MODE (1 << 17)
#define SD_CLK_DIV_MASK 0x1F
#define SD_CLK_DIV_VAL 8
static DEFINE_MUTEX(soc1_pll_mutex);
static DEFINE_MUTEX(sysclk_mutex);
static DEFINE_MUTEX(ab_ulpclk_mutex);
static DEFINE_MUTEX(ab_intclk_mutex);
static struct delayed_work sysclk_disable_work;
/* PLL operations. */
static int clk_pllsrc_enable(struct clk *clk)
{
/* To enable pll */
return 0;
}
static void clk_pllsrc_disable(struct clk *clk)
{
/* To disable pll */
}
static struct clkops pll_clk_ops = {
.enable = clk_pllsrc_enable,
.disable = clk_pllsrc_disable,
};
/* SysClk operations. */
static int request_sysclk(bool enable)
{
static int requests;
if ((enable && (requests++ == 0)) || (!enable && (--requests == 0)))
return prcmu_request_clock(PRCMU_SYSCLK, enable);
return 0;
}
static int sysclk_enable(struct clk *clk)
{
static bool swat_enable;
int r;
if (!swat_enable) {
r = ab8500_sysctrl_set(AB8500_SWATCTRL,
AB8500_SWATCTRL_SWATENABLE);
if (r)
return r;
swat_enable = true;
}
r = request_sysclk(true);
if (r)
return r;
if (clk->cg_sel) {
r = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1, (u8)clk->cg_sel);
if (r)
(void)request_sysclk(false);
}
return r;
}
static void sysclk_disable(struct clk *clk)
{
int r;
if (clk->cg_sel) {
r = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
(u8)clk->cg_sel);
if (r)
goto disable_failed;
}
r = request_sysclk(false);
if (r)
goto disable_failed;
return;
disable_failed:
pr_err("clock: failed to disable %s.\n", clk->name);
}
static struct clkops sysclk_ops = {
.enable = sysclk_enable,
.disable = sysclk_disable,
};
/* AB8500 UlpClk operations */
static int ab_ulpclk_enable(struct clk *clk)
{
int err;
if (clk->regulator == NULL) {
struct regulator *reg;
reg = regulator_get(NULL, "v-intcore");
if (IS_ERR(reg))
return PTR_ERR(reg);
clk->regulator = reg;
}
err = regulator_set_optimum_mode(clk->regulator, 1500);
if (unlikely(err < 0))
goto regulator_enable_error;
err = regulator_enable(clk->regulator);
if (unlikely(err))
goto regulator_enable_error;
err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCONF,
AB8500_SYSULPCLKCONF_ULPCLKCONF_MASK);
if (unlikely(err))
goto enable_error;
err = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
if (unlikely(err))
goto enable_error;
/* Unknown/undocumented PLL locking time => wait 1 ms. */
msleep(1);
return 0;
enable_error:
(void)regulator_disable(clk->regulator);
regulator_enable_error:
return err;
}
static void ab_ulpclk_disable(struct clk *clk)
{
int err;
err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
if (unlikely(regulator_disable(clk->regulator) || err))
goto out_err;
regulator_set_optimum_mode(clk->regulator, 0);
return;
out_err:
pr_err("clock: %s failed to disable %s.\n", __func__, clk->name);
}
static struct clkops ab_ulpclk_ops = {
.enable = ab_ulpclk_enable,
.disable = ab_ulpclk_disable,
};
/* AB8500 intclk operations */
enum ab_intclk_parent {
AB_INTCLK_PARENT_SYSCLK,
AB_INTCLK_PARENT_ULPCLK,
AB_INTCLK_PARENTS_END,
NUM_AB_INTCLK_PARENTS
};
static int ab_intclk_enable(struct clk *clk)
{
if (clk->parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
return ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
(1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
}
return 0;
}
static void ab_intclk_disable(struct clk *clk)
{
if (clk->parent == clk->parents[AB_INTCLK_PARENT_SYSCLK])
return;
if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK)) {
pr_err("clock: %s failed to disable %s.\n", __func__,
clk->name);
}
}
static int ab_intclk_set_parent(struct clk *clk, struct clk *parent)
{
int err;
if (!clk->enabled)
return 0;
err = __clk_enable(parent, clk->mutex);
if (unlikely(err))
goto parent_enable_error;
if (parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
err = ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
(1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
} else {
err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK);
}
if (unlikely(err))
goto config_error;
__clk_disable(clk->parent, clk->mutex);
return 0;
config_error:
__clk_disable(parent, clk->mutex);
parent_enable_error:
return err;
}
static struct clkops ab_intclk_ops = {
.enable = ab_intclk_enable,
.disable = ab_intclk_disable,
.set_parent = ab_intclk_set_parent,
};
/* AB8500 audio clock operations */
static int audioclk_enable(struct clk *clk)
{
return ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_AUDIOCLKENA);
}
static void audioclk_disable(struct clk *clk)
{
if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
AB8500_SYSULPCLKCTRL1_AUDIOCLKENA)) {
pr_err("clock: %s failed to disable %s.\n", __func__,
clk->name);
}
}
static struct clkops audioclk_ops = {
.enable = audioclk_enable,
.disable = audioclk_disable,
};
/* Primary camera clock operations */
static int clkout0_enable(struct clk *clk)
{
int r;
if (clk->regulator == NULL) {
struct regulator *reg;
reg = regulator_get(NULL, "v-ape");
if (IS_ERR(reg))
return PTR_ERR(reg);
clk->regulator = reg;
}
r = regulator_enable(clk->regulator);
if (r)
goto regulator_failed;
r = prcmu_config_clkout(0, PRCMU_CLKSRC_SYSCLK, 4);
if (r)
goto config_failed;
r = nmk_config_pin(GPIO227_CLKOUT1, false);
if (r)
goto gpio_failed;
return r;
gpio_failed:
(void)prcmu_config_clkout(0, PRCMU_CLKSRC_SYSCLK, 0);
config_failed:
(void)regulator_disable(clk->regulator);
regulator_failed:
return r;
}
static void clkout0_disable(struct clk *clk)
{
int r;
r = nmk_config_pin(GPIO227_GPIO, false);
if (r)
goto disable_failed;
(void)prcmu_config_clkout(0, PRCMU_CLKSRC_SYSCLK, 0);
(void)regulator_disable(clk->regulator);
return;
disable_failed:
pr_err("clock: failed to disable %s.\n", clk->name);
}
/* Touch screen/secondary camera clock operations. */
static int clkout1_enable(struct clk *clk)
{
int r;
if (clk->regulator == NULL) {
struct regulator *reg;
reg = regulator_get(NULL, "v-ape");
if (IS_ERR(reg))
return PTR_ERR(reg);
clk->regulator = reg;
}
r = regulator_enable(clk->regulator);
if (r)
goto regulator_failed;
r = prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 4);
if (r)
goto config_failed;
r = nmk_config_pin(GPIO228_CLKOUT2, false);
if (r)
goto gpio_failed;
return r;
gpio_failed:
(void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
config_failed:
(void)regulator_disable(clk->regulator);
regulator_failed:
return r;
}
static void clkout1_disable(struct clk *clk)
{
int r;
r = nmk_config_pin(GPIO228_GPIO, false);
if (r)
goto disable_failed;
(void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
(void)regulator_disable(clk->regulator);
return;
disable_failed:
pr_err("clock: failed to disable %s.\n", clk->name);
}
static struct clkops clkout0_ops = {
.enable = clkout0_enable,
.disable = clkout0_disable,
};
static struct clkops clkout1_ops = {
.enable = clkout1_enable,
.disable = clkout1_disable,
};
#define DEF_PER1_PCLK(_cg_bit, _name) \
DEF_PRCC_PCLK(_name, U8500_CLKRST1_BASE, _cg_bit, &per1clk)
#define DEF_PER2_PCLK(_cg_bit, _name) \
DEF_PRCC_PCLK(_name, U8500_CLKRST2_BASE, _cg_bit, &per2clk)
#define DEF_PER3_PCLK(_cg_bit, _name) \
DEF_PRCC_PCLK(_name, U8500_CLKRST3_BASE, _cg_bit, &per3clk)
#define DEF_PER5_PCLK(_cg_bit, _name) \
DEF_PRCC_PCLK(_name, U8500_CLKRST5_BASE, _cg_bit, &per5clk)
#define DEF_PER6_PCLK(_cg_bit, _name) \
DEF_PRCC_PCLK(_name, U8500_CLKRST6_BASE, _cg_bit, &per6clk)
#define DEF_PER1_KCLK(_cg_bit, _name, _parent) \
DEF_PRCC_KCLK(_name, U8500_CLKRST1_BASE, _cg_bit, _parent, &per1clk)
#define DEF_PER2_KCLK(_cg_bit, _name, _parent) \
DEF_PRCC_KCLK(_name, U8500_CLKRST2_BASE, _cg_bit, _parent, &per2clk)
#define DEF_PER3_KCLK(_cg_bit, _name, _parent) \
DEF_PRCC_KCLK(_name, U8500_CLKRST3_BASE, _cg_bit, _parent, &per3clk)
#define DEF_PER5_KCLK(_cg_bit, _name, _parent) \
DEF_PRCC_KCLK(_name, U8500_CLKRST5_BASE, _cg_bit, _parent, &per5clk)
#define DEF_PER6_KCLK(_cg_bit, _name, _parent) \
DEF_PRCC_KCLK(_name, U8500_CLKRST6_BASE, _cg_bit, _parent, &per6clk)
#define DEF_MTU_CLK(_cg_sel, _name, _bus_parent) \
struct clk _name = { \
.name = #_name, \
.ops = &mtu_clk_ops, \
.cg_sel = _cg_sel, \
.bus_parent = _bus_parent, \
}
/* Clock sources. */
static struct clk soc0_pll = {
.name = "soc0_pll",
.ops = &pll_clk_ops,
};
static struct clk soc1_pll = {
.name = "soc1_pll",
.ops = &prcmu_clk_ops,
.cg_sel = PRCMU_PLLSOC1,
.mutex = &soc1_pll_mutex,
};
static struct clk ddr_pll = {
.name = "ddr_pll",
.ops = &pll_clk_ops,
};
static struct clk ulp38m4 = {
.name = "ulp38m4",
};
static struct clk sysclk = {
.name = "sysclk",
.ops = &sysclk_ops,
.rate = 38400000,
.mutex = &sysclk_mutex,
};
static struct clk sysclk2 = {
.name = "sysclk2",
.ops = &sysclk_ops,
.cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
.mutex = &sysclk_mutex,
};
static struct clk sysclk3 = {
.name = "sysclk3",
.ops = &sysclk_ops,
.cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
.mutex = &sysclk_mutex,
};
static struct clk sysclk4 = {
.name = "sysclk4",
.ops = &sysclk_ops,
.cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
.mutex = &sysclk_mutex,
};
static struct clk rtc32k = {
.name = "rtc32k",
.rate = 32768,
};
static struct clk clkout0 = {
.name = "clkout0",
.ops = &clkout0_ops,
.parent = &sysclk,
.rate = 9600000,
.mutex = &sysclk_mutex,
};
static struct clk clkout1 = {
.name = "clkout1",
.ops = &clkout1_ops,
.parent = &sysclk,
.rate = 9600000,
.mutex = &sysclk_mutex,
};
static struct clk ab_ulpclk = {
.name = "ab_ulpclk",
.ops = &ab_ulpclk_ops,
.rate = 38400000,
.mutex = &ab_ulpclk_mutex,
};
static struct clk *ab_intclk_parents[NUM_AB_INTCLK_PARENTS] = {
[AB_INTCLK_PARENT_SYSCLK] = &sysclk,
[AB_INTCLK_PARENT_ULPCLK] = &ab_ulpclk,
[AB_INTCLK_PARENTS_END] = NULL,
};
static struct clk ab_intclk = {
.name = "ab_intclk",
.ops = &ab_intclk_ops,
.mutex = &ab_intclk_mutex,
.parent = &sysclk,
.parents = ab_intclk_parents,
};
static struct clk audioclk = {
.name = "audioclk",
.ops = &audioclk_ops,
.mutex = &ab_intclk_mutex,
.parent = &ab_intclk,
};
static DEF_PRCMU_CLK(sgaclk, PRCMU_SGACLK, 320000000);
static DEF_PRCMU_CLK(uartclk, PRCMU_UARTCLK, 38400000);
static DEF_PRCMU_CLK(msp02clk, PRCMU_MSP02CLK, 19200000);
static DEF_PRCMU_CLK(msp1clk, PRCMU_MSP1CLK, 19200000);
static DEF_PRCMU_CLK(i2cclk, PRCMU_I2CCLK, 24000000);
static DEF_PRCMU_CLK(slimclk, PRCMU_SLIMCLK, 19200000);
static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
static DEF_PRCMU_CLK(per5clk, PRCMU_PER5CLK, 133330000);
static DEF_PRCMU_CLK(per6clk, PRCMU_PER6CLK, 133330000);
static DEF_PRCMU_CLK(per7clk, PRCMU_PER7CLK, 100000000);
static DEF_PRCMU_CLK(lcdclk, PRCMU_LCDCLK, 48000000);
static DEF_PRCMU_OPP100_CLK(bmlclk, PRCMU_BMLCLK, 200000000);
static DEF_PRCMU_CLK(hsitxclk, PRCMU_HSITXCLK, 100000000);
static DEF_PRCMU_CLK(hsirxclk, PRCMU_HSIRXCLK, 200000000);
static DEF_PRCMU_CLK(hdmiclk, PRCMU_HDMICLK, 76800000);
static DEF_PRCMU_CLK(apeatclk, PRCMU_APEATCLK, 160000000);
static DEF_PRCMU_CLK(apetraceclk, PRCMU_APETRACECLK, 160000000);
static DEF_PRCMU_CLK(mcdeclk, PRCMU_MCDECLK, 160000000);
static DEF_PRCMU_OPP100_CLK(ipi2cclk, PRCMU_IPI2CCLK, 24000000);
static DEF_PRCMU_CLK(dsialtclk, PRCMU_DSIALTCLK, 384000000);
static DEF_PRCMU_CLK(dmaclk, PRCMU_DMACLK, 200000000);
static DEF_PRCMU_CLK(b2r2clk, PRCMU_B2R2CLK, 200000000);
static DEF_PRCMU_CLK(tvclk, PRCMU_TVCLK, 76800000);
/* TODO: For SSPCLK, the spec says 24MHz, while the old driver says 48MHz. */
static DEF_PRCMU_CLK(sspclk, PRCMU_SSPCLK, 24000000);
static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 19200000);
static DEF_PRCMU_CLK(uiccclk, PRCMU_UICCCLK, 48000000);
static DEF_PRCMU_CLK(timclk, PRCMU_TIMCLK, 2400000);
/* 100 MHz until 3.0.1, 50 MHz Since PRCMU FW 3.0.2 */
static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
/* PRCC PClocks */
static DEF_PER1_PCLK(0, p1_pclk0);
static DEF_PER1_PCLK(1, p1_pclk1);
static DEF_PER1_PCLK(2, p1_pclk2);
static DEF_PER1_PCLK(3, p1_pclk3);
static DEF_PER1_PCLK(4, p1_pclk4);
static DEF_PER1_PCLK(5, p1_pclk5);
static DEF_PER1_PCLK(6, p1_pclk6);
static DEF_PER1_PCLK(7, p1_pclk7);
static DEF_PER1_PCLK(8, p1_pclk8);
static DEF_PER1_PCLK(9, p1_pclk9);
static DEF_PER1_PCLK(10, p1_pclk10);
static DEF_PER1_PCLK(11, p1_pclk11);
static DEF_PER2_PCLK(0, p2_pclk0);
static DEF_PER2_PCLK(1, p2_pclk1);
static DEF_PER2_PCLK(2, p2_pclk2);
static DEF_PER2_PCLK(3, p2_pclk3);
static DEF_PER2_PCLK(4, p2_pclk4);
static DEF_PER2_PCLK(5, p2_pclk5);
static DEF_PER2_PCLK(6, p2_pclk6);
static DEF_PER2_PCLK(7, p2_pclk7);
static DEF_PER2_PCLK(8, p2_pclk8);
static DEF_PER2_PCLK(9, p2_pclk9);
static DEF_PER2_PCLK(10, p2_pclk10);
static DEF_PER2_PCLK(11, p2_pclk11);
static DEF_PER3_PCLK(0, p3_pclk0);
static DEF_PER3_PCLK(1, p3_pclk1);
static DEF_PER3_PCLK(2, p3_pclk2);
static DEF_PER3_PCLK(3, p3_pclk3);
static DEF_PER3_PCLK(4, p3_pclk4);
static DEF_PER3_PCLK(5, p3_pclk5);
static DEF_PER3_PCLK(6, p3_pclk6);
static DEF_PER3_PCLK(7, p3_pclk7);
static DEF_PER3_PCLK(8, p3_pclk8);
static DEF_PER5_PCLK(0, p5_pclk0);
static DEF_PER5_PCLK(1, p5_pclk1);
static DEF_PER6_PCLK(0, p6_pclk0);
static DEF_PER6_PCLK(1, p6_pclk1);
static DEF_PER6_PCLK(2, p6_pclk2);
static DEF_PER6_PCLK(3, p6_pclk3);
static DEF_PER6_PCLK(4, p6_pclk4);
static DEF_PER6_PCLK(5, p6_pclk5);
static DEF_PER6_PCLK(6, p6_pclk6);
static DEF_PER6_PCLK(7, p6_pclk7);
/* UART0 */
static DEF_PER1_KCLK(0, p1_uart0_kclk, &uartclk);
static DEF_PER_CLK(p1_uart0_clk, &p1_pclk0, &p1_uart0_kclk);
/* UART1 */
static DEF_PER1_KCLK(1, p1_uart1_kclk, &uartclk);
static DEF_PER_CLK(p1_uart1_clk, &p1_pclk1, &p1_uart1_kclk);
/* I2C1 */
static DEF_PER1_KCLK(2, p1_i2c1_kclk, &i2cclk);
static DEF_PER_CLK(p1_i2c1_clk, &p1_pclk2, &p1_i2c1_kclk);
/* MSP0 */
static DEF_PER1_KCLK(3, p1_msp0_kclk, &msp02clk);
static DEF_PER_CLK(p1_msp0_clk, &p1_pclk3, &p1_msp0_kclk);
/* MSP1 */
static DEF_PER1_KCLK(4, p1_msp1_kclk, &msp1clk);
static DEF_PER_CLK(p1_msp1_clk, &p1_pclk4, &p1_msp1_kclk);
/* SDI0 */
static DEF_PER1_KCLK(5, p1_sdi0_kclk, &sdmmcclk);
static DEF_PER_CLK(p1_sdi0_clk, &p1_pclk5, &p1_sdi0_kclk);
/* I2C2 */
static DEF_PER1_KCLK(6, p1_i2c2_kclk, &i2cclk);
static DEF_PER_CLK(p1_i2c2_clk, &p1_pclk6, &p1_i2c2_kclk);
/* SLIMBUS0 */
static DEF_PER1_KCLK(3, p1_slimbus0_kclk, &slimclk);
static DEF_PER_CLK(p1_slimbus0_clk, &p1_pclk8, &p1_slimbus0_kclk);
/* I2C4 */
static DEF_PER1_KCLK(9, p1_i2c4_kclk, &i2cclk);
static DEF_PER_CLK(p1_i2c4_clk, &p1_pclk10, &p1_i2c4_kclk);
/* MSP3 */
static DEF_PER1_KCLK(10, p1_msp3_kclk, &msp1clk);
static DEF_PER_CLK(p1_msp3_clk, &p1_pclk11, &p1_msp3_kclk);
/* I2C3 */
static DEF_PER2_KCLK(0, p2_i2c3_kclk, &i2cclk);
static DEF_PER_CLK(p2_i2c3_clk, &p2_pclk0, &p2_i2c3_kclk);
/* SDI4 */
static DEF_PER2_KCLK(2, p2_sdi4_kclk, &sdmmcclk);
static DEF_PER_CLK(p2_sdi4_clk, &p2_pclk4, &p2_sdi4_kclk);
/* MSP2 */
static DEF_PER2_KCLK(3, p2_msp2_kclk, &msp02clk);
static DEF_PER_CLK(p2_msp2_clk, &p2_pclk5, &p2_msp2_kclk);
/* SDI1 */
static DEF_PER2_KCLK(4, p2_sdi1_kclk, &sdmmcclk);
static DEF_PER_CLK(p2_sdi1_clk, &p2_pclk6, &p2_sdi1_kclk);
/* SDI3 */
static DEF_PER2_KCLK(5, p2_sdi3_kclk, &sdmmcclk);
static DEF_PER_CLK(p2_sdi3_clk, &p2_pclk7, &p2_sdi3_kclk);
/* HSIR */
static DEF_PER2_KCLK(6, p2_ssirx_kclk, &hsirxclk);
/* HSIT */
static DEF_PER2_KCLK(7, p2_ssitx_kclk, &hsitxclk);
/* SSP0 */
static DEF_PER3_KCLK(1, p3_ssp0_kclk, &sspclk);
static DEF_PER_CLK(p3_ssp0_clk, &p3_pclk1, &p3_ssp0_kclk);
/* SSP1 */
static DEF_PER3_KCLK(2, p3_ssp1_kclk, &sspclk);
static DEF_PER_CLK(p3_ssp1_clk, &p3_pclk2, &p3_ssp1_kclk);
/* I2C0 */
static DEF_PER3_KCLK(3, p3_i2c0_kclk, &i2cclk);
static DEF_PER_CLK(p3_i2c0_clk, &p3_pclk3, &p3_i2c0_kclk);
/* SDI2 */
static DEF_PER3_KCLK(4, p3_sdi2_kclk, &sdmmcclk);
static DEF_PER_CLK(p3_sdi2_clk, &p3_pclk4, &p3_sdi2_kclk);
/* SKE */
static DEF_PER3_KCLK(5, p3_ske_kclk, &rtc32k);
static DEF_PER_CLK(p3_ske_clk, &p3_pclk5, &p3_ske_kclk);
/* UART2 */
static DEF_PER3_KCLK(6, p3_uart2_kclk, &uartclk);
static DEF_PER_CLK(p3_uart2_clk, &p3_pclk6, &p3_uart2_kclk);
/* SDI5 */
static DEF_PER3_KCLK(7, p3_sdi5_kclk, &sdmmcclk);
static DEF_PER_CLK(p3_sdi5_clk, &p3_pclk7, &p3_sdi5_kclk);
/* RNG */
static DEF_PER6_KCLK(0, p6_rng_kclk, &rngclk);
static DEF_PER_CLK(p6_rng_clk, &p6_pclk0, &p6_rng_kclk);
/* MTU:S */
/* MTU0 */
static DEF_PER_CLK(p6_mtu0_clk, &p6_pclk6, &timclk);
/* MTU1 */
static DEF_PER_CLK(p6_mtu1_clk, &p6_pclk7, &timclk);
#ifdef CONFIG_DEBUG_FS
struct clk_debug_info {
struct clk *clk;
struct dentry *dir;
struct dentry *enable;
struct dentry *requests;
int enabled;
};
static struct dentry *clk_dir;
static struct dentry *clk_show;
static struct dentry *clk_show_enabled_only;
static struct clk_debug_info dbg_clks[] = {
/* Clock sources */
{ .clk = &soc0_pll, },
{ .clk = &soc1_pll, },
{ .clk = &ddr_pll, },
{ .clk = &ulp38m4, },
{ .clk = &sysclk, },
{ .clk = &rtc32k, },
/* PRCMU clocks */
{ .clk = &sgaclk, },
{ .clk = &uartclk, },
{ .clk = &msp02clk, },
{ .clk = &msp1clk, },
{ .clk = &i2cclk, },
{ .clk = &sdmmcclk, },
{ .clk = &slimclk, },
{ .clk = &per1clk, },
{ .clk = &per2clk, },
{ .clk = &per3clk, },
{ .clk = &per5clk, },
{ .clk = &per6clk, },
{ .clk = &per7clk, },
{ .clk = &lcdclk, },
{ .clk = &bmlclk, },
{ .clk = &hsitxclk, },
{ .clk = &hsirxclk, },
{ .clk = &hdmiclk, },
{ .clk = &apeatclk, },
{ .clk = &apetraceclk, },
{ .clk = &mcdeclk, },
{ .clk = &ipi2cclk, },
{ .clk = &dsialtclk, },
{ .clk = &dmaclk, },
{ .clk = &b2r2clk, },
{ .clk = &tvclk, },
{ .clk = &sspclk, },
{ .clk = &rngclk, },
{ .clk = &uiccclk, },
{ .clk = &sysclk2, },
{ .clk = &clkout0, },
{ .clk = &clkout1, },
};
static int clk_show_print(struct seq_file *s, void *p)
{
int i;
int enabled_only = (int)s->private;
seq_printf(s, "\n%-20s %s\n", "name", "enabled (kernel + debug)");
for (i = 0; i < ARRAY_SIZE(dbg_clks); i++) {
if (enabled_only && !dbg_clks[i].clk->enabled)
continue;
seq_printf(s,
"%-20s %5d + %d\n",
dbg_clks[i].clk->name,
dbg_clks[i].clk->enabled - dbg_clks[i].enabled,
dbg_clks[i].enabled);
}
return 0;
}
static int clk_show_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_show_print, inode->i_private);
}
static int clk_enable_print(struct seq_file *s, void *p)
{
struct clk_debug_info *cdi = s->private;
return seq_printf(s, "%d\n", cdi->enabled);
}
static int clk_enable_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_enable_print, inode->i_private);
}
static ssize_t clk_enable_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct clk_debug_info *cdi;
char buf[32];
ssize_t buf_size;
long user_val;
int err;
cdi = ((struct seq_file *)(file->private_data))->private;
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = '\0';
err = strict_strtol(buf, 0, &user_val);
if (err)
return -EINVAL;
if ((user_val > 0) && (!cdi->enabled)) {
err = clk_enable(cdi->clk);
if (err) {
pr_err("clock: clk_enable(%s) failed.\n",
cdi->clk->name);
return -EFAULT;
}
cdi->enabled = 1;
} else if ((user_val <= 0) && (cdi->enabled)) {
clk_disable(cdi->clk);
cdi->enabled = 0;
}
return buf_size;
}
static int clk_requests_print(struct seq_file *s, void *p)
{
struct clk_debug_info *cdi = s->private;
return seq_printf(s, "%d\n", cdi->clk->enabled);
}
static int clk_requests_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_requests_print, inode->i_private);
}
static const struct file_operations clk_enable_fops = {
.open = clk_enable_open,
.write = clk_enable_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations clk_requests_fops = {
.open = clk_requests_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations clk_show_fops = {
.open = clk_show_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int create_clk_dirs(struct clk_debug_info *cdi, int size)
{
int i;
for (i = 0; i < size; i++) {
cdi[i].dir = debugfs_create_dir(cdi[i].clk->name, clk_dir);
if (!cdi[i].dir)
goto no_dir;
}
for (i = 0; i < size; i++) {
cdi[i].enable = debugfs_create_file("enable",
(S_IRUGO | S_IWUGO),
cdi[i].dir, &cdi[i],
&clk_enable_fops);
if (!cdi[i].enable)
goto no_enable;
}
for (i = 0; i < size; i++) {
cdi[i].requests = debugfs_create_file("requests", S_IRUGO,
cdi[i].dir, &cdi[i],
&clk_requests_fops);
if (!cdi[i].requests)
goto no_requests;
}
return 0;
no_requests:
while (i--)
debugfs_remove(cdi[i].requests);
i = size;
no_enable:
while (i--)
debugfs_remove(cdi[i].enable);
i = size;
no_dir:
while (i--)
debugfs_remove(cdi[i].dir);
return -ENOMEM;
}
static void remove_clk_dirs(struct clk_debug_info *cdi, int size)
{
int i;
for (i = 0; i < size; i++) {
debugfs_remove(cdi[i].requests);
debugfs_remove(cdi[i].enable);
debugfs_remove(cdi[i].dir);
}
}
static int __init clk_debug_init(void)
{
clk_dir = debugfs_create_dir("clk", NULL);
if (!clk_dir)
goto no_dir;
clk_show = debugfs_create_file("show", S_IRUGO, clk_dir, (void *)0,
&clk_show_fops);
if (!clk_show)
goto no_show;
clk_show_enabled_only = debugfs_create_file("show-enabled-only",
S_IRUGO, clk_dir, (void *)1,
&clk_show_fops);
if (!clk_show_enabled_only)
goto no_enabled_only;
if (create_clk_dirs(&dbg_clks[0], ARRAY_SIZE(dbg_clks)))
goto no_clks;
return 0;
no_clks:
debugfs_remove(clk_show_enabled_only);
no_enabled_only:
debugfs_remove(clk_show);
no_show:
debugfs_remove(clk_dir);
no_dir:
return -ENOMEM;
}
static void __exit clk_debug_exit(void)
{
remove_clk_dirs(&dbg_clks[0], ARRAY_SIZE(dbg_clks));
debugfs_remove(clk_show);
debugfs_remove(clk_show_enabled_only);
debugfs_remove(clk_dir);
}
subsys_initcall(clk_debug_init);
module_exit(clk_debug_exit);
#endif /* CONFIG_DEBUG_FS */
/*
* TODO: Ensure names match with devices and then remove unnecessary entries
* when all drivers use the clk API.
*/
#define CLK_LOOKUP(_clk, _dev_id, _con_id) \
{ .dev_id = _dev_id, .con_id = _con_id, .clk = &_clk }
static struct clk_lookup u8500_clocks[] = {
CLK_LOOKUP(soc0_pll, NULL, "soc0_pll"),
CLK_LOOKUP(soc1_pll, NULL, "soc1_pll"),
CLK_LOOKUP(ddr_pll, NULL, "ddr_pll"),
CLK_LOOKUP(ulp38m4, NULL, "ulp38m4"),
CLK_LOOKUP(sysclk, NULL, "sysclk"),
CLK_LOOKUP(rtc32k, NULL, "clk32k"),
CLK_LOOKUP(sysclk, "ab8500-usb.0", "sysclk"),
CLK_LOOKUP(sysclk, "ab8500-codec.0", "sysclk"),
CLK_LOOKUP(ab_ulpclk, "ab8500-codec.0", "ulpclk"),
CLK_LOOKUP(ab_intclk, "ab8500-codec.0", "intclk"),
CLK_LOOKUP(audioclk, "ab8500-codec.0", "audioclk"),
CLK_LOOKUP(ab_intclk, "ab8500-pwm.1", NULL),
CLK_LOOKUP(ab_intclk, "ab8500-pwm.2", NULL),
CLK_LOOKUP(ab_intclk, "ab8500-pwm.3", NULL),
CLK_LOOKUP(clkout0, "pri-cam", NULL),
CLK_LOOKUP(clkout1, "3-005c", NULL),
CLK_LOOKUP(clkout1, "3-005d", NULL),
CLK_LOOKUP(clkout1, "sec-cam", NULL),
/* prcmu */
CLK_LOOKUP(sgaclk, "mali", NULL),
CLK_LOOKUP(uartclk, "UART", NULL),
CLK_LOOKUP(msp02clk, "MSP02", NULL),
CLK_LOOKUP(i2cclk, "I2C", NULL),
CLK_LOOKUP(sdmmcclk, "sdmmc", NULL),
CLK_LOOKUP(slimclk, "slim", NULL),
CLK_LOOKUP(per1clk, "PERIPH1", NULL),
CLK_LOOKUP(per2clk, "PERIPH2", NULL),
CLK_LOOKUP(per3clk, "PERIPH3", NULL),
CLK_LOOKUP(per5clk, "PERIPH5", NULL),
CLK_LOOKUP(per6clk, "PERIPH6", NULL),
CLK_LOOKUP(per7clk, "PERIPH7", NULL),
CLK_LOOKUP(lcdclk, "lcd", NULL),
CLK_LOOKUP(bmlclk, "bml", NULL),
CLK_LOOKUP(p2_ssitx_kclk, "ste_hsi.0", "hsit_hsitxclk"),
CLK_LOOKUP(p2_ssirx_kclk, "ste_hsi.0", "hsir_hsirxclk"),
CLK_LOOKUP(lcdclk, "mcde", "lcd"),
CLK_LOOKUP(hdmiclk, "hdmi", NULL),
CLK_LOOKUP(hdmiclk, "mcde", "hdmi"),
CLK_LOOKUP(apeatclk, "apeat", NULL),
CLK_LOOKUP(apetraceclk, "apetrace", NULL),
CLK_LOOKUP(mcdeclk, "mcde", NULL),
CLK_LOOKUP(mcdeclk, "mcde", "mcde"),
CLK_LOOKUP(ipi2cclk, "ipi2", NULL),
CLK_LOOKUP(dmaclk, "dma40.0", NULL),
CLK_LOOKUP(b2r2clk, "b2r2", NULL),
CLK_LOOKUP(b2r2clk, "b2r2_bus", NULL),
CLK_LOOKUP(b2r2clk, "U8500-B2R2.0", NULL),
CLK_LOOKUP(tvclk, "tv", NULL),
CLK_LOOKUP(tvclk, "mcde", "tv"),
CLK_LOOKUP(msp1clk, "MSP1", NULL),
CLK_LOOKUP(dsialtclk, "dsialt", NULL),
CLK_LOOKUP(sspclk, "SSP", NULL),
CLK_LOOKUP(rngclk, "rngclk", NULL),
CLK_LOOKUP(uiccclk, "uicc", NULL),
/* PERIPH 1 */
CLK_LOOKUP(p1_msp3_clk, "msp3", NULL),
CLK_LOOKUP(p1_msp3_clk, "MSP_I2S.3", NULL),
CLK_LOOKUP(p1_msp3_kclk, "ab8500-codec.0", "msp3-kernel"),
CLK_LOOKUP(p1_pclk11, "ab8500-codec.0", "msp3-bus"),
CLK_LOOKUP(p1_uart0_clk, "uart0", NULL),
CLK_LOOKUP(p1_uart1_clk, "uart1", NULL),
CLK_LOOKUP(p1_i2c1_clk, "nmk-i2c.1", NULL),
CLK_LOOKUP(p1_msp0_clk, "msp0", NULL),
CLK_LOOKUP(p1_msp0_clk, "MSP_I2S.0", NULL),
CLK_LOOKUP(p1_sdi0_clk, "sdi0", NULL),
CLK_LOOKUP(p1_i2c2_clk, "nmk-i2c.2", NULL),
CLK_LOOKUP(p1_slimbus0_clk, "slimbus0", NULL),
CLK_LOOKUP(p1_pclk9, "gpio.0", NULL),
CLK_LOOKUP(p1_pclk9, "gpio.1", NULL),
CLK_LOOKUP(p1_pclk9, "gpioblock0", NULL),
CLK_LOOKUP(p1_msp1_clk, "msp1", NULL),
CLK_LOOKUP(p1_msp1_clk, "MSP_I2S.1", NULL),
CLK_LOOKUP(p1_msp1_kclk, "ab8500-codec.0", "msp1-kernel"),
CLK_LOOKUP(p1_pclk4, "ab8500-codec.0", "msp1-bus"),
CLK_LOOKUP(p1_pclk7, "spi3", NULL),
CLK_LOOKUP(p1_i2c4_clk, "nmk-i2c.4", NULL),
/* PERIPH 2 */
CLK_LOOKUP(p2_i2c3_clk, "nmk-i2c.3", NULL),
CLK_LOOKUP(p2_pclk1, "spi2", NULL),
CLK_LOOKUP(p2_pclk2, "spi1", NULL),
CLK_LOOKUP(p2_pclk3, "pwl", NULL),
CLK_LOOKUP(p2_sdi4_clk, "sdi4", NULL),
CLK_LOOKUP(p2_msp2_clk, "msp2", NULL),
CLK_LOOKUP(p2_msp2_clk, "MSP_I2S.2", NULL),
CLK_LOOKUP(p2_sdi1_clk, "sdi1", NULL),
CLK_LOOKUP(p2_sdi3_clk, "sdi3", NULL),
CLK_LOOKUP(p2_pclk8, "spi0", NULL),
CLK_LOOKUP(p2_pclk9, "ste_hsi.0", "hsir_hclk"),
CLK_LOOKUP(p2_pclk10, "ste_hsi.0", "hsit_hclk"),
CLK_LOOKUP(p2_pclk11, "gpio.6", NULL),
CLK_LOOKUP(p2_pclk11, "gpio.7", NULL),
CLK_LOOKUP(p2_pclk11, "gpioblock1", NULL),
/* PERIPH 3 */
CLK_LOOKUP(p3_pclk0, "fsmc", NULL),
CLK_LOOKUP(p3_i2c0_clk, "nmk-i2c.0", NULL),
CLK_LOOKUP(p3_sdi2_clk, "sdi2", NULL),
CLK_LOOKUP(p3_ske_clk, "ske", NULL),
CLK_LOOKUP(p3_ske_clk, "nmk-ske-keypad", NULL),
CLK_LOOKUP(p3_uart2_clk, "uart2", NULL),
CLK_LOOKUP(p3_sdi5_clk, "sdi5", NULL),
CLK_LOOKUP(p3_pclk8, "gpio.2", NULL),
CLK_LOOKUP(p3_pclk8, "gpio.3", NULL),
CLK_LOOKUP(p3_pclk8, "gpio.4", NULL),
CLK_LOOKUP(p3_pclk8, "gpio.5", NULL),
CLK_LOOKUP(p3_pclk8, "gpioblock2", NULL),
CLK_LOOKUP(p3_ssp0_clk, "ssp0", NULL),
CLK_LOOKUP(p3_ssp1_clk, "ssp1", NULL),
/* PERIPH 5 */
CLK_LOOKUP(p5_pclk1, "gpio.8", NULL),
CLK_LOOKUP(p5_pclk1, "gpioblock3", NULL),
CLK_LOOKUP(p5_pclk0, "musb-ux500.0", "usb"),
/* PERIPH 6 */
CLK_LOOKUP(p6_pclk1, "cryp0", NULL),
CLK_LOOKUP(p6_pclk2, "hash0", NULL),
CLK_LOOKUP(p6_pclk3, "pka", NULL),
CLK_LOOKUP(p6_pclk5, "cfgreg", NULL),
CLK_LOOKUP(p6_mtu0_clk, "mtu0", NULL),
CLK_LOOKUP(p6_mtu1_clk, "mtu1", NULL),
CLK_LOOKUP(p6_pclk4, "hash1", NULL),
CLK_LOOKUP(p6_pclk1, "cryp1", NULL),
CLK_LOOKUP(p6_rng_clk, "rng", NULL),
};
static struct clk_lookup u8500_v2_sysclks[] = {
CLK_LOOKUP(sysclk2, NULL, "sysclk2"),
CLK_LOOKUP(sysclk3, NULL, "sysclk3"),
CLK_LOOKUP(sysclk4, NULL, "sysclk4"),
};
/* these are the clocks which are default from the bootloader */
static const char *u8500_boot_clk[] = {
"uart0",
"uart1",
"uart2",
"gpioblock0",
"gpioblock1",
"gpioblock2",
"gpioblock3",
"mtu0",
"mtu1",
"ssp0",
"ssp1",
"spi0",
"spi1",
"spi2",
"spi3",
"msp0",
"msp2",
"nmk-i2c.0",
"nmk-i2c.1",
"nmk-i2c.2",
"nmk-i2c.3",
"nmk-i2c.4",
};
static void sysclk_init_disable(struct work_struct *not_used)
{
int i;
mutex_lock(&sysclk_mutex);
/* Enable SWAT */
if (ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE))
goto err_swat;
for (i = 0; i < ARRAY_SIZE(u8500_v2_sysclks); i++) {
struct clk *clk = u8500_v2_sysclks[i].clk;
/* Disable sysclks */
if (!clk->enabled && clk->cg_sel) {
if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
(u8)clk->cg_sel))
goto err_sysclk;
}
}
goto unlock_and_exit;
err_sysclk:
pr_err("clock: Disable %s failed", u8500_v2_sysclks[i].clk->name);
ab8500_sysctrl_clear(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
goto unlock_and_exit;
err_swat:
pr_err("clock: Enable SWAT failed");
unlock_and_exit:
mutex_unlock(&sysclk_mutex);
}
static struct clk *boot_clks[ARRAY_SIZE(u8500_boot_clk)];
/* we disable a majority of peripherals enabled by default
* but without drivers
*/
static int __init u8500_boot_clk_disable(void)
{
unsigned int i = 0;
for (i = 0; i < ARRAY_SIZE(u8500_boot_clk); i++) {
if (!boot_clks[i])
continue;
clk_disable(boot_clks[i]);
clk_put(boot_clks[i]);
}
INIT_DELAYED_WORK(&sysclk_disable_work, sysclk_init_disable);
schedule_delayed_work(&sysclk_disable_work, 10 * HZ);
return 0;
}
late_initcall_sync(u8500_boot_clk_disable);
static void u8500_amba_clk_enable(void)
{
unsigned int i = 0;
writel(~0x0 & ~(1 << 9), __io_address(U8500_PER1_BASE + 0xF000
+ 0x04));
writel(~0x0, __io_address(U8500_PER1_BASE + 0xF000 + 0x0C));
writel(~0x0 & ~(1 << 11), __io_address(U8500_PER2_BASE + 0xF000
+ 0x04));
writel(~0x0, __io_address(U8500_PER2_BASE + 0xF000 + 0x0C));
if(machine_is_snowball()){
/* Leave FSMC alone, it is needed for ethernet controller */
writel(0xFE, __io_address(U8500_PER3_BASE + 0xF000 + 0x04));
writel(~0x0 & ~(1 << 1), __io_address(U8500_PER3_BASE + 0xF000
+ 0x0C));
} else {
/* GPIO,UART2 are enabled for booting */
writel(0xBF, __io_address(U8500_PER3_BASE + 0xF000 + 0x04));
writel(~0x0 & ~(1 << 6), __io_address(U8500_PER3_BASE + 0xF000
+ 0x0C));
}
for (i = 0; i < ARRAY_SIZE(u8500_boot_clk); i++) {
boot_clks[i] = clk_get_sys(u8500_boot_clk[i], NULL);
clk_enable(boot_clks[i]);
}
}
static int __init init_clock_states(void)
{
/*
* The following clks are shared with secure world.
* Currently this leads to a limitation where we need to
* enable them at all times.
*/
clk_enable(&p6_pclk1);
clk_enable(&p6_pclk2);
clk_enable(&p6_pclk3);
clk_enable(&p6_rng_clk);
/*
* Disable clocks that are on at boot, but should be off.
*/
if (!clk_enable(&bmlclk))
clk_disable(&bmlclk);
if (!clk_enable(&dsialtclk))
clk_disable(&dsialtclk);
if (!clk_enable(&hsirxclk))
clk_disable(&hsirxclk);
if (!clk_enable(&hsitxclk))
clk_disable(&hsitxclk);
if (!clk_enable(&ipi2cclk))
clk_disable(&ipi2cclk);
if (!clk_enable(&lcdclk))
clk_disable(&lcdclk);
if (!clk_enable(&per7clk))
clk_disable(&per7clk);
/*
* APEATCLK and APETRACECLK are enabled at boot and needed
* in order to debug with lauterbach
*/
if (!clk_enable(&apeatclk)) {
#ifdef CONFIG_UX500_DEBUG_NO_LAUTERBACH
clk_disable(&apeatclk);
#endif
}
if (!clk_enable(&apetraceclk)) {
#ifdef CONFIG_UX500_DEBUG_NO_LAUTERBACH
clk_disable(&apetraceclk);
#endif
}
return 0;
}
late_initcall(init_clock_states);
int __init db8500_clk_init(void)
{
clks_register(u8500_v2_sysclks,
ARRAY_SIZE(u8500_v2_sysclks));
clks_register(u8500_clocks,
ARRAY_SIZE(u8500_clocks));
u8500_amba_clk_enable();
return 0;
}