⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clock.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * arch/arm/mach-pnx4008/clock.c * * Clock control driver for PNX4008 * * Authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com> * Generic clock management functions are partially based on: *  linux/arch/arm/mach-omap/clock.c * * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/device.h>#include <linux/err.h>#include <linux/delay.h>#include <asm/semaphore.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/arch/clock.h>#include "clock.h"/*forward declaration*/static struct clk per_ck;static struct clk hclk_ck;static struct clk ck_1MHz;static struct clk ck_13MHz;static struct clk ck_pll1;static int local_set_rate(struct clk *clk, u32 rate);static inline void clock_lock(void){	local_irq_disable();}static inline void clock_unlock(void){	local_irq_enable();}static void propagate_rate(struct clk *clk){	struct clk *tmp_clk;	tmp_clk = clk;	while (tmp_clk->propagate_next) {		tmp_clk = tmp_clk->propagate_next;		local_set_rate(tmp_clk, tmp_clk->user_rate);	}}static inline void clk_reg_disable(struct clk *clk){	if (clk->enable_reg)		__raw_writel(__raw_readl(clk->enable_reg) &			     ~(1 << clk->enable_shift), clk->enable_reg);}static inline void clk_reg_enable(struct clk *clk){	if (clk->enable_reg)		__raw_writel(__raw_readl(clk->enable_reg) |			     (1 << clk->enable_shift), clk->enable_reg);}static inline void clk_reg_disable1(struct clk *clk){	if (clk->enable_reg1)		__raw_writel(__raw_readl(clk->enable_reg1) &			     ~(1 << clk->enable_shift1), clk->enable_reg1);}static inline void clk_reg_enable1(struct clk *clk){	if (clk->enable_reg1)		__raw_writel(__raw_readl(clk->enable_reg1) |			     (1 << clk->enable_shift1), clk->enable_reg1);}static int clk_wait_for_pll_lock(struct clk *clk){	int i;	i = 0;	while (i++ < 0xFFF && !(__raw_readl(clk->scale_reg) & 1)) ;	/*wait for PLL to lock */	if (!(__raw_readl(clk->scale_reg) & 1)) {		printk(KERN_ERR		       "%s ERROR: failed to lock, scale reg data: %x\n",		       clk->name, __raw_readl(clk->scale_reg));		return -1;	}	return 0;}static int switch_to_dirty_13mhz(struct clk *clk){	int i;	int ret;	u32 tmp_reg;	ret = 0;	if (!clk->rate)		clk_reg_enable1(clk);	tmp_reg = __raw_readl(clk->parent_switch_reg);	/*if 13Mhz clock selected, select 13'MHz (dirty) source from OSC */	if (!(tmp_reg & 1)) {		tmp_reg |= (1 << 1);	/* Trigger switch to 13'MHz (dirty) clock */		__raw_writel(tmp_reg, clk->parent_switch_reg);		i = 0;		while (i++ < 0xFFF && !(__raw_readl(clk->parent_switch_reg) & 1)) ;	/*wait for 13'MHz selection status */		if (!(__raw_readl(clk->parent_switch_reg) & 1)) {			printk(KERN_ERR			       "%s ERROR: failed to select 13'MHz, parent sw reg data: %x\n",			       clk->name, __raw_readl(clk->parent_switch_reg));			ret = -1;		}	}	if (!clk->rate)		clk_reg_disable1(clk);	return ret;}static int switch_to_clean_13mhz(struct clk *clk){	int i;	int ret;	u32 tmp_reg;	ret = 0;	if (!clk->rate)		clk_reg_enable1(clk);	tmp_reg = __raw_readl(clk->parent_switch_reg);	/*if 13'Mhz clock selected, select 13MHz (clean) source from OSC */	if (tmp_reg & 1) {		tmp_reg &= ~(1 << 1);	/* Trigger switch to 13MHz (clean) clock */		__raw_writel(tmp_reg, clk->parent_switch_reg);		i = 0;		while (i++ < 0xFFF && (__raw_readl(clk->parent_switch_reg) & 1)) ;	/*wait for 13MHz selection status */		if (__raw_readl(clk->parent_switch_reg) & 1) {			printk(KERN_ERR			       "%s ERROR: failed to select 13MHz, parent sw reg data: %x\n",			       clk->name, __raw_readl(clk->parent_switch_reg));			ret = -1;		}	}	if (!clk->rate)		clk_reg_disable1(clk);	return ret;}static int set_13MHz_parent(struct clk *clk, struct clk *parent){	int ret = -EINVAL;	if (parent == &ck_13MHz)		ret = switch_to_clean_13mhz(clk);	else if (parent == &ck_pll1)		ret = switch_to_dirty_13mhz(clk);	return ret;}#define PLL160_MIN_FCCO 156000#define PLL160_MAX_FCCO 320000/* * Calculate pll160 settings. * Possible input: up to 320MHz with step of clk->parent->rate. * In PNX4008 parent rate for pll160s may be either 1 or 13MHz. * Ignored paths: "feedback" (bit 13 set), "div-by-N". * Setting ARM PLL4 rate to 0 will put CPU into direct run mode. * Setting PLL5 and PLL3 rate to 0 will disable USB and DSP clock input. * Please refer to PNX4008 IC manual for details. */static int pll160_set_rate(struct clk *clk, u32 rate){	u32 tmp_reg, tmp_m, tmp_2p, i;	u32 parent_rate;	int ret = -EINVAL;	parent_rate = clk->parent->rate;	if (!parent_rate)		goto out;	/* set direct run for ARM or disable output for others  */	clk_reg_disable(clk);	/* disable source input as well (ignored for ARM) */	clk_reg_disable1(clk);	tmp_reg = __raw_readl(clk->scale_reg);	tmp_reg &= ~0x1ffff;	/*clear all settings, power down */	__raw_writel(tmp_reg, clk->scale_reg);	rate -= rate % parent_rate;	/*round down the input */	if (rate > PLL160_MAX_FCCO)		rate = PLL160_MAX_FCCO;	if (!rate) {		clk->rate = 0;		ret = 0;		goto out;	}	clk_reg_enable1(clk);	tmp_reg = __raw_readl(clk->scale_reg);	if (rate == parent_rate) {		/*enter direct bypass mode */		tmp_reg |= ((1 << 14) | (1 << 15));		__raw_writel(tmp_reg, clk->scale_reg);		clk->rate = parent_rate;		clk_reg_enable(clk);		ret = 0;		goto out;	}	i = 0;	for (tmp_2p = 1; tmp_2p < 16; tmp_2p <<= 1) {		if (rate * tmp_2p >= PLL160_MIN_FCCO)			break;		i++;	}	if (tmp_2p > 1)		tmp_reg |= ((i - 1) << 11);	else		tmp_reg |= (1 << 14);	/*direct mode, no divide */	tmp_m = rate * tmp_2p;	tmp_m /= parent_rate;	tmp_reg |= (tmp_m - 1) << 1;	/*calculate M */	tmp_reg |= (1 << 16);	/*power up PLL */	__raw_writel(tmp_reg, clk->scale_reg);	if (clk_wait_for_pll_lock(clk) < 0) {		clk_reg_disable(clk);		clk_reg_disable1(clk);		tmp_reg = __raw_readl(clk->scale_reg);		tmp_reg &= ~0x1ffff;	/*clear all settings, power down */		__raw_writel(tmp_reg, clk->scale_reg);		clk->rate = 0;		ret = -EFAULT;		goto out;	}	clk->rate = (tmp_m * parent_rate) / tmp_2p;	if (clk->flags & RATE_PROPAGATES)		propagate_rate(clk);	clk_reg_enable(clk);	ret = 0;out:	return ret;}/*configure PER_CLK*/static int per_clk_set_rate(struct clk *clk, u32 rate){	u32 tmp;	tmp = __raw_readl(clk->scale_reg);	tmp &= ~(0x1f << 2);	tmp |= ((clk->parent->rate / clk->rate) - 1) << 2;	__raw_writel(tmp, clk->scale_reg);	clk->rate = rate;	return 0;}/*configure HCLK*/static int hclk_set_rate(struct clk *clk, u32 rate){	u32 tmp;	tmp = __raw_readl(clk->scale_reg);	tmp = tmp & ~0x3;	switch (rate) {	case 1:		break;	case 2:		tmp |= 1;		break;	case 4:		tmp |= 2;		break;	}	__raw_writel(tmp, clk->scale_reg);	clk->rate = rate;	return 0;}static u32 hclk_round_rate(struct clk *clk, u32 rate){	switch (rate) {	case 1:	case 4:		return rate;	}	return 2;}static u32 per_clk_round_rate(struct clk *clk, u32 rate){	return CLK_RATE_13MHZ;}static int on_off_set_rate(struct clk *clk, u32 rate){	if (rate) {		clk_reg_enable(clk);		clk->rate = 1;	} else {		clk_reg_disable(clk);		clk->rate = 0;	}	return 0;}static int on_off_inv_set_rate(struct clk *clk, u32 rate){	if (rate) {		clk_reg_disable(clk);	/*enable bit is inverted */		clk->rate = 1;	} else {		clk_reg_enable(clk);		clk->rate = 0;	}	return 0;}static u32 on_off_round_rate(struct clk *clk, u32 rate){	return (rate ? 1 : 0);}static u32 pll4_round_rate(struct clk *clk, u32 rate){	if (rate > CLK_RATE_208MHZ)		rate = CLK_RATE_208MHZ;	if (rate == CLK_RATE_208MHZ && hclk_ck.user_rate == 1)		rate = CLK_RATE_208MHZ - CLK_RATE_13MHZ;	return (rate - (rate % (hclk_ck.user_rate * CLK_RATE_13MHZ)));}static u32 pll3_round_rate(struct clk *clk, u32 rate){	if (rate > CLK_RATE_208MHZ)		rate = CLK_RATE_208MHZ;	return (rate - rate % CLK_RATE_13MHZ);}static u32 pll5_round_rate(struct clk *clk, u32 rate){	return (rate ? CLK_RATE_48MHZ : 0);}static u32 ck_13MHz_round_rate(struct clk *clk, u32 rate){	return (rate ? CLK_RATE_13MHZ : 0);}static int ck_13MHz_set_rate(struct clk *clk, u32 rate){	if (rate) {		clk_reg_disable(clk);	/*enable bit is inverted */		udelay(500);		clk->rate = CLK_RATE_13MHZ;		ck_1MHz.rate = CLK_RATE_1MHZ;	} else {		clk_reg_enable(clk);		clk->rate = 0;		ck_1MHz.rate = 0;	}	return 0;}static int pll1_set_rate(struct clk *clk, u32 rate){#if 0 /* doesn't work on some boards, probably a HW BUG */	if (rate) {		clk_reg_disable(clk);	/*enable bit is inverted */		if (!clk_wait_for_pll_lock(clk)) {			clk->rate = CLK_RATE_13MHZ;		} else {			clk_reg_enable(clk);			clk->rate = 0;		}	} else {		clk_reg_enable(clk);		clk->rate = 0;	}#endif	return 0;}/* Clock sources */static struct clk osc_13MHz = {	.name = "osc_13MHz",	.flags = FIXED_RATE,	.rate = CLK_RATE_13MHZ,};static struct clk ck_13MHz = {	.name = "ck_13MHz",	.parent = &osc_13MHz,	.flags = NEEDS_INITIALIZATION,	.round_rate = &ck_13MHz_round_rate,	.set_rate = &ck_13MHz_set_rate,	.enable_reg = OSC13CTRL_REG,	.enable_shift = 0,	.rate = CLK_RATE_13MHZ,};static struct clk osc_32KHz = {	.name = "osc_32KHz",	.flags = FIXED_RATE,	.rate = CLK_RATE_32KHZ,};/*attached to PLL5*/static struct clk ck_1MHz = {	.name = "ck_1MHz",	.flags = FIXED_RATE | PARENT_SET_RATE,	.parent = &ck_13MHz,};/* PLL1 (397) - provides 13' MHz clock */static struct clk ck_pll1 = {	.name = "ck_pll1",	.parent = &osc_32KHz,	.flags = NEEDS_INITIALIZATION,	.round_rate = &ck_13MHz_round_rate,	.set_rate = &pll1_set_rate,	.enable_reg = PLLCTRL_REG,	.enable_shift = 1,	.scale_reg = PLLCTRL_REG,	.rate = CLK_RATE_13MHZ,};/* CPU/Bus PLL */static struct clk ck_pll4 = {	.name = "ck_pll4",	.parent = &ck_pll1,	.flags = RATE_PROPAGATES | NEEDS_INITIALIZATION,	.propagate_next = &per_ck,	.round_rate = &pll4_round_rate,	.set_rate = &pll160_set_rate,	.rate = CLK_RATE_208MHZ,	.scale_reg = HCLKPLLCTRL_REG,	.enable_reg = PWRCTRL_REG,	.enable_shift = 2,	.parent_switch_reg = SYSCLKCTRL_REG,	.set_parent = &set_13MHz_parent,};/* USB PLL */static struct clk ck_pll5 = {	.name = "ck_pll5",	.parent = &ck_1MHz,	.flags = NEEDS_INITIALIZATION,	.round_rate = &pll5_round_rate,	.set_rate = &pll160_set_rate,	.scale_reg = USBCTRL_REG,	.enable_reg = USBCTRL_REG,	.enable_shift = 18,	.enable_reg1 = USBCTRL_REG,	.enable_shift1 = 17,};/* XPERTTeak DSP PLL */static struct clk ck_pll3 = {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -