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