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

 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);        // 使能该时钟
 
        }
     }
 }

 

posted @ 2013-06-11 22:07  爱生活,爱编程  阅读(455)  评论(0编辑  收藏  举报