Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2278887
  • 博文数量: 668
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 8588
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-29 19:22
文章分类

全部博文(668)

文章存档

2011年(1)

2010年(2)

2009年(273)

2008年(392)

分类:

2009-05-04 18:00:53

/*
 * linux/arch/arm/mach-davinci/clock.c
 *
 * TI DaVinci clock config file
 *
 * Copyright (C) 2006 Texas Instruments.
 *
 * ----------------------------------------------------------------------------
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ----------------------------------------------------------------------------
 *
 */

 
/* 该文件中到程序实现了各模块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 = (<< domain);
        while ((((EPCPR >> domain) & 1) == 0)) ;

        PDCTL1 |= 0x100;
        while (!(((PTSTAT >> domain) & 1) == 0)) ;
    } else {
        PTCMD = (<< 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);        // 使能该时钟

        }
    }
}
阅读(1012) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~