您的位置:首页 > 运维架构 > Linux

Davinci DM6446 Linux 内核分析—— clock.c .

2013-08-17 13:45 369 查看
http://www.61ic.com/Article/DaVinci/DM644X/201201/40303.html

/* 该文件中到程序实现了各模块PSC的管理,时钟的初始化、注册、获取以及使能等 */

/**************************************************************************

* Included Files

**************************************************************************/

#include <linux/config.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <linux/major.h>

#include <linux/root_dev.h>

#include <asm/setup.h>

#include <asm/semaphore.h>

#include <asm/hardware/clock.h>

#include <asm/io.h>

#include <asm/mach-types.h>

#include <asm/mach/arch.h>

#include <asm/mach/map.h>

#include <asm/arch/hardware.h>

#include <asm/arch/cpu.h>

#include <asm/arch/mux.h>

#include "clock.h"

#define PLL1_PLLM __REG(0x01c40910)

#define PLL2_PLLM __REG(0x01c40D10)

#define PTCMD __REG(0x01C41120)

#define PDSTAT __REG(0x01C41200)

#define PDCTL1 __REG(0x01C41304)

#define EPCPR __REG(0x01C41070)

#define PTSTAT __REG(0x01C41128)

#define MDSTAT IO_ADDRESS(0x01C41800)

#define MDCTL IO_ADDRESS(0x01C41A00)

#define VDD3P3V_PWDN __REG(0x01C40048)

static LIST_HEAD(clocks); // 链表头,用于管理所有到时钟结构体

static DECLARE_MUTEX(clocks_sem); // 添加和删除时钟用到信号量,用来互斥

static DEFINE_RAW_SPINLOCK(clockfw_lock);

static unsigned int commonrate;

static unsigned int div_by_four;

static unsigned int div_by_six;

static unsigned int div_by_eight;

static unsigned int armrate;

static unsigned int fixedrate;

/*

* 正如下面英文解释所说的,board_setup_psc函数用于使能和禁止某个PSC域,

* 共有两个域DSP和ARM,在include/asm-arm/arch-davinci/hardware.h

* 中定义。如果要使用某个模块到功能,比如SPI,则必须先使用该函数,打开该

* 模块的电源和时钟,然后才可以设置其寄存器和操作。

*/

/**************************************

Routine: board_setup_psc

Description: Enable/Disable a PSC domain

**************************************/

void board_setup_psc(unsigned int domain, unsigned int id, char enable)

{

volatile unsigned int *mdstat = (unsigned int *)((int)MDSTAT + 4 * id);

volatile unsigned int *mdctl = (unsigned int *)((int)MDCTL + 4 * id);

if (enable) {

*mdctl |= 0x00000003; /* Enable Module */

} else {

*mdctl &= 0xFFFFFFF2; /* Disable Module */

}

if ((PDSTAT & 0x00000001) == 0) {

PDCTL1 |= 0x1;

PTCMD = (1 << domain);

while ((((EPCPR >> domain) & 1) == 0)) ;

PDCTL1 |= 0x100;

while (!(((PTSTAT >> domain) & 1) == 0)) ;

} else {

PTCMD = (1 << domain);

while (!(((PTSTAT >> domain) & 1) == 0)) ;

}

if (enable) {

while (!((*mdstat & 0x0000001F) == 0x3)) ;

} else {

while (!((*mdstat & 0x0000001F) == 0x2)) ;

}

}

/*

* 获取时钟结构体指针

* 从链表中获取时钟结构体指针,比较时钟名(字符串),如果相同就返回该结构体指针

*/

struct clk *clk_get(struct device *dev, const char *id)

{

struct clk *p, *clk = ERR_PTR(-ENOENT);

down(&clocks_sem);

list_for_each_entry(p, &clocks, node) {

if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {

clk = p;

break;

}

}

up(&clocks_sem);

return clk;

}

/* 导出该符号,以便其它驱动程序调用 */

EXPORT_SYMBOL(clk_get);

/* 将时钟结构体放回到链表中,以便其它驱动能够获得 */

void clk_put(struct clk *clk)

{

if (clk && !IS_ERR(clk))

module_put(clk->owner); // 减少模块使用计数,模块使用计数的介绍可参考本站转载的文章《Linux kernel-2.6 模块使用计数》

}

EXPORT_SYMBOL(clk_put);

/* 打开模块的电源和时钟 */

int __clk_enable(struct clk *clk)

{

if (clk->flags & ALWAYS_ENABLED) // 检查是否已经打开

return 0;

board_setup_psc(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 1); // 1代表使能

return 0;

}

/* 关闭模块的电源和时钟 */

void __clk_disable(struct clk *clk)

{

if (clk->usecount) // 检查模块是否正在被使用

return;

board_setup_psc(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 0); // 0代表关闭

}

/* 减少时钟引用计数 */

void __clk_unuse(struct clk *clk)

{

if (clk->usecount > 0) {

--clk->usecount;

}

}

/* 增加时钟引用计数 */

int __clk_use(struct clk *clk)

{

int ret = 0;

clk->usecount++;

return ret;

}

/* __clk_enable函数的封装,其它驱动可调用。 */

int clk_enable(struct clk *clk)

{

unsigned long flags;

int ret;

spin_lock_irqsave(&clockfw_lock, flags); // 关中断和多CPU占用

ret = __clk_enable(clk);

spin_unlock_irqrestore(&clockfw_lock, flags);

if (davinci_pinmux_setup)

davinci_pinmux_setup(clk->lpsc); /* 打开其引脚到模块功能,dm644x各模块到功能引脚大多和GPIO功能复用,

故使用前要初始化,默认是模块功能一脚而非GPIO */

else

printk (KERN_WARNING "WARNING davinci_pinmux_setup "

"uninitialized\n");

return ret;

}

EXPORT_SYMBOL(clk_enable);

/* __clk_disable函数的封装 */

void clk_disable(struct clk *clk)

{

unsigned long flags;

spin_lock_irqsave(&clockfw_lock, flags);

__clk_disable(clk);

spin_unlock_irqrestore(&clockfw_lock, flags);

}

EXPORT_SYMBOL(clk_disable);

/* __clk_use函数的封装 */

int clk_use(struct clk *clk)

{

unsigned long flags;

int ret = 0;

spin_lock_irqsave(&clockfw_lock, flags);

ret = __clk_use(clk);

spin_unlock_irqrestore(&clockfw_lock, flags);

return ret;

}

EXPORT_SYMBOL(clk_use);

/* __clk_unuse函数的封装 */

void clk_unuse(struct clk *clk)

{

unsigned long flags;

spin_lock_irqsave(&clockfw_lock, flags);

__clk_unuse(clk);

spin_unlock_irqrestore(&clockfw_lock, flags);

}

EXPORT_SYMBOL(clk_unuse);

/* 获取模块时钟的频率 */

unsigned long clk_get_rate(struct clk *clk)

{

return *(clk->rate);

}

EXPORT_SYMBOL(clk_get_rate);

/* 添加时钟到链表中 */

int clk_register(struct clk *clk)

{

down(&clocks_sem);

list_add(&clk->node, &clocks);

up(&clocks_sem);

return 0;

}

EXPORT_SYMBOL(clk_register);

/* 将时钟从链表中清除 */

void clk_unregister(struct clk *clk)

{

down(&clocks_sem);

list_del(&clk->node);

up(&clocks_sem);

}

EXPORT_SYMBOL(clk_unregister);

/* dm644x平台所有模块的时钟*/

static struct clk davinci_dm644x_clks[] = {

{

.name = "ARMCLK", // 时钟名,ARM域的时钟

.rate = &armrate,

.lpsc = -1,

.flags = ALWAYS_ENABLED,

},

{

.name = "UART0",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_UART0,

.usecount = 1,

},

{

.name = "EMACCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_EMAC_WRAPPER,

},

{

.name = "I2CCLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_I2C,

},

{

.name = "IDECLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_ATA,

},

{

.name = "McBSPCLK0",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_McBSP0,

},

{

.name = "MMCSDCLK0",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_MMC_SD0,

},

{

.name = "SPICLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_SPI,

},

{

.name = "gpio",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_GPIO,

},

{

.name = "AEMIFCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_AEMIF,

.usecount = 1,

},

{

.name = "PWM0_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM0,

},

{

.name = "PWM1_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM1,

},

{

.name = "PWM2_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM2,

},

{

.name = "USBCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_USB,

},

};

/* dm6467平台所有模块的时钟*/

static struct clk davinci_dm6467_clks[] = {

{

.name = "ARMCLK",

.rate = &armrate,

.lpsc = -1,

.flags = ALWAYS_ENABLED,

},

{

.name = "UART0",

.rate = &fixedrate,

.lpsc = DAVINCI_DM646X_LPSC_UART0,

.usecount = 1,

},

{

.name = "UART1",

.rate = &fixedrate,

.lpsc = DAVINCI_DM646X_LPSC_UART1,

.usecount = 1,

},

{

.name = "UART2",

.rate = &fixedrate,

.lpsc = DAVINCI_DM646X_LPSC_UART2,

.usecount = 1,

},

{

.name = "EMACCLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_EMAC,

},

{

.name = "I2CCLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_I2C,

},

{

.name = "IDECLK",

.rate = &div_by_six,

.lpsc = DAVINCI_LPSC_ATA,

},

{

.name = "McASPCLK0",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_McASP0,

},

{

.name = "McASPCLK1",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_McASP1,

},

{

.name = "SPICLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_SPI,

},

{

.name = "AEMIFCLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_AEMIF,

.usecount = 1,

},

{

.name = "PWM0_CLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_PWM0,

},

{

.name = "PWM1_CLK",

.rate = &div_by_four,

.lpsc = DAVINCI_DM646X_LPSC_PWM1,

},

{

.name = "USBCLK",

.rate = &div_by_four,

.lpsc = DAVINCI_LPSC_USB,

},

};

/* dm355平台所有模块的时钟*/

static struct clk davinci_dm355_clks[] = {

{

.name = "ARMCLK",

.rate = &armrate,

.lpsc = -1,

.flags = ALWAYS_ENABLED,

},

{

.name = "UART0",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_UART0,

.usecount = 1,

},

{

.name = "UART1",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_UART1,

.usecount = 1,

},

{

.name = "UART2",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_UART2,

.usecount = 1,

},

{

.name = "EMACCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_EMAC_WRAPPER,

},

{

.name = "I2CCLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_I2C,

},

{

.name = "IDECLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_ATA,

},

{

.name = "McBSPCLK0",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_McBSP0,

},

{

.name = "McBSPCLK1",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_McBSP1,

},

{

.name = "MMCSDCLK0",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_MMC_SD0,

},

{

.name = "MMCSDCLK1",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_MMC_SD1,

},

{

.name = "SPICLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_SPI,

},

{

.name = "gpio",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_GPIO,

},

{

.name = "AEMIFCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_AEMIF,

.usecount = 1,

},

{

.name = "PWM0_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM0,

},

{

.name = "PWM1_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM1,

},

{

.name = "PWM2_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM2,

},

{

.name = "PWM3_CLK",

.rate = &fixedrate,

.lpsc = DAVINCI_LPSC_PWM3,

},

{

.name = "USBCLK",

.rate = &commonrate,

.lpsc = DAVINCI_LPSC_USB,

},

};

/*

clk初始化,采用一个链表把所有到时钟结构体链接在一起,该链表的头是clocks。

davinci_clk_init()例程由io.c中的davinci_map_common_io()例程调用,后者被

davinci_map_io()例程调用,而其又被注册到board_evm.c中的机器描述符中(struct machine_desc),

故具体调用过程是:

start_kernel()-->setup_arch()-->paging_init()-->mdesc->map_io()

(其就是davinci_map_io())-->davinci_map_common_io()-->davinci_clk_init()。

*/

void davinci_clk_init(void)

{

struct clk *clkp;

static struct clk *board_clks;

int count = 0, num_clks;

if (cpu_is_davinci_dm355()) { // dm355平台时钟初始化

/*

* FIXME

* We're assuming a 24MHz reference, but the DM355 also

* supports a 36MHz reference.

*/

unsigned long postdiv;

/*

* Read the PLL1 POSTDIV register to determine if the post

* divider is /1 or /2

*/

postdiv = (davinci_readl(DAVINCI_PLL_CNTRL0_BASE + 0x128)

& 0x1f) + 1;

fixedrate = 24000000;

armrate = (PLL1_PLLM + 1) * (fixedrate / (16 * postdiv));

commonrate = armrate / 2;

board_clks = davinci_dm355_clks;

num_clks = ARRAY_SIZE(davinci_dm355_clks);

} else if (cpu_is_davinci_dm6467()) { // dm6467平台时钟初始化

fixedrate = 24000000;

div_by_four = ((PLL1_PLLM + 1) * 27000000) / 4;

div_by_six = ((PLL1_PLLM + 1) * 27000000) / 6;

div_by_eight = ((PLL1_PLLM + 1) * 27000000) / 8;

armrate = ((PLL1_PLLM + 1) * 27000000) / 2;

board_clks = davinci_dm6467_clks;

num_clks = ARRAY_SIZE(davinci_dm6467_clks);

} else { // dm644X平台时钟初始化

fixedrate = 27000000; // 平台输入频率,即晶振频率

armrate = (PLL1_PLLM + 1) * (fixedrate / 2); // arm时钟,其为DSP时钟的一半

commonrate = armrate / 3; // 其他时钟为DSP时钟的1/6,arm时钟的1/3

board_clks = davinci_dm644x_clks;

num_clks = ARRAY_SIZE(davinci_dm644x_clks);

}

for (clkp = board_clks; count < num_clks; count++, clkp++) {

clk_register(clkp); // 将时钟注册到链表中

/* Turn on clocks that have been enabled in the

* table above */

if (clkp->usecount) { // 如果该时钟已经被使用,则开启它

clk_enable(clkp); // 使能该时钟

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: