blob: 7ef79a91746e48b07be6d6927f361beb0ae7e3b4 [file] [log] [blame]
/*
* linux/arch/arm/mach-comcerto/clock.c
*
* Copyright (C) 2008 Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clkdev.h>
#include <linux/printk.h>
#include <linux/list.h>
#include <mach/comcerto-2000/clock.h>
static DEFINE_SPINLOCK(clock_lock);
/* Forward declaration */
static void HAL_set_clk_divider(unsigned long ,u32 , u32);
static unsigned long HAL_get_clk_freq(u32, u32);
/* API clk_put clk_get
* Declared in "include/linux/clk.h", include this
* in correspoding driver.
* Defined in drivers/clk/clkdev.c, will
* use the above.
*/
/* @ struct clk *
* API:local_clk_disable
* Call the device level disable.
* This will decrease the usecount following to disabling
* the clock.
*/
static void local_clk_disable(struct clk *clk)
{
u32 val;
if (clk->usecount == 0){
pr_warning("Warning : %s Clock is already disabled \n",clk->name);
return;
}
/* Decrement the usecount */
clk->usecount--;
if (!clk->usecount){
/* Take care of parent disable , if present for the clock.
* Disable the parent , if no other clock is using.
*/
if (clk->parent)
local_clk_disable(clk->parent);
/* Apply the Clock register write here */
if (clk->enable_reg){
val = readl(clk->enable_reg);
val &= ~clk->enable_mask;
writel(val , clk->enable_reg);
}
}
}
/* @ struct clk *
* API:clk_disable used to disable clock for
* the devices.
* This API will be available to outside
* ( for all device driver).
* Call of this API should be followed by clk_get
* clk_disable->clk_put.
*/
void clk_disable(struct clk *clk)
{
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
local_clk_disable(clk);
spin_unlock_irqrestore(&clock_lock, flags);
}
EXPORT_SYMBOL(clk_disable);
/* @ struct clk *
* API : Internal call for clk_disable_unused for
* unused devices.
*/
static void __clk_disable_unused(struct clk *clk)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&clock_lock, flags);
/* Apply the Clock register write here */
if (clk->enable_reg){
val = readl(clk->enable_reg);
val &= ~clk->enable_mask;
writel(val , clk->enable_reg);
}
spin_unlock_irqrestore(&clock_lock, flags);
}
/* @ struct clk *
* API:local_clk_enable
* Call the device level enable.
* This will increase the usecount.
*/
static int local_clk_enable(struct clk *clk)
{
int ret = 0;
u32 val;
if (clk->usecount == 0) {
/* Check for parent clock .Enable the parent clock,if available*/
if (clk->parent){
ret = local_clk_enable(clk->parent);
if (ret)
return -EINVAL;
}
if (clk->enable_reg){
val = readl(clk->enable_reg);
val |= clk->enable_mask;
writel(val , clk->enable_reg);
ret=0;
}
}
/* Increment the use count */
clk->usecount++;
return ret;
}
/* @ struct clk *
* API:clk_enable used to enable clock for
* the devices.
* This API will be available to outside
* ( for all device driver).
* Call of this API should be followed by clk_get
* clk_get -> clk_enable.
*/
int clk_enable(struct clk *clk)
{
unsigned long flags;
int ret=0;
if (!clk)
return -EINVAL;
spin_lock_irqsave(&clock_lock, flags);
ret = local_clk_enable(clk);
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
EXPORT_SYMBOL(clk_enable);
/* @ struct clk *
* API:clk_get_rate used to get clock rate value
* for the devices. This API will be available to
* outside ( for all device driver).
*/
unsigned long clk_get_rate(struct clk *clk)
{
if (clk->get_rate)
return clk->get_rate(clk);
else
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
/* @ struct clk * , unsigned long
* API:local_set_rate used to set clock rate value
* for the devices.
*/
static int local_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EINVAL;
if (clk->set_rate) {
ret = clk->set_rate(clk, rate);
}
return ret;
}
/* @ struct clk * , unsigned long
* API:clk_set_rate used to set clock rate value
* for the devices. This API will be available to
* outside ( for all device driver).
*/
int clk_set_rate(struct clk *clk, unsigned long rate)
{
unsigned long actual_rate;
int ret = -EINVAL;
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
actual_rate=clk->rate;
if (actual_rate != rate ){
ret = local_set_rate(clk, rate);
}
else{ /*configured rate is same as desired rate*/
pr_debug("Current rate value of clock source (%s) is same as desired rate value (%ld)\n",clk->name,clk->rate);
spin_unlock_irqrestore(&clock_lock, flags);
return 0;
}
if ( ret == -EINVAL ){
pr_debug("Cannot change clock:(%s) ,not supporting set_rate,set to previous value (%ld)\n",clk->name,clk->rate);
}else{
pr_debug("Changed clock:(%s) rate ,rate value is (%ld)\n",clk->name,clk->rate);
}
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
/* @ struct clk *
* API:clk_get_parent used to parent clock source.
* This API will be available to
* outside ( for all device driver).
*/
struct clk *clk_get_parent(struct clk *clk)
{
return clk->parent;
}
EXPORT_SYMBOL(clk_get_parent);
/* @ struct clk * , unsigned long
* API:clk_set_parent used to set to a different
* parent clock source.
* This API will be available to
* outside ( for all device driver).
*/
int clk_set_parent(struct clk *clk, struct clk *parent)
{
unsigned long flags;
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
/* Only set parent once and if none is already set. */
if (clk->parent)
return -EINVAL;
/* Cannot change parent on enabled clock */
if ( WARN_ON(clk->usecount))
return -EBUSY;
spin_lock_irqsave(&clock_lock, flags);
/* Set the parent in clk strusture */
clk->parent = parent;
spin_unlock_irqrestore(&clock_lock, flags);
return 0;
}
EXPORT_SYMBOL(clk_set_parent);
/*
* CLKGEN Divider registers
* The divider bypass bit in several configuration registers
* can only be written (if you read back you get zero).
*
* The Bug is in registers: 0x84 (A9DP_CLKDIV_CNTRL), 0x84 + 16 (L2CC_CLKDIV_CNTRL), 0x84 + 32
* (TPI_CLKDIV_CNTRL), etc... until PLL_ADDR_SPACE at 0x1C0.
*/
/*
* Barebox uses IRAM to mirror the clock divider registers
* Linux will relocate this mirror from IRAM to DDR to free up IRAM.
*/
#define IRAM_CLK_REG_MIRROR (0x8300FC00 - COMCERTO_AXI_IRAM_BASE + IRAM_MEMORY_VADDR)
#define CLK_REG_DIV_BUG_BASE AXI_CLK_DIV_CNTRL
#define CLK_REG_DIV_BUG_SIZE (PLL0_M_LSB - AXI_CLK_DIV_CNTRL)
static u8 clk_div_backup_table [CLK_REG_DIV_BUG_SIZE];
#define read_clk_div_bypass_backup(reg) readl(reg - CLK_REG_DIV_BUG_BASE + clk_div_backup_table)
#define write_clk_div_bypass_backup(val, reg) writel(val, reg - CLK_REG_DIV_BUG_BASE + clk_div_backup_table)
void HAL_clk_div_backup_relocate_table (void)
{
memcpy (clk_div_backup_table, (void*) IRAM_CLK_REG_MIRROR, CLK_REG_DIV_BUG_SIZE);
}
EXPORT_SYMBOL(HAL_clk_div_backup_relocate_table);
/*
* Get the reference clock after reading bootstrap
*/
unsigned long HAL_get_ref_clk (void)
{
unsigned long clock_freq = 0;
unsigned int boot_strap, tmp;
boot_strap = readl(COMCERTO_GPIO_SYSTEM_CONFIG);
tmp = (boot_strap & GPIO_SYS_PLL_REF_CLK_MASK) >> GPIO_SYS_PLL_REF_CLK_SHIFT;
if ( USB_XTAL_REF_CLK == tmp )
{
if ( boot_strap & GPIO_USB_OSC_PAD_MASK )
clock_freq = REF_CLK_24MHZ;
else
clock_freq = REF_CLK_48MHZ;
}
else if ( SERDES_XTAL_REF_CLK == tmp )
{
if ( boot_strap & GPIO_SERDES_OSC_PAD_MASK )
clock_freq = REF_CLK_24MHZ;
else
clock_freq = REF_CLK_48MHZ;
}
return clock_freq;
}
EXPORT_SYMBOL(HAL_get_ref_clk);
static unsigned long HAL_get_pll_freq(int pll_no)
{
u32 p;
u32 od;
u32 m;
u32 k;
u32 s;
unsigned long pll_clk = 0;
unsigned long ref_clk = HAL_get_ref_clk();
unsigned long pll_div = 0;
if (pll_no < PLL3)
{
//get NF, NR and OD values
switch (pll_no)
{
case PLL0:
m = readl(PLL0_M_LSB) & 0xff;
m |= (readl(PLL0_M_MSB) & 0x3) << 8;
p = readl(PLL0_P) & 0x3f;
s = readl(PLL0_S) & 0x7;
od = (1 << s); // 2^s;
pll_div = readl(PLL0_DIV_CNTRL);
break;
case PLL1:
m = readl(PLL1_M_LSB) & 0xff;
m |= (readl(PLL1_M_MSB) & 0x3) << 8;
p = readl(PLL1_P) & 0x3f;
s = readl(PLL1_S) & 0x7;
od = (1 << s);
pll_div = readl(PLL1_DIV_CNTRL);
break;
case PLL2:
m = readl(PLL2_M_LSB) & 0xff;
m |= (readl(PLL2_M_MSB) & 0x3) << 8;
p = readl(PLL2_P) & 0x3f;
s = readl(PLL2_S) & 0x7;
od = (1 << s);
pll_div = readl(PLL2_DIV_CNTRL);
break;
default:
return 0;
break;
}
/* Ref Clock divided by 1000000. It should be displayed in MHz. */
pll_clk = ((ref_clk / 1000000) * m) / p / od ;
if (pll_div & CLK_DIV_BYPASS)
pll_div = 1;
else
pll_div = pll_div & 0x1F;
if (pll_div > 1)
pll_clk = pll_clk/pll_div;
}
else if (pll_no == PLL3)
{
m = readl(PLL3_M_LSB) & 0xff;
m |= (readl(PLL3_M_MSB) & 0x3) << 8;
p = readl(PLL3_P) & 0x3f;
s = readl(PLL3_S) & 0x7;
k = readl(PLL3_K_LSB) & 0xff;
k |= (readl(PLL3_K_MSB) & 0xf) << 8;
od = (1 << s);
pll_clk = (((ref_clk / 1000000) * (m * 1024 + k)) / p / od + 1023) / 1024;
}
return (pll_clk * 1000000); /* convert into Hz and return it */
}
static void HAL_set_clk_divider(unsigned long rate,u32 ctrl_reg, u32 div_reg)
{
u32 pll_src;
u32 val;
int divider;
unsigned long pll_rate;
/* Get PLL Source */
pll_src = readl(ctrl_reg);
pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
/* Get PLL Freq */
pll_rate = HAL_get_pll_freq(pll_src);
/* Get The Divider value For Clock */
divider = pll_rate/rate;
if ( divider == 1){
write_clk_div_bypass_backup(CLK_DIV_BYPASS,div_reg);
/* Enable the Bypass bit in Hw reg (clk_div_bypass in div_reg) */
val = readl(div_reg);
val |= CLK_DIV_BYPASS;
writel(val , div_reg);
}
else
{
write_clk_div_bypass_backup(0,div_reg);
/* Write to the divider reg */
val = readl(div_reg);
val &= ~0x1f;
val |= divider;
writel(val, div_reg);
/* Clear the Bypass bit in Hw reg (clk_div_bypass in div_reg) */
val = readl(div_reg);
val &= ~CLK_DIV_BYPASS;
writel(val , div_reg);
}
}
static int HAL_get_clock_pll_source(u32 ctrl_reg)
{
int pll_src;
/* Get PLL source */
pll_src = readl(ctrl_reg);
pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
return pll_src;
}
static void HAL_set_clock_pll_source(u32 ctrl_reg,int pll_src){
switch(pll_src)
{
case PLL0:
writel(readl(ctrl_reg) | (1 << 0) , ctrl_reg);
break;
case PLL1:
writel(readl(ctrl_reg) | (1 << 1) , ctrl_reg);
break;
case PLL3:
writel(readl(ctrl_reg) | (1 << 3), ctrl_reg);
break;
case PLL2:
default:
writel(readl(ctrl_reg) | (1 << 2), ctrl_reg);
break;
}
}
static unsigned long HAL_get_clk_freq(u32 ctrl_reg, u32 div_reg)
{
u32 pll_src;
u32 clk_div;
unsigned long clk_out;
unsigned long pll_clk;
int bypass = 0;
/* Get PLL Source */
pll_src = readl(ctrl_reg);
pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
/* Get clock divider bypass value from IRAM Clock Divider registers mirror location */
clk_div = read_clk_div_bypass_backup(div_reg);
if (clk_div & CLK_DIV_BYPASS)
bypass = 1;
else
{
clk_div = readl(div_reg);
clk_div &= 0x1f;
}
pll_clk = HAL_get_pll_freq(pll_src);
if (bypass)
clk_out = pll_clk;
else
clk_out = pll_clk / clk_div;
return clk_out;
}
/* Get rate API callback */
static unsigned long HAL_get_clk(struct clk *clk)
{
return HAL_get_clk_freq(clk->clkgen_reg, clk->div_reg);
}
/* Get rate API callback for those clocks having Parent */
static unsigned long HAL_get_parent_clk(struct clk *clk)
{
return clk_get_rate(clk->parent);
}
/* Specific Get rate API callback for arm peripherals */
static unsigned long HAL_get_arm_peri_clk(struct clk *clk)
{
return clk_get_rate(clk->parent)/4 ;
}
/* Set rate APIs callbacks */
static int HAL_set_clk(struct clk *clk,unsigned long rate)
{
struct clk *clk_parent;
/* Get the parent clock */
clk_parent=clk_get_parent(clk);
if ( clk_parent || rate <= clk_parent->rate){
/* Set the divider value to reg */
HAL_set_clk_divider(rate,clk->clkgen_reg,clk->div_reg);
/* Set the rate value to clk structure */
clk->rate=rate;
return 0;
}else
return -EINVAL;
}
/* Initization of clk structure for individual device */
static struct clk clk_pll0 = {
.name = "pll0",
};
static struct clk clk_pll1 = {
.name = "pll1",
};
static struct clk clk_pll2 = {
.name = "pll2",
};
static struct clk clk_pll3 = {
.name = "pll3",
};
static struct clk gemtx_clk = {
.name = "gemtx",
.enable_reg = GEMTX_CLK_CNTRL,
.clkgen_reg = GEMTX_CLK_CNTRL,
.div_reg = GEMTX_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk ddr_clk = {
.name = "ddr",
.enable_reg = DDR_CLK_CNTRL,
.clkgen_reg = DDR_CLK_CNTRL,
.div_reg = DDR_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
};
static struct clk arm_clk = {
.name = "arm",
.enable_reg = A9DP_CLK_CNTRL,
.clkgen_reg = A9DP_CLK_CNTRL,
.div_reg = A9DP_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk l2cc_clk = {
.name = "l2cc",
.enable_reg = L2CC_CLK_CNTRL,
.clkgen_reg = L2CC_CLK_CNTRL,
.div_reg = L2CC_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk axi_clk = {
.name = "axi",
.enable_reg = AXI_CLK_CNTRL_0,
.clkgen_reg = AXI_CLK_CNTRL_0,
.div_reg = AXI_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk uart_clk = { /* Legacy UART */
.parent = &axi_clk,
.name = "uart",
.enable_reg = AXI_CLK_CNTRL_1,
.enable_mask = CLK_DOMAIN_UART_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk ipsec_eape_clk = {
.name = "ipsec_eape",
.enable_reg = IPSEC_CLK_CNTRL,
.clkgen_reg = IPSEC_CLK_CNTRL,
.div_reg = IPSEC_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
};
static struct clk ipsec_spacc_clk = { /* IPSEC spacc clock for Elliptic EPN1802*/
.parent = &axi_clk,
.name = "ipsec_spacc",
.enable_reg = AXI_CLK_CNTRL_1,
.enable_mask = CLK_DOMAIN_IPSEC_SPACC_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk dpi_cie_clk = { /* DPI cie clock*/
.parent = &axi_clk,
.name = "dpi_cie",
.enable_reg = AXI_CLK_CNTRL_0,
.enable_mask = CLK_DOMAIN_DPI_CIE_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk dpi_decomp_clk = { /* DPI decomp clock*/
.parent = &axi_clk,
.name = "dpi_decomp",
.enable_reg = AXI_CLK_CNTRL_0,
.enable_mask = CLK_DOMAIN_DPI_DECOMP_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk DUS_clk = { /* DMA,FAST-UART and SMI clock */
.parent = &axi_clk,
.name = "DUS",
.enable_reg = AXI_CLK_CNTRL_1,
.enable_mask = CLK_DOMAIN_DUS_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk arm_peri_clk = {
.parent = &arm_clk,
.name = "arm_peri",
.enable_reg = A9DP_MPU_CLK_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_arm_peri_clk,
};
static struct clk hfe_core_clk = {
.name = "hfe_core",
.enable_reg = PFE_CLK_CNTRL,
.clkgen_reg = PFE_CLK_CNTRL,
.div_reg = PFE_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk spi_i2c_clk = {
.parent = &axi_clk,
.name = "spi_i2c",
.enable_reg = AXI_CLK_CNTRL_1,
.enable_mask = CLK_DOMAIN_SPI_I2C_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk tdmNTG_clk = {
.name = "ntgref",
.enable_reg = TDMNTG_REF_CLK_CNTRL,
.clkgen_reg = TDMNTG_REF_CLK_CNTRL,
.div_reg = TDMNTG_REF_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk pcie0_clk = {
.parent = &axi_clk,
.name = "pcie0",
.enable_reg = AXI_CLK_CNTRL_2,
.enable_mask = CLK_DOMAIN_PCIE0_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk pcie1_clk = {
.parent = &axi_clk,
.name = "pcie1",
.enable_reg = AXI_CLK_CNTRL_2,
.enable_mask = CLK_DOMAIN_PCIE1_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk usb0_clk = {
.parent = &axi_clk,
.name = "usb0",
.enable_reg = AXI_CLK_CNTRL_2,
.enable_mask = CLK_DOMAIN_USB0_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk usb1_clk = {
.parent = &axi_clk,
.name = "usb1",
.enable_reg = AXI_CLK_CNTRL_2,
.enable_mask = CLK_DOMAIN_USB1_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk sata_clk = { /* SATA AXI clock */
.parent = &axi_clk,
.name = "sata",
.enable_reg = AXI_CLK_CNTRL_2,
.enable_mask = CLK_DOMAIN_SATA_MASK,
.get_rate = HAL_get_parent_clk,
};
static struct clk sata_oob_clk = {
.name = "sata_oob", /* SATA PMU alive clock */
.enable_reg = SATA_OOB_CLK_CNTRL,
.clkgen_reg = SATA_OOB_CLK_CNTRL,
.div_reg = SATA_OOB_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk sata_pmu_clk = {
.name = "sata_pmu", /* SATA core clock control */
.enable_reg = SATA_PMU_CLK_CNTRL,
.clkgen_reg = SATA_PMU_CLK_CNTRL,
.div_reg = SATA_PMU_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk ext_phy0_clk = {
.name = "ext_phy0",
.enable_reg = EXTPHY0_CLK_CNTRL,
.clkgen_reg = EXTPHY0_CLK_CNTRL,
.div_reg = EXTPHY0_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk ext_phy1_clk = {
.name = "ext_phy1",
.enable_reg = EXTPHY1_CLK_CNTRL,
.clkgen_reg = EXTPHY1_CLK_CNTRL,
.div_reg = EXTPHY1_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk ext_phy2_clk = {
.name = "ext_phy2",
.enable_reg = EXTPHY2_CLK_CNTRL,
.clkgen_reg = EXTPHY2_CLK_CNTRL,
.div_reg = EXTPHY2_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk tpi_clk = {
.name = "tpi",
.enable_reg = TPI_CLK_CNTRL,
.clkgen_reg = TPI_CLK_CNTRL,
.div_reg = TPI_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk csys_clk = {
.name = "csys",
.enable_reg = CSYS_CLK_CNTRL,
.clkgen_reg = CSYS_CLK_CNTRL,
.div_reg = CSYS_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk tsuntg_clk = {
.name = "tsuntg",
.enable_reg = TSUNTG_REF_CLK_CNTRL,
.clkgen_reg = TSUNTG_REF_CLK_CNTRL,
.div_reg = TSUNTG_REF_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk sata_occ_clk = {
.name = "sata_occ",
.enable_reg = SATA_OCC_CLK_CNTRL,
.clkgen_reg = SATA_OCC_CLK_CNTRL,
.div_reg = SATA_OCC_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk pcie_occ_clk = {
.name = "pcie_occ",
.enable_reg = PCIE_OCC_CLK_CNTRL,
.clkgen_reg = PCIE_OCC_CLK_CNTRL,
.div_reg = PCIE_OCC_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
static struct clk sgmii_occ_clk = {
.name = "sgmii_occ",
.enable_reg = SGMII_OCC_CLK_CNTRL,
.clkgen_reg = SGMII_OCC_CLK_CNTRL,
.div_reg = SGMII_OCC_CLK_DIV_CNTRL,
.enable_mask = CLK_DOMAIN_MASK,
.get_rate = HAL_get_clk,
.set_rate = HAL_set_clk,
};
/* These clocks are visible outside this module
* and can be initialized , this list could be expanded
* according to new device support.
*/
static struct clk *c2k_clks[] __initdata = {
&clk_pll0,
&clk_pll1,
&clk_pll2,
&clk_pll3,
&uart_clk,
&DUS_clk,
&gemtx_clk,
&ipsec_eape_clk,
&ipsec_spacc_clk,
&dpi_cie_clk,
&dpi_decomp_clk,
&ddr_clk,
&arm_clk,
&l2cc_clk,
&axi_clk,
&arm_peri_clk,
&hfe_core_clk,
&spi_i2c_clk,
&tdmNTG_clk,
&pcie0_clk,
&pcie1_clk,
&usb0_clk,
&usb1_clk,
&sata_clk,
&sata_oob_clk,
&sata_pmu_clk,
&ext_phy0_clk,
&ext_phy1_clk,
&ext_phy2_clk,
&tpi_clk,
&csys_clk,
&tsuntg_clk,
&sata_occ_clk,
&pcie_occ_clk,
&sgmii_occ_clk,
};
static struct clk_lookup c2k_clksreg[] = {
{ .clk = &clk_pll0, .con_id = "pll0"},
{ .clk = &clk_pll1, .con_id = "pll1"},
{ .clk = &clk_pll2, .con_id = "pll2"},
{ .clk = &clk_pll3, .con_id = "pll3"},
{ .clk = &uart_clk, .con_id = "uart"},
{ .clk = &DUS_clk, .con_id = "DUS"},
{ .clk = &gemtx_clk, .con_id = "gemtx"},
{ .clk = &ipsec_eape_clk, .con_id = "ipsec_eape"},
{ .clk = &ipsec_spacc_clk, .con_id = "ipsec_spacc"},
{ .clk = &dpi_cie_clk, .con_id = "dpi_cie"},
{ .clk = &dpi_decomp_clk, .con_id = "dpi_decomp"},
{ .clk = &ddr_clk, .con_id = "ddr"},
{ .clk = &arm_clk, .con_id = "arm"},
{ .clk = &l2cc_clk, .con_id = "l2cc"},
{ .clk = &axi_clk, .con_id = "axi"},
{ .clk = &arm_peri_clk, .con_id = "arm_peri"},
{ .clk = &hfe_core_clk, .con_id = "hfe_core"},
{ .clk = &spi_i2c_clk, .con_id = "spi_i2c"},
{ .clk = &tdmNTG_clk, .con_id = "ntgref"},
{ .clk = &pcie0_clk, .con_id = "pcie0"},
{ .clk = &pcie1_clk, .con_id = "pcie1"},
{ .clk = &usb0_clk, .con_id = "usb0"},
{ .clk = &usb1_clk, .con_id = "usb1"},
{ .clk = &sata_clk, .con_id = "sata"},
{ .clk = &sata_oob_clk, .con_id = "sata_oob"},
{ .clk = &sata_pmu_clk, .con_id = "sata_pmu"},
{ .clk = &ext_phy0_clk, .con_id = "ext_phy0"},
{ .clk = &ext_phy1_clk, .con_id = "ext_phy1"},
{ .clk = &ext_phy2_clk, .con_id = "ext_phy2"},
{ .clk = &tpi_clk, .con_id = "tpi"},
{ .clk = &csys_clk, .con_id = "csys"},
{ .clk = &tsuntg_clk, .con_id = "tsuntg"},
{ .clk = &sata_occ_clk, .con_id = "sata_occ"},
{ .clk = &pcie_occ_clk, .con_id = "pcie_occ"},
{ .clk = &sgmii_occ_clk, .con_id = "sgmii_occ"},
};
/* Initilize all the available clocks */
int clk_init(void){
struct clk **clkp;
int pll_no;
spin_lock_init(&clock_lock);
/* Determine the barebox configured pll0,pll1,pll2,pll3 rate value */
clk_pll0.rate = HAL_get_pll_freq(PLL0);
clk_pll1.rate = HAL_get_pll_freq(PLL1);
clk_pll2.rate = HAL_get_pll_freq(PLL2);
clk_pll3.rate = HAL_get_pll_freq(PLL3);
/* Set the NTG ref clock to PLL src (gemtx PLL source)
* Currently it is not set from barebox,set here.
*/
pll_no = HAL_get_clock_pll_source(gemtx_clk.clkgen_reg);
HAL_set_clock_pll_source(tdmNTG_clk.clkgen_reg,pll_no);
pr_info("PLL0 running at %ld MHz, PLL1 at %ld MHz PLL2 running at %ld MHz PLL3 running at %ld MHz\n",
clk_pll0.rate/1000000, clk_pll1.rate/1000000, clk_pll2.rate/1000000, clk_pll3.rate/1000000);
/* Initilization of Clocks present in C2k device */
for (clkp = c2k_clks; clkp < c2k_clks + ARRAY_SIZE(c2k_clks);clkp++) {
struct clk *clk = *clkp;
/* Setting the parent clock in the init
* Only to those who can be configured with PLL source
*/
if (!clk->parent){
if (clk->clkgen_reg){ /* This check is for not to set parent for PLLS */
pll_no = HAL_get_clock_pll_source(clk->clkgen_reg);
switch (pll_no)
{
case PLL0:
clk_set_parent(clk,&clk_pll0);
break;
case PLL1:
clk_set_parent(clk,&clk_pll1);
break;
case PLL2:
clk_set_parent(clk,&clk_pll2);
break;
default:
break;
}
}
}
/* Get The Device Clock Rate Values */
if ( clk->get_rate)
clk->rate=clk->get_rate(clk);
pr_debug("%s: clock %s, rate %ld\n",__func__, clk->name, clk->rate);
}
/* Creation of Clock Device Tree */
clkdev_add_table(c2k_clksreg, ARRAY_SIZE(c2k_clksreg));
return 0;
}
EXPORT_SYMBOL(clk_init);
/*
* Disable any unused clocks left on by the bootloader(Barebox)
*/
static int __init clk_disable_unused(void)
{
struct clk **clkp;
for (clkp = c2k_clks; clkp < c2k_clks + ARRAY_SIZE(c2k_clks);clkp++) {
struct clk *clk = *clkp;
if (clk->usecount > 0)
continue;
/* FIXME Currently there is no clock FW support for the
* following clocks in corresponding driver . So skipping these
* clocks from by default disable state.
* All other unused clocks will be disabled.
*/
if ( strcmp(clk->name, "hfe_core") &&
strcmp(clk->name, "ipsec_eape") &&
strcmp(clk->name, "pll3") &&
strcmp(clk->name, "arm_peri") )
{
pr_info("Clocks: disabled unused %s\n", clk->name);
__clk_disable_unused(clk);
}
}
return 0;
}
late_initcall(clk_disable_unused);