Chinaunix首页 | 论坛 | 博客
  • 博客访问: 121061
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-10 21:57
文章分类

全部博文(31)

文章存档

2016年(4)

2015年(27)

我的朋友

分类: LINUX

2015-03-19 14:35:50

参考链接:http://blog.sina.com.cn/s/blog_753fd0b00100t9u7.html

内核时钟初始化函数是s3c24xx_init_clocks(12000000)。
改函数的从start_kernel-->setup_arch(&command_line)-->paging_init(mdesc)-->devicemaps_init(mdesc)-->if (mdesc->map_io)mdesc->map_io();-->mini2440_map_io-->s3c24xx_init_clocks(12000000).所以此函数是各个部分初始化的起点。
s3c24xx_init_clocks(12000000)定义如下:
--------------------------------------------------------------------------------------------------
void __init s3c24xx_init_clocks(int xtal)
{
 if (xtal == 0)
  xtal = 12*1000*1000;
 if (cpu == NULL)
  panic("s3c24xx_init_clocks: no cpu setup?\n");
 if (cpu->init_clocks == NULL)
  panic("s3c24xx_init_clocks: cpu has no clock init\n");
 else
  (cpu->init_clocks)(xtal);
}
--------------------------------------------------------------------------------------------------
这个函数中看到一个cpu,那么这个变量是什么。看文件plat-s3c/init.c:
static struct cpu_table *cpu;它是一个cpu_table类型的指针。如下是其结构定义:
--------------------------------------------------------------------------------------------------
struct cpu_table {
 unsigned long idcode;
 unsigned long idmask;
 void  (*map_io)(void);
 void  (*init_uarts)(struct s3c2410_uartcfg *cfg, int no);
 void  (*init_clocks)(int xtal);
 int  (*init)(void);
 const char *name;
};
--------------------------------------------------------------------------------------------------
这样就不难理解s3c24xx_init_clocks了。那么这个CPU指针具体指向什么呢。在文件cpu.c中有如下定义:
static const char name_s3c2400[]  = "S3C2400";
static const char name_s3c2410[]  = "S3C2410";
static const char name_s3c2412[]  = "S3C2412";
static const char name_s3c2440[]  = "S3C2440";
static const char name_s3c2442[]  = "S3C2442";
static const char name_s3c2442b[]  = "S3C2442B";
static const char name_s3c2443[]  = "S3C2443";
static const char name_s3c2410a[] = "S3C2410A";
static const char name_s3c2440a[] = "S3C2440A";
static struct cpu_table cpu_ids[] __initdata = {
 {
  .idcode  = 0x32410000,
  .idmask  = 0xffffffff,
  .map_io  = s3c2410_map_io,
  .init_clocks = s3c2410_init_clocks,
  .init_uarts = s3c2410_init_uarts,
  .init  = s3c2410_init,
  .name  = name_s3c2410
 },
 {
  .idcode  = 0x32410002,
  .idmask  = 0xffffffff,
  .map_io  = s3c2410_map_io,
  .init_clocks = s3c2410_init_clocks,
  .init_uarts = s3c2410_init_uarts,
  .init  = s3c2410a_init,
  .name  = name_s3c2410a
 },
 {
  .idcode  = 0x32440000,
  .idmask  = 0xffffffff,
  .map_io  = s3c244x_map_io,
  .init_clocks = s3c244x_init_clocks,
  .init_uarts = s3c244x_init_uarts,
  .init  = s3c2440_init,
  .name  = name_s3c2440
 },
 {
  .idcode  = 0x32440001,
  .idmask  = 0xffffffff,
  .map_io  = s3c244x_map_io,
  .init_clocks = s3c244x_init_clocks,
  .init_uarts = s3c244x_init_uarts,
  .init  = s3c2440_init,
  .name  = name_s3c2440a
 },
 {
  .idcode  = 0x32440aaa,
  .idmask  = 0xffffffff,
  .map_io  = s3c244x_map_io,
  .init_clocks = s3c244x_init_clocks,
  .init_uarts = s3c244x_init_uarts,
  .init  = s3c2442_init,
  .name  = name_s3c2442
 },
 {
  .idcode  = 0x32440aab,
  .idmask  = 0xffffffff,
  .map_io  = s3c244x_map_io,
  .init_clocks = s3c244x_init_clocks,
  .init_uarts = s3c244x_init_uarts,
  .init  = s3c2442_init,
  .name  = name_s3c2442b
 },
 {
  .idcode  = 0x32412001,
  .idmask  = 0xffffffff,
  .map_io  = s3c2412_map_io,
  .init_clocks = s3c2412_init_clocks,
  .init_uarts = s3c2412_init_uarts,
  .init  = s3c2412_init,
  .name  = name_s3c2412,
 },
 {   
  .idcode  = 0x32412003,
  .idmask  = 0xffffffff,
  .map_io  = s3c2412_map_io,
  .init_clocks = s3c2412_init_clocks,
  .init_uarts = s3c2412_init_uarts,
  .init  = s3c2412_init,
  .name  = name_s3c2412,
 },
 {
  .idcode  = 0x32443001,
  .idmask  = 0xffffffff,
  .map_io  = s3c2443_map_io,
  .init_clocks = s3c2443_init_clocks,
  .init_uarts = s3c2443_init_uarts,
  .init  = s3c2443_init,
  .name  = name_s3c2443,
 },
 {
  .idcode  = 0x0,  
  .idmask  = 0xffffffff,
  .map_io  = s3c2400_map_io,
  .init_clocks = s3c2400_init_clocks,
  .init_uarts = s3c2400_init_uarts,
  .init  = s3c2400_init,
  .name  = name_s3c2400
 },
};

怎么确定是调用哪一个.init_clocks回调函数?start_kernel-->setup_arch(&command_line)-->paging_init(mdesc)-->devicemaps_init(mdesc)-->if (mdesc->map_io)mdesc->map_io();-->mini2440_map_io->s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc))  -->s3c24xx_init_io
来看s3c24xx_init_io这个函数

点击(此处)折叠或打开

  1. void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
  2. {
  3.     unsigned long idcode = 0x0;

  4.     /* initialise the io descriptors we need for initialisation */
  5.     iotable_init(mach_desc, size);//开发板层次的静态映射
  6.     iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));//最小系统层次的静态映射

  7.     if (cpu_architecture() >= CPU_ARCH_ARMv5) {
  8.         idcode = s3c24xx_read_idcode_v5();
  9.     } else {
  10.         idcode = s3c24xx_read_idcode_v4();
  11.     }

  12.     arm_pm_restart = s3c24xx_pm_restart;

  13.     s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
  14. }

点击(此处)折叠或打开

  1. static unsigned long s3c24xx_read_idcode_v4(void)
  2. {
  3. #ifndef CONFIG_CPU_S3C2400
  4.     return __raw_readl(S3C2410_GSTATUS1);//S3C2410_GSTATUS1 读取chip ID
  5. #else
  6.     return 0UL;
  7. #endif
  8. }

点击(此处)折叠或打开

  1. void __init s3c_init_cpu(unsigned long idcode,
  2.              struct cpu_table *cputab, unsigned int cputab_size)
  3. {
  4.     cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);

  5.     if (cpu == NULL) {
  6.         printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
  7.         panic("Unknown S3C24XX CPU");
  8.     }

  9.     printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);

  10.     if (cpu->map_io == NULL || cpu->init == NULL) {
  11.         printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
  12.         panic("Unsupported Samsung CPU");
  13.     }

  14.     cpu->map_io();
  15. }

点击(此处)折叠或打开

  1. static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
  2.                         struct cpu_table *tab,
  3.                         unsigned int count)
  4. {
  5.     for (; count != 0; count--, tab++) {
  6.         if ((idcode & tab->idmask) == (tab->idcode & tab->idmask))
  7.             return tab;
  8.     }

  9.     return NULL;
  10. }
由上述代码可值,通过比较芯片chip ID 和 tab->idcode,从cpu_table中找到正确的回调函数



接着就到了s3c244x.c中的函数s3c244x_init_clocks:
---------------------------------------------------------
void __init s3c244x_init_clocks(int xtal)
{
 
 s3c24xx_register_baseclocks(xtal);
 s3c244x_setup_clocks();
 s3c2410_baseclk_add();
}
--------------------------------------------------------------------------------------------------
在s3c244x_init_clocks函数里,有三个函数
s3c24xx_register_baseclocks(xtal);这个函数用来注册clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p六个clk类型的时钟。
s3c244x_setup_clocks()是时钟的设置。
s3c2410_baseclk_add()添加不同部件的时钟,以便驱动中使用。后面会相信分析。
下面一个一个来分析他们这三个函数到底做了什么工作。
s3c24xx_register_baseclocks(xtal)函数定义如下:
-------------------------------------------------
int __init s3c24xx_register_baseclocks(unsigned long xtal)
{
 printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");
 clk_xtal.rate = xtal;
 
 if (s3c24xx_register_clock(&clk_xtal) < 0)
  printk(KERN_ERR "failed to register master xtal\n");
 if (s3c24xx_register_clock(&clk_mpll) < 0)
  printk(KERN_ERR "failed to register mpll clock\n");
 if (s3c24xx_register_clock(&clk_upll) < 0)
  printk(KERN_ERR "failed to register upll clock\n");
 if (s3c24xx_register_clock(&clk_f) < 0)
  printk(KERN_ERR "failed to register cpu fclk\n");
 if (s3c24xx_register_clock(&clk_h) < 0)
  printk(KERN_ERR "failed to register cpu hclk\n");
 if (s3c24xx_register_clock(&clk_p) < 0)
  printk(KERN_ERR "failed to register cpu pclk\n");
 return 0;
}
--------------------------------------------------------
就如上面所说的,这个函数通过调用s3c24xx_register_clock()一一注册了六个时钟变量clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p。它们定义在文件plat-s3c的clock.c中:
struct clk clk_xtal = {
 .name  = "xtal",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
};
struct clk clk_ext = {
 .name  = "ext",
 .id  = -1,
};
struct clk clk_epll = {
 .name  = "epll",
 .id  = -1,
};
struct clk clk_mpll = {
 .name  = "mpll",
 .id  = -1,
 .set_rate = clk_default_setrate,
};
struct clk clk_upll = {
 .name  = "upll",
 .id  = -1,
 .parent  = NULL,
 .ctrlbit = 0,
};
struct clk clk_f = {
 .name  = "fclk",
 .id  = -1,
 .rate  = 0,
 .parent  = &clk_mpll,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};
struct clk clk_h = {
 .name  = "hclk",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};
struct clk clk_p = {
 .name  = "pclk",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};
struct clk结构体的定义如下:
---------------------------------------------------------------------------------------
struct clk {
 struct list_head      list;
 struct module        *owner;
 struct clk           *parent;
 const char           *name;
 int        id;
 int        usage;
 unsigned long         rate;
 unsigned long         ctrlbit;
 int      (*enable)(struct clk *, int enable);
 int      (*set_rate)(struct clk *c, unsigned long rate);
 unsigned long     (*get_rate)(struct clk *c);
 unsigned long     (*round_rate)(struct clk *c, unsigned long rate);
 int      (*set_parent)(struct clk *c, struct clk *parent);
};
------------------------------------------------------------------------------------------
注册代码如下:
-------------------------------------------------------------------------------------
int s3c24xx_register_clock(struct clk *clk)
{
 if (clk->enable == NULL)
  clk->enable = clk_null_enable;
 
 
 BUG_ON(clk->list.prev != clk->list.next);
 spin_lock(&clocks_lock);
 list_add(&clk->list, &clocks);
 spin_unlock(&clocks_lock);
 return 0;
}
--------------------------------------------------------
主要看list_add(&clk->list,&clocks).系统中存在一个clocks的列表,这个就是通过list_add()把clk的list添加到这个列表中,从而注册。
 
s3c244x_setup_clocks()函数如下:
---------------------------------------------------------------------------

点击(此处)折叠或打开

  1. void __init_or_cpufreq s3c244x_setup_clocks(void)
  2. {
  3.  struct clk *xtal_clk;
  4.  unsigned long clkdiv;
  5.  unsigned long camdiv;
  6.  unsigned long xtal;
  7.  unsigned long hclk, fclk, pclk;
  8.  int hdiv = 1;
  9.  xtal_clk = clk_get(NULL, "xtal");//clk_get从一个时钟list链表中以字符id名称来查找一个时钟clk结构体并且返回
  10.  xtal = clk_get_rate(xtal_clk);//返回 xtal_clk->rate
  11.  clk_put(xtal_clk);//module_put(clk->owner);?
  12.  fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; //获取fclk频率
  13.  clkdiv = __raw_readl(S3C2410_CLKDIVN);//读取分频寄存器
  14.  camdiv = __raw_readl(S3C2440_CAMDIVN);
  15.  
  16.  switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
  17.  case S3C2440_CLKDIVN_HDIVN_1://HCLK = FCLK/1.
  18.   hdiv = 1;
  19.   break;
  20.  case S3C2440_CLKDIVN_HDIVN_2://HCLK = FCLK/2
  21.   hdiv = 2;
  22.   break;
  23.  case S3C2440_CLKDIVN_HDIVN_4_8://HCLK = FCLK/4 when CAMDIVN[9] = 0    HCLK= FCLK/8 when CAMDIVN[9] = 1
  24.   hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
  25.   break;
  26.  case S3C2440_CLKDIVN_HDIVN_3_6://11 : HCLK = FCLK/3 when CAMDIVN[8] = 0  HCLK = FCLK/6 when CAMDIVN[8] = 1.
  27.   hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
  28.   break;
  29.  }
  30.  hclk = fclk / hdiv;//计算hclk
  31.  pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);//计算pclk
  32.  
  33.  printk("S3C244X: core %ld.ld MHz, memory %ld.ld MHz, peripheral %ld.ld MHz\n",
  34.         print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
  35.  s3c24xx_setup_clocks(fclk, hclk, pclk);
  36. }

点击(此处)折叠或打开

  1. static inline unsigned int
  2. s3c24xx_get_pll(unsigned int pllval, unsigned int baseclk)
  3. {
  4.     unsigned int mdiv, pdiv, sdiv;
  5.     uint64_t fvco;

  6.     mdiv = pllval >> S3C24XX_PLLCON_MDIVSHIFT;
  7.     pdiv = pllval >> S3C24XX_PLLCON_PDIVSHIFT;
  8.     sdiv = pllval >> S3C24XX_PLLCON_SDIVSHIFT;
            //提取MPLLCON 寄存器相关值
  1.     mdiv &= S3C24XX_PLLCON_MDIVMASK;
  2.     pdiv &= S3C24XX_PLLCON_PDIVMASK;
  3.     sdiv &= S3C24XX_PLLCON_SDIVMASK;

  4.     fvco = (uint64_t)baseclk * (mdiv + 8);// mpll(fclk) = ( 2 *(mdiv + 8) * 12000000 )/((pdiv + 2) * (1<))
  5.     do_div(fvco, (pdiv + 2) << sdiv);   //do_div(a,b) = a/b,做除法

  6.     return (unsigned int)fvco;
  7. }

点击(此处)折叠或打开

  1. void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk,
  2.                      unsigned long hclk,
  3.                      unsigned long pclk)
  4. {
  5.     clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON),
  6.                     clk_xtal.rate);

  7.     clk_mpll.rate = fclk;
  8.     clk_h.rate = hclk;
  9.     clk_p.rate = pclk;
  10.     clk_f.rate = fclk;
  11. }


---------------------------------------------------------------------------
在这里有必要简单的说下s3c2440的时钟发生模块
linuxBSPmini2440之时钟
 
主时钟源来自外部晶振(XTIpll)或者外部时钟。用芯片引脚OM[3:2]来选择,一般情况下,OM3和OM2接地,这样,主时钟源即为外部晶振(此时USB时钟源也是外部晶振)。集成电路MPLL产生一个在频率和相位上同步于参考输入信号的输出信号,计算公式为:
mpll(fclk) = ( 2 *(mdiv + 8) *  Fin)/((pdiv + 2) * (1<

        图中的CLKCNTL模块主要由时钟控制寄存器CLKCON和慢速时钟控制寄存器CLKSLOW两个寄存器组成,一方面,它可以控制外围设备的时钟使能,从而实现功耗控制;另一方面,它还可以控制PLL,使FCLK=Mpll或者FCLK=输入时钟(外部晶振或者外部时钟)的分频。
 
        FCLK作为芯片核心ARM920T模块的时钟。
        HCLK也可以为ARM920T提供时钟(当CAMERA时钟分频寄存器的DVS_EN位为1时,HCLK作为ARM核心时钟),HCLK也为内存控制器、中断控制器、LCD控制器、DMA和USB HOST模块提供时钟。
        PCLK为WDT、IIS、IIC、PWM定时器、MMC接口、ADC、UART、GPIO、RTC和SPI提供时钟。
        FCLK、HCLK和PCLK之间的关系由时钟分频控制寄存器CLKDIVN(包括HDIVN和PDIVN两个部分)来决定。
 
        图中的USBCNTL模块主要是为USB host接口和USB设备接口提供正常工作需要的48MHz时钟,UCLK主要有4个状态:
1)刚刚复位时,UCLK为外部晶振或外部时钟。
2)配置UPLL结束后,在UPLL稳定时间内,UCLK为低电平,UPLL稳定时间结束,UCLK为48MHz。
3)CLKSLOW寄存器关闭UPLL,UCLK为外部晶振或者外部时钟。
4)CLKSLOW寄存器打开UPLL,UCLK为48MHz。
 
        图中的POWERCNTL模块主要实现功耗控制的功能。PLL稳定时间由稳定时间寄存器LOCKTIME来控制,此寄存器一般设为0xFFFFFFFF。
 
下面接着函数分析,在函数s3c244x_setup_clocks()中用到了clk_get,clk_get_rate,clk_put这些函数,这些都是对时钟机制的一个封装,对外提供的API接口。
在plat-s3c/clock.c中可以看到这些API的定义:
//传入参数dev和dev获取相应的模块的时钟clk结构体
struct clk *clk_get(struct device *dev, const char *id)
{
 struct clk *p;
 struct clk *clk = ERR_PTR(-ENOENT);
 int idno;
 if (dev == NULL || dev->bus != &platform_bus_type)
  idno = -1;
 else
  idno = to_platform_device(dev)->id;
 spin_lock(&clocks_lock);

//遍历clocks为链表头的实体链表,对实体链表中的数据结构进行处理,这是个for循环语句
 list_for_each_entry(p, &clocks, list) {   
//比较时钟的名字
  if (p->id == idno &&
      strcmp(id, p->name) == 0 &&
      try_module_get(p->owner)) {
   clk = p;//找到对应clk 结构
   break;
  }
 }
 
 if (IS_ERR(clk)) {
  list_for_each_entry(p, &clocks, list) {
   if (p->id == -1 && strcmp(id, p->name) == 0 &&
       try_module_get(p->owner)) {
    clk = p;
    break;
   }
  }
 }
 spin_unlock(&clocks_lock);
 return clk;
}
void clk_put(struct clk *clk)
{
 module_put(clk->owner);
}
//启动时钟
int clk_enable(struct clk *clk)
{
 if (IS_ERR(clk) || clk == NULL)
  return -EINVAL;
 clk_enable(clk->parent);
 spin_lock(&clocks_lock);
 if ((clk->usage++) == 0)
  (clk->enable)(clk, 1);
 spin_unlock(&clocks_lock);
 return 0;
}
//禁止时钟,通过调用具体clk的disable
void clk_disable(struct clk *clk)
{
 if (IS_ERR(clk) || clk == NULL)
  return;
 spin_lock(&clocks_lock);
 if ((--clk->usage) == 0)
  (clk->enable)(clk, 0);
 spin_unlock(&clocks_lock);
 clk_disable(clk->parent);
}


unsigned long clk_get_rate(struct clk *clk)
{
 if (IS_ERR(clk))
  return 0;
 if (clk->rate != 0)
  return clk->rate;
 if (clk->get_rate != NULL)
  return (clk->get_rate)(clk);
 if (clk->parent != NULL)
  return clk_get_rate(clk->parent);
 return clk->rate;
}
long clk_round_rate(struct clk *clk, unsigned long rate)
{
 if (!IS_ERR(clk) && clk->round_rate)
  return (clk->round_rate)(clk, rate);
 return rate;
}
int clk_set_rate(struct clk *clk, unsigned long rate)
{
 int ret;
 if (IS_ERR(clk))
  return -EINVAL;
 
 WARN_ON(clk->set_rate == NULL);
 if (clk->set_rate == NULL)
  return -EINVAL;
 spin_lock(&clocks_lock);
 ret = (clk->set_rate)(clk, rate);
 spin_unlock(&clocks_lock);
 return ret;
}
struct clk *clk_get_parent(struct clk *clk)
{
 return clk->parent;
}
int clk_set_parent(struct clk *clk, struct clk *parent)
{
 int ret = 0;
 if (IS_ERR(clk))
  return -EINVAL;
 spin_lock(&clocks_lock);
 if (clk->set_parent)
  ret = (clk->set_parent)(clk, parent);
 spin_unlock(&clocks_lock);
 return ret;
}
EXPORT_SYMBOL(clk_get);
EXPORT_SYMBOL(clk_put);
EXPORT_SYMBOL(clk_enable);
EXPORT_SYMBOL(clk_disable);
EXPORT_SYMBOL(clk_get_rate);
EXPORT_SYMBOL(clk_round_rate);
EXPORT_SYMBOL(clk_set_rate);
EXPORT_SYMBOL(clk_get_parent);
EXPORT_SYMBOL(clk_set_parent);
上面的函数比较简单,都是在clocks这个列表中通过一定的条件获得clk,或者通过获得的clk调用clk结构体里面的定义函数完成相应的功能。
xtal_clk = clk_get(NULL, "xtal");获取xtal即描述晶振的clk结构体。xtal = clk_get_rate(xtal_clk);获得晶振的频率为12MHz,我们可以看到在s3c24xx_register_baseclocks函数中clk_xtal.rate = xtal;实现赋值。
clk_put(xtal_clk);不太理解。好像与模块有关。
fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;这个函数通过读取寄存器值在通过公式计算出FCLK的值具体的计算方法看看datasheet。
然后clkdiv = __raw_readl(S3C2410_CLKDIVN);
 camdiv = __raw_readl(S3C2440_CAMDIVN);获得了寄存器CLKDIVN和CAMDIVN的值
寄存器描述如下:
linuxBSPmini2440之时钟
linuxBSPmini2440之时钟
所以接下来的switch()语句得到一个hdiv值来进一步获得hclk和pclk的频率。这两个频率相当重要,作用上面已经提到。最后一个是s3c24xx_setup_clocks(),不知道你们又没留心看有上面提到的clk_upll,clk_mpll,clk_h,clk_f没有初始化他们的频率,那么s3c24xx_setup_clocks()正是起到了这样的一个作用。s3c244x_setup_clocks()函数分析完了,接下来是s3c2410_baseclk_add()函数。从这个函数表面的意思来看,是添加一些基本的时钟。的确如此。这些时钟是一些抽象的概念,板上一个设备对应相应的一个时钟。从本质上看,这些时钟其实都是pclk,hclk,fclk等。只不过是面向对象话得抽象了。那么这些时钟有哪里呢。都是些什么。
文件s3c2410-clock.c中定义了两个数组static struct clk init_clocks[] ,static struct clk init_clocks_disable[]。
---------------------------------------------------------------------------------------
static struct clk init_clocks_disable[] = {
 {
  .name  = "nand",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_NAND,
 }, {
  .name  = "sdi",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_SDI,
 }, {
  .name  = "adc",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_ADC,
 }, {
  .name  = "i2c",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_IIC,
 }, {
  .name  = "iis",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_IIS,
 }, {
  .name  = "spi",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_SPI,
 }
};
static struct clk init_clocks[] = {
 {
  .name  = "lcd",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_LCDC,
 }, {
  .name  = "gpio",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_GPIO,
 }, {
  .name  = "usb-host",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_USBH,
 }, {
  .name  = "usb-device",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_USBD,
 }, {
  .name  = "timers",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_PWMT,
 }, {
  .name  = "uart",
  .id  = 0,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART0,
 }, {
  .name  = "uart",
  .id  = 1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART1,
 }, {
  .name  = "uart",
  .id  = 2,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART2,
 }, {
  .name  = "rtc",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_RTC,
 }, {
  .name  = "watchdog",
  .id  = -1,
  .parent  = &clk_p,
  .ctrlbit = 0,
 }, {
  .name  = "usb-bus-host",
  .id  = -1,
  .parent  = &clk_usb_bus,
 }, {
  .name  = "usb-bus-gadget",
  .id  = -1,
  .parent  = &clk_usb_bus,
 },
}; 
--------------------------------------------------------------------------------------------------
这两个又什么区别呢,init_clocks_disable[]数组里面的时钟在系统启动的时候要关闭的,而init_clocks里面的是要系统启动要初始化的。所以在启动时间我们要谨慎的关闭我们哪些不需要的时钟。就像LCD,如果启动的时候启动时钟打开DMA请求,那么就会出问题。相反启动过后LCD运行着关闭他的时钟也是很危险的。
下面是s3c2410_baseclk_add()的源码:
--------------------------------------------------------------------------------------------
int __init s3c2410_baseclk_add(void)
{
 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
 unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
 struct clk *clkp;
 struct clk *xtal;
 int ret;
 int ptr;
 clk_upll.enable = s3c2410_upll_enable;
 if (s3c24xx_register_clock(&clk_usb_bus) < 0)
  printk(KERN_ERR "failed to register usb bus clock\n");
 
 clkp = init_clocks;
 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
  
  clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
  ret = s3c24xx_register_clock(clkp);
  if (ret < 0) {
   printk(KERN_ERR "Failed to register clock %s (%d)\n",
          clkp->name, ret);
  }
 }
 
 
 clkp = init_clocks_disable;
 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
  ret = s3c24xx_register_clock(clkp);
  if (ret < 0) {
   printk(KERN_ERR "Failed to register clock %s (%d)\n",
          clkp->name, ret);
  }
  s3c2410_clkcon_enable(clkp, 0);
 }
 
 xtal = clk_get(NULL, "xtal");
 printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
        print_mhz(clk_get_rate(xtal) /
    ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
        (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
        (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
        (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
 s3c_pwmclk_init();
 return 0;
}
---------------------------------------------------------------------------------------------
两个for语句分别安装了上面提到的两个数组的时钟,相关的初始化结构体。赋值了clk_upll.enable。最后面初始化和注册了PWM时钟。PWM时钟的注册和其他分开始因为它比较特别。
阅读(1744) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~