Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145405
  • 博文数量: 89
  • 博客积分: 71
  • 博客等级: 民兵
  • 技术积分: 490
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-19 19:39
文章分类
文章存档

2013年(3)

2012年(86)

分类:

2012-11-28 17:05:57

s3c6410中有关时钟部份的代码架构:

1.  触摸屏中有关时钟代码:
        static struct clk    *adc_clock;
    adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
        printk(KERN_ERR "failed to get adc clock source\n");
        return -ENOENT;
    }
    clk_enable(adc_clock);
    
        ... ...
        clk_disable(adc_clock);
        
        ... ...
        if (adc_clock) {
        clk_disable(adc_clock);
        clk_put(adc_clock);
        adc_clock = NULL;
    }        


2.
static struct clk    *adc_clock;

struct clk {
    struct list_head      list;   ===> 用于加入链表:static LIST_HEAD(clocks)
    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);
};

extern struct clk s3c24xx_dclk0;
extern struct clk s3c24xx_dclk1;
extern struct clk s3c24xx_clkout0;
extern struct clk s3c24xx_clkout1;
extern struct clk s3c24xx_uclk;

extern struct clk clk_usb_bus;

extern struct clk clk_f;
extern struct clk clk_h;
extern struct clk clk_p;
extern struct clk clk_mpll;
extern struct clk clk_upll;
extern struct clk clk_xtal;


*******************************************
                s3c24xx_register_clock()
*******************************************
static LIST_HEAD(clocks);
DEFINE_MUTEX(clocks_mutex);


int s3c24xx_register_clock(struct clk *clk)
{
    clk->owner = THIS_MODULE;

        ... ...    
    mutex_lock(&clocks_mutex);
    list_add(&clk->list, &clocks);
    mutex_unlock(&clocks_mutex);

    ... ...
}

*************************************************  
            s3c6410时钟函数的调用流程
*************************************************  
时钟调用流程:
smdk6410_map_io()==>s3c24xx_init_clocks()==>(cpu->init_clocks)(xtal);==>arch/arm/plat-s3c24xx/cpu.c:
cpu_ids[]: s3c6400_init_clocks()
    ==>arch/arm/plat-s3c24xx/clock.c: s3c24xx_setup_clocks()

s3c6400_map_io()调用流程:
setup_arch()==>paging_init()==>devicemaps_init()==>mdesc=setup_machine() + mdesc->map_io();
        ==smdk6410_map_io()
        MACHINE_START(SMDK6410, "SMDK6410")==>s3c24xx_init_io()
        ==>cpu = s3c_lookup_cpu() + (cpu->map_io)(...); == s3c6400_map_io()
        ==>完成后,转入调smdk6410_map_io()里面的s3c24xx_init_io()后一个函数即:s3c24xx_init_clocks(0);
--------------------
/* initalise all the clocks */

传入参数:
xtal值的来历:
#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_##_type    \
 __attribute_used__                    \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr        = MACH_TYPE_##_type,        \
    .name        = _name,

#define MACHINE_END                \
};

MACHINE_START(SMDK6410, "SMDK6410")
    ... ...
    .map_io     = smdk6410_map_io,
        ... ...
MACHINE_END

void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
        ... ...
    devicemaps_init(mdesc);
        ... ...
}

static void __init devicemaps_init(struct machine_desc *mdesc)
{
        ... ...    
    if (mdesc->map_io)    ===>   smdk6410_map_io()
        mdesc->map_io();
        ... ...
}

    
void __init setup_arch(char **cmdline_p)
{
        ... ...
    struct machine_desc *mdesc;
        ... ...
        
    mdesc = setup_machine(machine_arch_type);
    ...
    paging_init(&meminfo, mdesc);
    ... ...
}

static void __init smdk6410_map_io(void)
{
    ... ...
    s3c24xx_init_clocks(0);
    ... ...
}

static struct cpu_table cpu_ids[] __initdata = {
        ... ...
    {
        .idcode        = 0xAAAABBBB,
        .idmask        = 0xffffffff,
        .map_io        = s3c6400_map_io,
        .init_clocks    = s3c6400_init_clocks,
        .init_uarts    = s3c6400_init_uarts,
        .init        = s3c6400_init,
        .name        = name_s3c6400
    },
    {
        .idcode        = 0xAAAABBBB,
        .idmask        = 0xffffffff,
        .map_io        = s3c6400_map_io,
        .init_clocks    = s3c6400_init_clocks,
        .init_uarts    = s3c6400_init_uarts,
        .init        = s3c6400_init,
        .name        = name_s3c6410
    },
};

static struct cpu_table *
s3c_lookup_cpu(unsigned long idcode)
{
    struct cpu_table *tab;
    int count;

    tab = cpu_ids;
    for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) {
        if ((idcode & tab->idmask) == tab->idcode)
            return tab;
    }

    return NULL;
}

void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
{
        ... ...
    idcode = 0xAAAABBBB;

    cpu = s3c_lookup_cpu(idcode);
    ... ...
}

void __init s3c24xx_init_clocks(int xtal)
{
    if (xtal == 0)
        xtal = 12*1000*1000;

        ... ...

    if (cpu->init_clocks == NULL)
        panic("s3c24xx_init_clocks: cpu has no clock init\n");
    else
        (cpu->init_clocks)(xtal);   ===> s3c6400_init_clocks()
}

static void __init smdk6410_map_io(void)
{
    s3c24xx_init_io(smdk6410_iodesc, ARRAY_SIZE(smdk6410_iodesc));
    s3c24xx_init_clocks(0);  ===> s3c6400_init_clocks()
    ... ...
}

*************************************************  
            s3c6410主时钟函数的初始化主函数
*************************************************  
void __init s3c6400_init_clocks(int xtal)
{
    unsigned long clkdiv0;
    unsigned long fclk, arm_clk;
    unsigned long hclkx2;
    unsigned long hclk;
    unsigned long pclk;
    unsigned long epll_clk;

    struct clk *clkp;
    int ret;
    int ptr;

    clk_xtal.rate = xtal;  <===初始化于s3c24xx_init_clocks(0): 12MHz

    fclk = s3c6400_get_pll(__raw_readl(S3C_APLL_CON), xtal);
    clkdiv0 = __raw_readl(S3C_CLK_DIV0);

        arm_clk = fclk/((clkdiv0 & S3C_CLKDIVN_APLL_MASK)+1);

    if(!(__raw_readl(S3C_OTHERS) & 0x80)) {
        fclk = s3c6400_get_pll(__raw_readl(S3C_MPLL_CON), xtal);
    }

        hclkx2 = fclk / (((clkdiv0 & S3C_CLKDIVN_HCLKX2_MASK)>>9)+1);

    hclk = hclkx2 / (((clkdiv0 & S3C_CLKDIVN_HCLK_MASK)>>8)+1);

        //it should get the divisor from register and then sets the pclk properly
        pclk = hclkx2 / (((clkdiv0 & S3C_CLKDIVN_PCLK_MASK)>>12)+1);
        /* print brief summary of clocks, etc */

        printk("S3C6400: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
               print_mhz(arm_clk), print_mhz(hclk),
               print_mhz(pclk));

    /* Default EPLL frequency : 192Mhz */
    writel(S3C_EPLL_EN|S3C_EPLLVAL(32,1,1), S3C_EPLL_CON0);
    writel(0, S3C_EPLL_CON1);
    mdelay(50);

    epll_clk = s3c6400_get_epll(xtal);
    clk_epll.rate = epll_clk;
    printk("S3C6400: EPLL %ld.%03ld MHz\n", print_mhz(epll_clk));

    clk_hx2.rate = hclkx2;

        /* initialise the clocks here, to allow other things like the
         * console to use them, and to add new ones after the initialisation
         */

        s3c24xx_setup_clocks(xtal, arm_clk, hclk, pclk);

    for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
        clkp = clks[ptr];

        ret = s3c24xx_register_clock(clkp);
        if (ret < 0) {
            printk(KERN_ERR "Failed to register clock %s (%d)\n",
                   clkp->name, ret);
        }
    }

    /* register clocks from clock array */
    clkp = init_clocks;
    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);
        }

        /* set clock rate to use in drivers */
        if (!clkp->rate) {
            if (clkp->parent) {
                clkp->rate = clkp->parent->rate;
            }
        }

    }

#ifdef CONFIG_USB_OHCI_HCD
    if (S3C_USB_CLKSRC_EPLL == 1) {
            .... 条件不满足
    } else {  /* 48MHz */
        writel(readl(S3C_CLK_SRC)& ~S3C_CLKSRC_UHOST_MASK, S3C_CLK_SRC);
        writel(readl(S3C_OTHERS)|S3C_OTHERS_USB_SIG_MASK, S3C_OTHERS);

        writel(0x0, S3C_USBOTG_PHYPWR);
        writel(0x22, S3C_USBOTG_PHYCLK);
        writel(0x1, S3C_USBOTG_RSTCON);
        udelay(50);
        writel(0x0, S3C_USBOTG_RSTCON);
        udelay(50);

        /* USB host colock divider ratio is 1 */
        writel(readl(S3C_CLK_DIV1)& ~S3C_CLKDIVN_UHOST_MASK, S3C_CLK_DIV1);
    }

    writel(readl(S3C_HCLK_GATE)|S3C_CLKCON_HCLK_UHOST|S3C_CLKCON_HCLK_SECUR,
        S3C_HCLK_GATE);
    writel(readl(S3C_SCLK_GATE)|S3C_CLKCON_SCLK_UHOST, S3C_SCLK_GATE);

#endif


    /* We must be careful disabling the clocks we are not intending to
     * be using at boot time, as subsytems such as the LCD which do
     * their own DMA requests to the bus can cause the system to lockup
     * if they where in the middle of requesting bus access.
     *
     * 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.
    */

    /* install (and disable) the clocks we do not need immediately */

    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);
        }

        (clkp->enable)(clkp, 0);
    }
}
    
--------------
PLL函数:
static inline unsigned int
s3c6400_get_pll(unsigned long  pllval, unsigned long baseclk)
{
    unsigned long  mdiv, pdiv, sdiv;

    /* To prevent overflow in calculation -JaeCheol Lee */
    baseclk /= 1000;

    mdiv = (pllval & (0x3ff << 16))>>16;
    pdiv = (pllval & (0x3f << 8))>>8;
    sdiv = (pllval & (0x03 << 0))>>0;
    return (baseclk * (mdiv)) / ((pdiv) << sdiv)*1000;
}

static inline unsigned int
s3c6400_get_epll(unsigned long baseclk)
{
    unsigned long  pllval, mdiv, pdiv, sdiv, kdiv;

    /* To prevent overflow in calculation -JaeCheol Lee */
    baseclk /= 1000;

    pllval = readl(S3C_EPLL_CON0);
    mdiv = (pllval & (0x3ff << 16))>>16;
    pdiv = (pllval & (0x3f << 8))>>8;
    sdiv = (pllval & (0x03 << 0))>>0;
    kdiv = readl(S3C_EPLL_CON1) & (0xffff);

    return (baseclk * (mdiv+kdiv/65536) / (pdiv << sdiv))*1000;
}        

int __init s3c24xx_setup_clocks(unsigned long xtal,
                unsigned long fclk,
                unsigned long hclk,
                unsigned long pclk)
{
        ... ...
    /* initialise the main system clocks */

    clk_xtal.rate = xtal;
    clk_mpll.rate = fclk;
    clk_h.rate = hclk;
    clk_p.rate = pclk;
    clk_f.rate = fclk;

    /* 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_mpll) < 0)
        printk(KERN_ERR "failed to register mpll 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");

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)
    if (s3c24xx_register_clock(&clk_epll) < 0)
        printk(KERN_ERR "failed to register epll clock\n");
    
    /* register hclkx2  */
    if (s3c24xx_register_clock(&clk_hx2) < 0)
        printk(KERN_ERR "failed to register cpu hclkx2\n");

    if (s3c24xx_register_clock(&clk_s) < 0)
        printk(KERN_ERR "failed to register cpu pclk\n");

    if (s3c24xx_register_clock(&clk_u) < 0)
        printk(KERN_ERR "failed to register cpu uclk\n");

    if (s3c24xx_register_clock(&clk_48m) < 0)
        printk(KERN_ERR "failed to register cpu uclk\n");

    if (s3c24xx_register_clock(&clk_27m) < 0)
        printk(KERN_ERR "failed to register cpu uclk\n");
#endif

    return 0;
}

/* base clocks */

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

struct clk clk_mpll = {
    .name        = "mpll",
    .id        = -1,
};

... ...
#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)
struct clk clk_epll = {
    .name        = "epll",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};
#endif

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

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

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

... ...
#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)
struct clk clk_hx2 = {
    .name        = "hclkx2",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

struct clk clk_s = {
    .name        = "sclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

struct clk clk_u = {
    .name        = "uclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

struct clk clk_48m = {
    .name        = "clk48m",
    .id        = -1,
    .rate        = 48*1000*1000,
    .parent        = NULL,
    .ctrlbit    = 0,
};

struct clk clk_27m = {
    .name        = "clk27m",
    .id        = -1,
    .rate        = 27*1000*1000,
    .parent        = NULL,
    .ctrlbit    = 0,
};

#endif

----------------
/* clocks to add straight away */
static struct clk *clks[] __initdata = {
    &clk_epll_uart_192m,
    &clk_mpll_uart,
    &clk_hsmmc_DOUTmpll_mmc0,
    &clk_hsmmc_DOUTmpll_mmc1,
    &clk_hsmmc_DOUTmpll_mmc2,
};

/* standard clock definitions */
static struct clk init_clocks[] = {
        /* AHB devices */
    { .name    = "dma 1",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_DMA1,
    },
    { .name    = "dma 0",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_DMA0,
    },
    { .name    = "lcd",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_LCD,
    },
    { .name    = "TZIC",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_TZIC,
    },
    { .name    = "INTC",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_INTC,
    },
    { .name    = "camif",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_CAMIF
    },
    { .name    = "otg",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_USB
    },
    { .name    = "hsmmc0",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .get_rate = s3c6400_clk_getrate,
      .ctrlbit = S3C_CLKCON_HCLK_HSMMC0
    },
    { .name    = "hsmmc1",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .get_rate = s3c6400_clk_getrate,
      .ctrlbit = S3C_CLKCON_HCLK_HSMMC1
    },
    { .name    = "hsmmc2",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .get_rate = s3c6400_clk_getrate,
      .ctrlbit = S3C_CLKCON_HCLK_HSMMC2
    },
    { .name    = "hclk_mfc",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_MFC
    },
    { .name    = "hclk_post0",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_POST0
    },
    { .name    = "hclk_jpeg",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_JPEG
    },
    { .name    = "tv_encoder",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_TV
    },
    { .name    = "tv_scaler",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_SCALER
    },
    /* register to use HS-MMC clock */
    { .name    = "sclk_48m",
      .id       = -1,
      .parent  = &clk_48m,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = 1<<16,    /* USB SIG Mask */
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "sclk_48m_mmc0",
      .id       = -1,
      .parent  = &clk_s,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_SCLK_MMC0_48,
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "sclk_48m_mmc1",
      .id       = -1,
      .parent  = &clk_s,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_SCLK_MMC1_48,
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "sclk_48m_mmc2",
      .id       = -1,
      .parent  = &clk_s,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_SCLK_MMC2_48,
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "sclk_mfc",
      .id       = -1,
      .parent  = &clk_s,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_SCLK_MFC,
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "sclk_jpeg",
      .id       = -1,
      .parent  = &clk_s,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_SCLK_JPEG,
      .usage   = 0,
      .rate    = 48*1000*1000,
    },
    { .name    = "cfata",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_IHOST
    },

    /* APB Devices */
    { .name    = "RTC",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_RTC,
    },
    { .name    = "GPIO",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_GPIO,
    },
    { .name    = "UART2",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_UART2,
    },
    { .name    = "UART1",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_UART1,
    },
    { .name    = "UART0",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_UART0,
    },
    { .name    = "timers",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_PWM,
    },
    { .name    = "watchdog",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_WDT,
    },
    { .name    = "i2c",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_IIC,
    },
    { .name    = "spi0",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_SPI0,
    },
    { .name    = "spi1",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_SPI1,
    },
    { .name    = "usb-host",
      .id       = -1,
      .parent  = &clk_h,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_HCLK_UHOST,
    },
    { .name    = "adc",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_TSADC,
    },
    { .name    = "pclk_mfc",
      .id       = -1,
      .parent  = &clk_p,
      .enable  = s3c_clkcon_enable,
      .ctrlbit = S3C_CLKCON_PCLK_MFC,
    },
    {
      .name     = "camera",
      .id        = -1,
      .parent   = &clk_hx2,
      .enable   = s3c_clkcon_enable,
      .ctrlbit  = S3C_CLKCON_SCLK_CAM,
      .set_rate = s3c6410_setrate_camera,
    },
};

-----------------
static struct clk init_clocks_disable[] = {
    {
        .name        = "nand",
        .id        = -1,
        .parent        = &clk_h,
        .enable        = s3c_clkcon_enable,
        //.ctrlbit    = S3C_CLKCON_HCLK_NAND,
    }, {
        .name        = "adc",
        .id        = -1,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_TSADC,
    }, {
        .name        = "i2c",
        .id        = -1,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_IIC,
    }, {
        .name        = "iis",
        .id        = 0,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_IIS0,
    }, {
        .name        = "iis",
        .id        = 1,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_IIS1,
    }, {
        .name        = "spi",
        .id        = 0,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_SPI0,
    }, {
        .name        = "spi",
        .id        = 1,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_SPI1,
    }
};

***********************************
            clock API
***********************************
struct clk *clk_get(struct device *dev, const char *id)
{
    ... ...
    idno = to_platform_device(dev)->id;

    mutex_lock(&clocks_mutex);

    list_for_each_entry(p, &clocks, list) {
        if (p->id == idno &&
            strcmp(id, p->name) == 0 &&
            try_module_get(p->owner)) {
            clk = p;
            break;
        }
    }

        ... ...
    mutex_unlock(&clocks_mutex);
    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);

    mutex_lock(&clocks_mutex);

    if ((clk->usage++) == 0)
        (clk->enable)(clk, 1);

    mutex_unlock(&clocks_mutex);
    return 0;
}

void clk_disable(struct clk *clk)
{
    if (IS_ERR(clk) || clk == NULL)
        return;

    mutex_lock(&clocks_mutex);

    if ((--clk->usage) == 0)
        (clk->enable)(clk, 0);

    mutex_unlock(&clocks_mutex);
    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;
}


int clk_set_rate(struct clk *clk, unsigned long rate)
{
    int ret;

    if (IS_ERR(clk))
        return -EINVAL;

    mutex_lock(&clocks_mutex);
    ret = (clk->set_rate)(clk, rate);
    mutex_unlock(&clocks_mutex);

    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;

    mutex_lock(&clocks_mutex);

    if (clk->set_parent)
        ret = (clk->set_parent)(clk, parent);

    mutex_unlock(&clocks_mutex);

    return ret;
}

******************************************
        时钟例子
******************************************
drivers/input/touchscreen/s3c-ts.c

********************************************
            s3c6410 touchscreen中的取时钟,使能时钟的时钟例子
********************************************
static struct clk    *adc_clock;
... ...
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
    printk(KERN_ERR "failed to get adc clock source\n");
    return -ENOENT;
}
clk_enable(adc_clock);

------------------
adc时钟初始化地方:
struct clk clk_p = {
    .name        = "pclk",
    .id        = -1,
    .rate        = 0,
    .parent        = NULL,
    .ctrlbit    = 0,
};

void __init s3c6400_init_clocks(int xtal)
{
    /* Changed below lines to support s3c6400 - JaeCheol Lee */
    fclk = s3c6400_get_pll(__raw_readl(S3C_APLL_CON), xtal);
    clkdiv0 = __raw_readl(S3C_CLK_DIV0);

        ... ...
        hclkx2 = fclk / (((clkdiv0 & S3C_CLKDIVN_HCLKX2_MASK)>>9)+1);

        pclk = hclkx2 / (((clkdiv0 & S3C_CLKDIVN_PCLK_MASK)>>12)+1);

        printk("S3C6400: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
               print_mhz(arm_clk), print_mhz(hclk),
               print_mhz(pclk));

        ... ...
        s3c24xx_setup_clocks(xtal, arm_clk, hclk, pclk);
        ... ...
}

int __init s3c24xx_setup_clocks(unsigned long xtal,
                unsigned long fclk,
                unsigned long hclk,
                unsigned long pclk)
{
        ... ...
    clk_p.rate = pclk;
    
        ... ...
    if (s3c24xx_register_clock(&clk_p) < 0)
        printk(KERN_ERR "failed to register cpu pclk\n");

        ... ...
}

#define S3C_CLKCON_PCLK_TSADC    (1<<12)    
static struct clk init_clocks_disable[] = {
    ... ...
    
     {
        .name        = "adc",
        .id        = -1,
        .parent        = &clk_p,
        .enable        = s3c_clkcon_enable,
        .ctrlbit    = S3C_CLKCON_PCLK_TSADC,
    },
        ... ...
};

int s3c_clkcon_enable (struct clk *clk, int enable)
{
    unsigned long gate_reg;

    gate_reg = clk_get_gate_reg(clk);
    s3c_clk_enable(clk->ctrlbit, enable, gate_reg);//1<<12, 0, pclk
    return 0;
}

struct clk *clk_get_parent(struct clk *clk)
{
    return clk->parent;
}

#ifndef __ASSEMBLY__

#ifdef __CHECKER__
# define __user        __attribute__((noderef, address_space(1)))
# define __kernel    /* default address space */
# define __force    __attribute__((force))
# define __iomem    __attribute__((noderef, address_space(2)))
... ...
#else
# define __user
# define __kernel
# define __force
# define __iomem
... ...
#endif
... ...
#endif

#ifndef __ASSEMBLY__
#define S3C2410_ADDR(x)      ((void __iomem __force *)0xF0000000 + (x))  ==>3GB以上的空间
#else
#define S3C2410_ADDR(x)      (0xF0000000 + (x))
#endif

#define S3C24XX_VA_SYSCON    S3C2410_ADDR(0x02900000)
#define S3C_CLKREG(x)                ((x) + S3C24XX_VA_SYSCON)

//See P92
#define S3C_HCLK_GATE        S3C_CLKREG(0x30)   //0x7e00_0030
#define S3C_PCLK_GATE               S3C_CLKREG(0x34)   //0x7e00_0034
#define S3C_SCLK_GATE        S3C_CLKREG(0x38)   //0x7e00_0038
#define S3C_OTHERS               S3C_CLKREG(0x900) //0x7e00_f900

/*This function returns the virtual address of gating register*/
ulong clk_get_gate_reg (struct clk *clk)
{
    struct clk *parent_clk = clk_get_parent(clk) ;

    if (strcmp(parent_clk->name, "hclk") == 0)
        return (unsigned long) S3C_HCLK_GATE;
    else if (strcmp(parent_clk->name, "pclk") == 0)    ===>adc 的父时钟
        return (unsigned long) S3C_PCLK_GATE;
    else if (strcmp(parent_clk->name, "clk48m") == 0)
        return (unsigned long) S3C_OTHERS;
    else
        return (unsigned long) S3C_SCLK_GATE;
}

#ifdef __CHECKER__
... ...
extern void __chk_io_ptr(const void __iomem *);
... ...
#else
... ...
# define __chk_io_ptr(x) (void)0
... ...
#endif

#define __raw_readl(a)        (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a))

//clocks:1<<12, enable:0, gate_reg:S3C_PCLK_GATE
void inline s3c_clk_enable (uint clocks, uint enable, ulong gate_reg)
{
    unsigned long clkcon;
    unsigned long flags;

    local_irq_save(flags);

    clkcon = __raw_readl(gate_reg);
    clkcon &= ~clocks;

    if (enable)
        clkcon |= clocks;

    __raw_writel(clkcon, gate_reg);

    local_irq_restore(flags);
}


*************************************************  
            s3c6410时钟函数的调用流程
*************************************************  
时钟调用流程:
smdk6410_map_io()==>s3c24xx_init_clocks()==>(cpu->init_clocks)(xtal);==>arch/arm/plat-s3c24xx/cpu.c:
cpu_ids[]: s3c6400_init_clocks()
    ==>arch/arm/plat-s3c24xx/clock.c: s3c24xx_setup_clocks()

s3c6400_map_io()调用流程:
setup_arch()==>paging_init()==>devicemaps_init()==>mdesc=setup_machine() + mdesc->map_io();
        ==smdk6410_map_io()
        MACHINE_START(SMDK6410, "SMDK6410")==>s3c24xx_init_io()
        ==>cpu = s3c_lookup_cpu() + (cpu->map_io)(...); == s3c6400_map_io()
        ==>完成后,转入调smdk6410_map_io()里面的s3c24xx_init_io()后一个函数即:s3c24xx_init_clocks(0);

--------------------
void __init s3c6400_init_clocks(int xtal)
{
        ... ...
    /* We must be careful disabling the clocks we are not intending to
     * be using at boot time, as subsytems such as the LCD which do
     * their own DMA requests to the bus can cause the system to lockup
     * if they where in the middle of requesting bus access.
     *
     * 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.
    */

    /* install (and disable) the clocks we do not need immediately */

    clkp = init_clocks_disable;
    for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {

        ret = s3c24xx_register_clock(clkp);
        ... ...
        (clkp->enable)(clkp, 0);
    }
}

----------
clk_get()拿到adc时钟:

//id: device name such as "adc"
struct clk *clk_get(struct device *dev, const char *id)
{
    struct clk *p;
    struct clk *clk = ERR_PTR(-ENOENT);
    int idno;

       //device not  on platform bus
    if (dev == NULL || dev->bus != &platform_bus_type)
        idno = -1;
    else
        idno = to_platform_device(dev)->id;

    mutex_lock(&clocks_mutex);

    list_for_each_entry(p, &clocks, list) {
        if (p->id == idno &&            /*clk id == platform device id*/
            strcmp(id, p->name) == 0 &&     /*dev name==clk{}'s name*/
            try_module_get(p->owner)) {
            clk = p;
            break;
        }
    }


        ... ...
    mutex_unlock(&clocks_mutex);
    return clk;
}

*****************************
int clk_enable(struct clk *clk)
{
    if (IS_ERR(clk) || clk == NULL)
        return -EINVAL;

    clk_enable(clk->parent);

    mutex_lock(&clocks_mutex);

    if ((clk->usage++) == 0)
        (clk->enable)(clk, 1);

    mutex_unlock(&clocks_mutex);
    return 0;
}
阅读(914) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~