clock.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 682 行 · 第 1/2 页
C
682 行
/* * linux/arch/arm/mach-at91rm9200/clock.c * * Copyright (C) 2005 David Brownell * Copyright (C) 2005 Ivan Kokshaysky * * 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. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/debugfs.h>#include <linux/seq_file.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <linux/clk.h>#include <asm/semaphore.h>#include <asm/io.h>#include <asm/mach-types.h>#include <asm/arch/hardware.h>#include <asm/arch/board.h> /* for master clock global */#include "generic.h"#undef DEBUG/* * There's a lot more which can be done with clocks, including cpufreq * integration, slow clock mode support (for system suspend), letting * PLLB be used at other rates (on boards that don't need USB), etc. */struct clk { const char *name; unsigned long rate_hz; struct clk *parent; u32 pmc_mask; void (*mode)(struct clk *, int); unsigned id:2; /* PCK0..3, or 32k/main/a/b */ unsigned primary:1; unsigned pll:1; unsigned programmable:1; u16 users;};static spinlock_t clk_lock;static u32 at91_pllb_usb_init;/* * Four primary clock sources: two crystal oscillators (32K, main), and * two PLLs. PLLA usually runs the master clock; and PLLB must run at * 48 MHz (unless no USB function clocks are needed). The main clock and * both PLLs are turned off to run in "slow clock mode" (system suspend). */static struct clk clk32k = { .name = "clk32k", .rate_hz = AT91_SLOW_CLOCK, .users = 1, /* always on */ .id = 0, .primary = 1,};static struct clk main_clk = { .name = "main", .pmc_mask = 1 << 0, /* in PMC_SR */ .users = 1, .id = 1, .primary = 1,};static struct clk plla = { .name = "plla", .parent = &main_clk, .pmc_mask = 1 << 1, /* in PMC_SR */ .id = 2, .primary = 1, .pll = 1,};static void pllb_mode(struct clk *clk, int is_on){ u32 value; if (is_on) { is_on = AT91_PMC_LOCKB; value = at91_pllb_usb_init; } else value = 0; at91_sys_write(AT91_CKGR_PLLBR, value); do { cpu_relax(); } while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != is_on);}static struct clk pllb = { .name = "pllb", .parent = &main_clk, .pmc_mask = 1 << 2, /* in PMC_SR */ .mode = pllb_mode, .id = 3, .primary = 1, .pll = 1,};static void pmc_sys_mode(struct clk *clk, int is_on){ if (is_on) at91_sys_write(AT91_PMC_SCER, clk->pmc_mask); else at91_sys_write(AT91_PMC_SCDR, clk->pmc_mask);}/* USB function clocks (PLLB must be 48 MHz) */static struct clk udpck = { .name = "udpck", .parent = &pllb, .pmc_mask = AT91_PMC_UDP, .mode = pmc_sys_mode,};static struct clk uhpck = { .name = "uhpck", .parent = &pllb, .pmc_mask = AT91_PMC_UHP, .mode = pmc_sys_mode,};#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS/* * The four programmable clocks can be parented by any primary clock. * You must configure pin multiplexing to bring these signals out. */static struct clk pck0 = { .name = "pck0", .pmc_mask = AT91_PMC_PCK0, .mode = pmc_sys_mode, .programmable = 1, .id = 0,};static struct clk pck1 = { .name = "pck1", .pmc_mask = AT91_PMC_PCK1, .mode = pmc_sys_mode, .programmable = 1, .id = 1,};static struct clk pck2 = { .name = "pck2", .pmc_mask = AT91_PMC_PCK2, .mode = pmc_sys_mode, .programmable = 1, .id = 2,};static struct clk pck3 = { .name = "pck3", .pmc_mask = AT91_PMC_PCK3, .mode = pmc_sys_mode, .programmable = 1, .id = 3,};#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS *//* * The master clock is divided from the CPU clock (by 1-4). It's used for * memory, interfaces to on-chip peripherals, the AIC, and sometimes more * (e.g baud rate generation). It's sourced from one of the primary clocks. */static struct clk mck = { .name = "mck", .pmc_mask = 1 << 3, /* in PMC_SR */ .users = 1, /* (must be) always on */};static void pmc_periph_mode(struct clk *clk, int is_on){ if (is_on) at91_sys_write(AT91_PMC_PCER, clk->pmc_mask); else at91_sys_write(AT91_PMC_PCDR, clk->pmc_mask);}static struct clk udc_clk = { .name = "udc_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_UDP, .mode = pmc_periph_mode,};static struct clk ohci_clk = { .name = "ohci_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_UHP, .mode = pmc_periph_mode,};static struct clk ether_clk = { .name = "ether_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_EMAC, .mode = pmc_periph_mode,};static struct clk mmc_clk = { .name = "mci_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_MCI, .mode = pmc_periph_mode,};static struct clk twi_clk = { .name = "twi_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_TWI, .mode = pmc_periph_mode,};static struct clk usart0_clk = { .name = "usart0_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_US0, .mode = pmc_periph_mode,};static struct clk usart1_clk = { .name = "usart1_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_US1, .mode = pmc_periph_mode,};static struct clk usart2_clk = { .name = "usart2_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_US2, .mode = pmc_periph_mode,};static struct clk usart3_clk = { .name = "usart3_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_US3, .mode = pmc_periph_mode,};static struct clk spi_clk = { .name = "spi0_clk", .parent = &mck, .pmc_mask = 1 << AT91_ID_SPI, .mode = pmc_periph_mode,};static struct clk *const clock_list[] = { /* four primary clocks -- MUST BE FIRST! */ &clk32k, &main_clk, &plla, &pllb, /* PLLB children (USB) */ &udpck, &uhpck,#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS /* programmable clocks */ &pck0, &pck1, &pck2, &pck3,#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */ /* MCK and peripherals */ &mck, &usart0_clk, &usart1_clk, &usart2_clk, &usart3_clk, &mmc_clk, &udc_clk, &twi_clk, &spi_clk, // ssc0..ssc2 // tc0..tc5 &ohci_clk, ðer_clk,};/* clocks are all static for now; no refcounting necessary */struct clk *clk_get(struct device *dev, const char *id){ int i; for (i = 0; i < ARRAY_SIZE(clock_list); i++) { if (strcmp(id, clock_list[i]->name) == 0) return clock_list[i]; } return ERR_PTR(-ENOENT);}EXPORT_SYMBOL(clk_get);void clk_put(struct clk *clk){}EXPORT_SYMBOL(clk_put);static void __clk_enable(struct clk *clk){ if (clk->parent) __clk_enable(clk->parent); if (clk->users++ == 0 && clk->mode) clk->mode(clk, 1);}int clk_enable(struct clk *clk){ unsigned long flags; spin_lock_irqsave(&clk_lock, flags); __clk_enable(clk); spin_unlock_irqrestore(&clk_lock, flags); return 0;}EXPORT_SYMBOL(clk_enable);static void __clk_disable(struct clk *clk){ BUG_ON(clk->users == 0); if (--clk->users == 0 && clk->mode) clk->mode(clk, 0); if (clk->parent) __clk_disable(clk->parent);}void clk_disable(struct clk *clk){ unsigned long flags; spin_lock_irqsave(&clk_lock, flags); __clk_disable(clk);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?