--------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http:// zhiqiang0071.cublog.cn -------------------------------------------- /* * 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 = (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 */ 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); // 使能该时钟 } } } 本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79779/showart_1356828.html |