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,	&ether_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 + -
显示快捷键?