本文出处:http://blogold.chinaunix.net/u3/93291/showart_2071102.html

逢山开路 遇水架桥,今天想自己写个adc的驱动,发现不清楚系统各个模块的系统时钟如何使用。
总不能自己想怎么弄,就怎么弄吧,还是学学框架吧——使用时钟的框架。

adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
        printk(KERN_ERR "failed to get adc clock source/n");
        return -ENOENT;
    }
    clk_use(adc_clock);
    clk_enable(adc_clock);

上面的这段代码是touchscreen的驱动中的一段,我不清楚,所以去学学系统各个模块时钟的使用方式。
在系统的初始化的时候,看见过,但是忘了,在回顾一下。

那是在paging_init()中调用了 mdesc->map_io(),

void __init sbc2440_map_io(void)
{
    s3c24xx_init_io(sbc2440_iodesc, ARRAY_SIZE(sbc2440_iodesc));
    s3c24xx_init_clocks(12000000); //这个是系统各个部分始终初始化的起点
    s3c24xx_init_uarts(sbc2440_uartcfgs, ARRAY_SIZE(sbc2440_uartcfgs));
    s3c24xx_set_board(&sbc2440_board);

s3c_device_nand.dev.platform_data = &bit_nand_info;
}

跟 cpu_table 有关,拷贝过来

/* table of supported CPUs */

static const char name_s3c2410[]  = "S3C2410";
static const char name_s3c2440[]  = "S3C2440";
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        = s3c2410_init,
        .name        = name_s3c2410a
    },
    {
        .idcode        = 0x32440000,
        .idmask        = 0xffffffff,
        .map_io        = s3c2440_map_io,
        .init_clocks    = s3c2440_init_clocks,
        .init_uarts    = s3c2440_init_uarts,
        .init        = s3c2440_init,
        .name        = name_s3c2440
    },
    {
        .idcode        = 0x32440001,
        .idmask        = 0xffffffff,
        .map_io        = s3c2440_map_io,
        .init_clocks    = s3c2440_init_clocks,
        .init_uarts    = s3c2440_init_uarts,
        .init        = s3c2440_init,
        .name        = name_s3c2440a
    }
};

和时钟相关的调用路径: 在 s3c24xx_init_clocks() -> (cpu->init_clocks)(xtal)-> s3c24xx_setup_clocks()
这个s3c24xx_setup_clocks()注册了系统的所有时钟,仔细看看它。

在这个函数被调用之前,代码已经根据 S3C2410_MPLLCON,S3C2410_CLKDIVN寄存器 和 晶振 的频率计算出了
fclk,hclk,pclk,他们应该分别是400M,100M,50M。

struct clk {
    struct list_head      list;
    struct module        *owner;
    struct clk           *parent;
    const char           *name;
    int              id;
    atomic_t              used;
    unsigned long         rate;
    unsigned long         ctrlbit;
    int            (*enable)(struct clk *, int enable);
};

clk数据结构是系统中时钟的抽象,它用list串成一个双向链表,在这个clocks链表里的clk结构,说明是系统中已经注册的,
parent表示他的来源,f,h,p之一,name是寻找到某个clk的唯一标识。enable是面向对象的思想的体现,不过,这里
没有用到,只是全部被填充为 s3c24xx_clkcon_enable()。

/* clock information */

static LIST_HEAD(clocks);
static DECLARE_MUTEX(clocks_sem);

/* clock definitions */

static struct clk init_clocks[] = {
    { .name    = "nand",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_NAND
    },
    { .name    = "lcd",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_LCDC
    },
    { .name    = "usb-host",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_USBH
    },
    { .name    = "usb-device",
      .id       = -1,
      /*.parent  = &clk_h, */
      .parent  = &clk_xtal,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_USBD
    },
    { .name    = "timers",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_PWMT
    },
    { .name    = "sdi",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_SDI
    },
    { .name    = "uart",
      .id       = 0,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_UART0
    },
    { .name    = "uart",
      .id       = 1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_UART1
    },
    { .name    = "uart",
      .id       = 2,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_UART2
    },
    { .name    = "gpio",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_GPIO
    },
    { .name    = "rtc",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_RTC
    },
    { .name    = "adc",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_ADC
    },
    { .name    = "i2c",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_IIC
    },
    { .name    = "iis",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_IIS
    },
    { .name    = "spi",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c24xx_clkcon_enable,
      .ctrlbit = S3C2410_CLKCON_SPI
    },
    { .name    = "watchdog",
      .id       = -1,
      .parent  = &clk_p,
      .ctrlbit = 0
    }
};

仔细看,usb-device 的parent有些特别,watchdog没有enable,只有uart才有id,其他的id都是-1。

下面可以看 s3c24xx_setup_clocks()了,像所注视的那样,它初始化了所有的时钟,其实是注册到clocks链表里面,以后可以从clocks
链表中找到。

/* initalise all the clocks */

int __init s3c24xx_setup_clocks(unsigned long xtal,
                unsigned long fclk,
                unsigned long hclk,
                unsigned long pclk)
{
    struct clk *clkp = init_clocks;
    int ptr;
    int ret;

printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics/n");

/* initialise the main system clocks */

clk_xtal.rate = xtal;

clk_h.rate = hclk;
    clk_p.rate = pclk;
    clk_f.rate = fclk;

上面的时钟是祖宗级别的,他们的频率已经被确定了。
分别代表晶震12Mhz,arm核400M,h总线100M,p总线50M。

/* it looks like just setting the register here is not good
     * enough, and causes the odd hang at initial boot time, so
     * do all of them indivdually.
     *
     * I think disabling the LCD clock if the LCD is active is
     * very dangerous, and therefore the bootloader should be
     * careful to not enable the LCD clock if it is not needed.
     *
     * and of course, this looks neater
     */

s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0);  // ghcstop: disable? ==> enable
    s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0);
    s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0);
    s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0);
    s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0);
    s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0);
    //s3c24xx_clk_enable(S3C2410_CLKCON_IIS, 1); // default value is 1 ==> enable

s3c24xx_clk_enable用来使能/禁止系统对某个模块供应时钟,他操作的对象是CLKCON,这个寄存器的bit[4~20]每位代表
了系统中的一个模块的时钟供应情况,要么使能,要么禁止。bit[2~3]分别代表idle和sleep模式,所以s3c24xx_clk_enable
总是去擦出这两个bit位。然后根据第2个参数去打开(1)/禁止(0)对模个模块的时钟供应。
显然,上面的操作都是禁止时钟供应的,包括nand,usbhost,usbdevice,adc,iic,spi。

/* assume uart clocks are correctly setup */

/* register our clocks */

if (s3c24xx_register_clock(&clk_xtal) < 0)
        printk(KERN_ERR "failed to register master xtal/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");

s3c24xx_register_clock用于注册这个时钟到clocks链表,他还设置clk的owner成员为内核模块所拥有,
并且设置clk->used原子型结构为没有被使用(0),然后根据clk->enable有无初始值,为没有初始值的设置一个
哑clk_null_enable,上面的四个base clock都是不能被关闭的,所以他们的clk->enable成员都是clk_null_enable

/* register clocks from clock array */

for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
        ret = s3c24xx_register_clock(clkp);
        if (ret < 0) {
            printk(KERN_ERR "Failed to register clock %s (%d)/n",
                   clkp->name, ret);
        }
    }

上面完成了系统其他部分时钟初始化,当然这部分才是我们关心的内容,这些模块的时钟源都来自base clock。
其中watchdog没有enable成员,不能被关闭。

return 0;
}//s3c24xx_setup_clocks()end

下面是四个系统的基本时钟,clk_xtal代表晶震。
他们的rate都被上面的函数确定了,而其他部分的时钟还没有rate呢。

/* base clocks */

static struct clk clk_xtal = {
    .name        = "xtal",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

static struct clk clk_f = {
    .name        = "fclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

static struct clk clk_h = {
    .name        = "hclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

static struct clk clk_p = {
    .name        = "pclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

宏THIS_MODULE,它的定义如下是#define THIS_MODULE (&__this_module),__this_module是一个struct module变量,
代表当前模块,跟current有几分相似。可以通过THIS_MODULE宏来引用模块的struct module结构

好了,回头看看让我晕的函数。

adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
        printk(KERN_ERR "failed to get adc clock source/n");
        return -ENOENT;
    }
    clk_use(adc_clock);
    clk_enable(adc_clock);

上面涉及到3个函数,分别是clk_get,clk_use,clk_enable()。
其中clk_get()的主要代码如下:

list_for_each_entry(p, &clocks, list) {
            if (p->id == -1 && strcmp(id, p->name) == 0 &&
                try_module_get(p->owner)) {
                clk = p;
                break;
            }
        }
看到了吧,不再clocks这个时钟链表里的时钟配置是不会被看到的,这都是s3c24xx_register_clock()函数的功劳,
然后他根据名字,找到对应的时钟结构,比如根据"adc"找到adc的clk结构,然后增加对这个模块的使用计数,最后返回
这个找到的clk指针。

clk_use()很简单,只是单纯的增加本时钟的使用
int clk_use(struct clk *clk)
{
    atomic_inc(&clk->used);
    return 0;
}

在看时钟打开函数,
clk_enable(adc_clock)
int clk_enable(struct clk *clk)
{
    if (IS_ERR(clk))
        return -EINVAL;

return (clk->enable)(clk, 1);
}
这里就体现出了面向对象的思想了,其中watchdog,四个基本的时钟是没有打开关闭的。
当然这个函数也是最主要的操作,他包含了对寄存器CLKCON的操作。

s3c24xx的时钟初始化相关推荐

  1. 【嵌入式开发】时钟初始化 ( 时钟相关概念 | 嵌入式时钟体系 | Lock Time | 分频参数设置 | CPU 异步模式设置 | APLL MPLL 时钟频率设置 )

    文章目录 一. 时钟相关概念解析 1. 相关概念术语 ( 1 ) 时钟脉冲信号 ( 概念 : 电压幅度 时间间隔 形成脉冲 | 作用 : 时序逻辑基础 间隔固定 根据脉冲数量可计算出时间 ) ( 2 ...

  2. 02 ARM11 时钟初始化后的跑马灯程序

    2019独角兽企业重金招聘Python工程师标准>>> .text .globl _start _start:ldr r0, =0x70000000orr r0, r0, #0x13 ...

  3. [国嵌攻略][038][时钟初始化]

    时钟脉冲信号 按一定的电压幅度,一定的时间间隔连续发出的脉冲信号叫做时钟脉冲信号.用于给处理器和其他硬件提供时钟度量. 时钟脉冲频率 在单位时间内产生的时钟脉冲的个数叫做时间脉冲频率 时钟源分类 1. ...

  4. bootloader功能介绍/时钟初始化设置/串口工作原理/内存工作原理/NandFlash工作原理...

    bootloader功能介绍 初始化开发板上主要硬件(时钟,内存,硬盘), 把操作系统从硬盘拷贝到内存,然后让cpu跳转到内存中执行操作系统. boot阶段 1.关闭影响CPU正常执行的外设 -关闭看 ...

  5. stm32时钟初始化过程浅析

    stm32时钟初始化过程浅析 (大致梳理了一下32启动过程中时钟的初始化过程) 加载main函数之前(启动代码中LDR R0, =__main之前),HCLK总线时钟默认上电是上一次断电前配置的频率 ...

  6. 时间子系统10_hpet时钟初始化

    // 时钟mult :mult/2^shift = ns/cyc // 参考:http://www.bluezd.info/archives/reg_clock_event_device_1 // x ...

  7. STM32_3 时钟初始化分析

    在startup文件中,调用了2个函数,一个是System_Init, 另一个是main. System_Init()在system_stm32f10x.c 这个文件中,先看一下时钟树,再分析一下这个 ...

  8. arm 跑马灯 linux,02 ARM11 时钟初始化后的跑马灯程序

    .text .globl _start _start: ldr r0, =0x70000000 orr r0, r0, #0x13 mcr p15, 0, r0, c15, c2, 4 ldr r0, ...

  9. uboot中系统时钟初始化函数:system_clock_init

    时钟初始化函数内容如下: /** system_clock_init: Initialize core clock and bus clock.* void system_clock_init(voi ...

最新文章

  1. python读取txt文件并写入excel-Python读取txt内容写入xls格式excel中的方法
  2. ansible-playbook组件解析及操作全解
  3. 深度学习之对象检测_深度学习时代您应该阅读的12篇文章,以了解对象检测
  4. 在论坛中出现的各种疑难问题:日志收缩问题
  5. POJ 2184 Cow Exhibition
  6. process_进程池
  7. Flume案例Ganglia监控
  8. HackerOne 发布《2021年黑客报告》:黑客的动力、发展和未来
  9. C++,Java,Pathy这几种编程语言的区别
  10. 7系列高速收发器简介 GTP IP核
  11. 《MFC 控件透明处理》
  12. 正交设计之正交四原则
  13. 《Software Testing》英文原著阅读 单词
  14. 究竟什么是嵌入式? 嵌入式开发是什么意思?
  15. ACM第三次比赛题目及标准程序(贪心)
  16. 这是属于你我平凡人的荣耀
  17. 微信分享多张图片到朋友圈的好操作
  18. ①Linux简明系统编程(嵌入式公众号的课)---总课时12h
  19. 万字篇:2020Android面经,历时一个半月,斩获3个大厂offer(京东、新浪、滴滴)
  20. 华硕K42J设置USB启动系统

热门文章

  1. POI - Excel 打印配置
  2. php - 解决百万级全站用户消息推送问题
  3. python解压rar
  4. SDNU_ACM_ICPC_2020_Winter_Practice_4th [Reproduced]
  5. huihoo和中国的开源,路在何方???
  6. 人工智能在电力系统中的应用值得思考的问题
  7. 房子,婚姻,名字,这三个关键词,你怎么看?
  8. fatfs文件系统详解之f_mount函数分析
  9. java根据经纬度转地址或者根据地址转经纬度
  10. 4、编写程序,根据用户输入的数字转换成相应的中文的大写数字。例如,1.23转换为“壹点贰叁”。