mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-27 13:12:24 +02:00
240bf1625e
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11601 3c298f89-4303-0410-b956-a3cf2f4a3e73
1087 lines
31 KiB
C
1087 lines
31 KiB
C
/*
|
|
* ########################################################################
|
|
*
|
|
* This program is free software; you can distribute it and/or modify it
|
|
* under the terms of the GNU General Public License (Version 2) as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
|
|
*
|
|
* ########################################################################
|
|
*
|
|
* danube_cgu.c
|
|
*
|
|
* Description:
|
|
* device driver of clock generation unit of Danube chip
|
|
* Author:
|
|
* Samuels Xu Liang
|
|
* Created:
|
|
* 19 Jul 2005
|
|
* History & Modification Tag:
|
|
* ___________________________________________________________________________
|
|
* | Tag | Comments | Modifier & Time |
|
|
* |--------+---------------------------------------------+------------------|
|
|
* | S0.0 | First version of this driver and the tag is | Samuels Xu Liang |
|
|
* | | implied. | 19 Jul 2005 |
|
|
* ---------------------------------------------------------------------------
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Head File
|
|
* ####################################
|
|
*/
|
|
|
|
/*
|
|
* Common Head File
|
|
*/
|
|
#include <linux/config.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/version.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/init.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/irq.h>
|
|
#include <linux/errno.h>
|
|
|
|
/*
|
|
* Chip Specific Head File
|
|
*/
|
|
#include "ifx_cgu.h"
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Definition
|
|
* ####################################
|
|
*/
|
|
|
|
#define DEBUG_ON_AMAZON 1
|
|
#define DEBUG_PRINT_INFO 1
|
|
|
|
/*
|
|
* Frequency of Clock Direct Feed from The Analog Line Driver Chip
|
|
*/
|
|
#define BASIC_INPUT_CLOCK_FREQUENCY 35328000
|
|
|
|
/*
|
|
* Bits Operation
|
|
*/
|
|
#define GET_BITS(x, msb, lsb) (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
|
|
#define SET_BITS(x, msb, lsb, value) (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb)))
|
|
|
|
/*
|
|
* CGU Register Mapping
|
|
*/
|
|
#define DANUBE_CGU (KSEG1 + 0x1F103000)
|
|
#define DANUBE_CGU_DIV ((volatile u32*)(DANUBE_CGU + 0x0000))
|
|
#define DANUBE_CGU_PLL_NMK0 ((volatile u32*)(DANUBE_CGU + 0x0004))
|
|
#define DANUBE_CGU_PLL_SR0 ((volatile u32*)(DANUBE_CGU + 0x0008))
|
|
#define DANUBE_CGU_PLL_NMK1 ((volatile u32*)(DANUBE_CGU + 0x000C))
|
|
#define DANUBE_CGU_PLL_SR1 ((volatile u32*)(DANUBE_CGU + 0x0010))
|
|
#define DANUBE_CGU_PLL_SR2 ((volatile u32*)(DANUBE_CGU + 0x0014))
|
|
#define DANUBE_CGU_IF_CLK ((volatile u32*)(DANUBE_CGU + 0x0018))
|
|
#define DANUBE_CGU_OSC_CTRL ((volatile u32*)(DANUBE_CGU + 0x001C))
|
|
#define DANUBE_CGU_SMD ((volatile u32*)(DANUBE_CGU + 0x0020))
|
|
#define DANUBE_CGU_CRD ((volatile u32*)(DANUBE_CGU + 0x0024))
|
|
#define DANUBE_CGU_CT1SR ((volatile u32*)(DANUBE_CGU + 0x0028))
|
|
#define DANUBE_CGU_CT2SR ((volatile u32*)(DANUBE_CGU + 0x002C))
|
|
#define DANUBE_CGU_PCMCR ((volatile u32*)(DANUBE_CGU + 0x0030))
|
|
#define DANUBE_CGU_MUX ((volatile u32*)(DANUBE_CGU + 0x0034))
|
|
|
|
/*
|
|
* CGU Divider Register
|
|
*/
|
|
#define CGU_DIV_SFTR (*DANUBE_CGU_DIV & (1 << 31))
|
|
#define CGU_DIV_DIVE (*DANUBE_CGU_DIV & (1 << 16))
|
|
#define CGU_DIV_IOR GET_BITS(*DANUBE_CGU_DIV, 5, 4)
|
|
#define CGU_DIV_FKS GET_BITS(*DANUBE_CGU_DIV, 3, 2)
|
|
#define CGU_DIV_FBS GET_BITS(*DANUBE_CGU_DIV, 1, 0)
|
|
|
|
/*
|
|
* CGU PLL0 NMK Register
|
|
*/
|
|
#define CGU_PLL_NMK0_PLLN ((*DANUBE_CGU_PLL_NMK0 & (0xFFFFFFFF ^ ((1 << 24) - 1))) >> 24)
|
|
#define CGU_PLL_NMK0_PLLM GET_BITS(*DANUBE_CGU_PLL_NMK0, 23, 20)
|
|
#define CGU_PLL_NMK0_PLLK GET_BITS(*DANUBE_CGU_PLL_NMK0, 19, 0)
|
|
|
|
/*
|
|
* CGU PLL0 Status Register
|
|
*/
|
|
#define CGU_PLL_SR0_PLLDIV ((*DANUBE_CGU_PLL_SR0 & (0xFFFFFFFF ^ ((1 << 28) - 1))) >> 28)
|
|
#define CGU_PLL_SR0_PLLDEN (*DANUBE_CGU_PLL_SR0 & (1 << 26))
|
|
#define CGU_PLL_SR0_PLLPSE GET_BITS(*DANUBE_CGU_PLL_SR0, 5, 4)
|
|
#define CGU_PLL_SR0_PLLB (*DANUBE_CGU_PLL_SR0 & (1 << 2))
|
|
#define CGU_PLL_SR0_PLLL (*DANUBE_CGU_PLL_SR0 & (1 << 1))
|
|
#define CGU_PLL_SR0_PLLEN (*DANUBE_CGU_PLL_SR0 & (1 << 0))
|
|
|
|
#define CGU_PLL_SR0_DSMSEL 1
|
|
#define CGU_PLL_SR0_PHASE_DIV_EN 1
|
|
|
|
/*
|
|
* CGU PLL1 NMK Register
|
|
*/
|
|
#define CGU_PLL_NMK1_PLLN ((*DANUBE_CGU_PLL_NMK1 & (0xFFFFFFFF ^ ((1 << 24) - 1))) >> 24)
|
|
#define CGU_PLL_NMK1_PLLM GET_BITS(*DANUBE_CGU_PLL_NMK1, 23, 20)
|
|
#define CGU_PLL_NMK1_PLLK GET_BITS(*DANUBE_CGU_PLL_NMK1, 19, 0)
|
|
|
|
/*
|
|
* CGU PLL1 Status Register
|
|
*/
|
|
#define CGU_PLL_SR1_PLLDIV ((*DANUBE_CGU_PLL_SR1 & (0xFFFFFFFF ^ ((1 << 28) - 1))) >> 28)
|
|
#define CGU_PLL_SR1_PLLDEN (*DANUBE_CGU_PLL_SR1 & (1 << 26))
|
|
#define CGU_PLL_SR1_PLLPSE GET_BITS(*DANUBE_CGU_PLL_SR1, 5, 4)
|
|
#define CGU_PLL_SR1_PLLB (*DANUBE_CGU_PLL_SR1 & (1 << 2))
|
|
#define CGU_PLL_SR1_PLLL (*DANUBE_CGU_PLL_SR1 & (1 << 1))
|
|
#define CGU_PLL_SR1_PLLEN (*DANUBE_CGU_PLL_SR1 & (1 << 0))
|
|
|
|
#define CGU_PLL_SR1_DSMSEL 1
|
|
#define CGU_PLL_SR1_PHASE_DIV_EN 1
|
|
|
|
/*
|
|
* CGU PLL2 Status Register
|
|
*/
|
|
#define CGU_PLL_SR2_PLLDIV ((*DANUBE_CGU_PLL_SR2 & (0xFFFFFFFF ^ ((1 << 28) - 1))) >> 28)
|
|
#define CGU_PLL_SR2_PLLDEN (*DANUBE_CGU_PLL_SR2 & (1 << 27))
|
|
#define CGU_PLL_SR2_PLLN GET_BITS(*DANUBE_CGU_PLL_SR2, 25, 20)
|
|
#define CGU_PLL_SR2_PLLM GET_BITS(*DANUBE_CGU_PLL_SR2, 19, 16)
|
|
#define CGU_PLL_SR2_PLLPS (*DANUBE_CGU_PLL_SR2 & (1 << 5))
|
|
#define CGU_PLL_SR2_PLLPE (*DANUBE_CGU_PLL_SR2 & (1 << 4))
|
|
#define CGU_PLL_SR2_PLLB (*DANUBE_CGU_PLL_SR2 & (1 << 2))
|
|
#define CGU_PLL_SR2_PLLL (*DANUBE_CGU_PLL_SR2 & (1 << 1))
|
|
#define CGU_PLL_SR2_PLLEN (*DANUBE_CGU_PLL_SR2 & (1 << 0))
|
|
|
|
/*
|
|
* CGU Interface Clock Register
|
|
*/
|
|
#define CGU_IF_CLK_CLKOD0 GET_BITS(*DANUBE_CGU_IF_CLK, 27, 26)
|
|
#define CGU_IF_CLK_CLKOD1 GET_BITS(*DANUBE_CGU_IF_CLK, 25, 24)
|
|
#define CGU_IF_CLK_CLKOD2 GET_BITS(*DANUBE_CGU_IF_CLK, 23, 22)
|
|
#define CGU_IF_CLK_CLKOD3 GET_BITS(*DANUBE_CGU_IF_CLK, 21, 20)
|
|
#define CGU_IF_CLK_PDA (*DANUBE_CGU_IF_CLK & (1 << 18))
|
|
#define CGU_IF_CLK_PCI_B (*DANUBE_CGU_IF_CLK & (1 << 17))
|
|
#define CGU_IF_CLK_PCIBM (*DANUBE_CGU_IF_CLK & (1 << 16))
|
|
#define CGU_IF_CLK_MIICS (*DANUBE_CGU_IF_CLK & (1 << 3))
|
|
#define CGU_IF_CLK_USBCS (*DANUBE_CGU_IF_CLK & (1 << 2))
|
|
#define CGU_IF_CLK_PCIF (*DANUBE_CGU_IF_CLK & (1 << 1))
|
|
#define CGU_IF_CLK_PCIS (*DANUBE_CGU_IF_CLK & (1 << 0))
|
|
|
|
/*
|
|
* CGU Oscillator Control Register
|
|
*/
|
|
#define CGU_OSC_CTRL GET_BITS(*DANUBE_CGU_OSC_CTRL, 1, 0)
|
|
|
|
/*
|
|
* CGU SDRAM Memory Delay Register
|
|
*/
|
|
#define CGU_SMD_CLKI (*DANUBE_CGU_SMD & (1 << 31))
|
|
#define CGU_SMD_MIDS GET_BITS(*DANUBE_CGU_SMD, 17, 12)
|
|
#define CGU_SMD_MODS GET_BITS(*DANUBE_CGU_SMD, 11, 6)
|
|
#define CGU_SMD_MDSEL GET_BITS(*DANUBE_CGU_SMD, 5, 0)
|
|
|
|
/*
|
|
* CGU CPU Clock Reduction Register
|
|
*/
|
|
#define CGU_CRD_SFTR (*DANUBE_CGU_CRD & (1 << 31))
|
|
#define CGU_CRD_DIVE (*DANUBE_CGU_CRD & (1 << 16))
|
|
#define CGU_CRD_CRD1 GET_BITS(*DANUBE_CGU_CRD, 3, 2)
|
|
#define CGU_CRD_CRD GET_BITS(*DANUBE_CGU_CRD, 1, 0)
|
|
|
|
/*
|
|
* CGU CT Status Register 1
|
|
*/
|
|
#define CGU_CT1SR_PDOUT GET_BITS(*DANUBE_CGU_CT1SR, 13, 0)
|
|
|
|
/*
|
|
* CGU CT Status Register 2
|
|
*/
|
|
#define CGU_CT2SR_PLL1K GET_BITS(*DANUBE_CGU_CT2SR, 9, 0)
|
|
|
|
/*
|
|
* CGU PCM Control Register
|
|
*/
|
|
#define CGU_PCMCR_DCL1 GET_BITS(*DANUBE_CGU_PCMCR, 27, 25)
|
|
#define CGU_PCMCR_MUXDCL (*DANUBE_CGU_PCMCR & (1 << 22))
|
|
#define CGU_PCMCR_MUXFSC (*DANUBE_CGU_PCMCR & (1 << 18))
|
|
#define CGU_PCMCR_PCM_SL (*DANUBE_CGU_PCMCR & (1 << 13))
|
|
#define CGU_PCMCR_DNTR (*DANUBE_CGU_PCMCR & (1 << 12))
|
|
|
|
/*
|
|
* CGU Clock Mux Register
|
|
*/
|
|
#define CGU_MUX_MII_CLK (*DANUBE_CGU_MUX & (1 << 6))
|
|
#define CGU_MUX_SUB_SYS GET_BITS(*DANUBE_CGU_MUX, 5, 3)
|
|
#define CGU_MUX_PP32 GET_BITS(*DANUBE_CGU_MUX, 1, 0)
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Preparation of Debug on Amazon Chip
|
|
* ####################################
|
|
*/
|
|
|
|
/*
|
|
* If try module on Amazon chip, prepare some tricks to prevent invalid memory write.
|
|
*/
|
|
#if defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
|
|
u32 g_pFakeRegisters[0x0100];
|
|
|
|
#undef DANUBE_CGU
|
|
#define DANUBE_CGU ((u32)g_pFakeRegisters)
|
|
#endif // defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Data Type
|
|
* ####################################
|
|
*/
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Declaration
|
|
* ####################################
|
|
*/
|
|
|
|
/*
|
|
* Pre-declaration of File Operations
|
|
*/
|
|
static ssize_t cgu_read(struct file *, char *, size_t, loff_t *);
|
|
static ssize_t cgu_write(struct file *, const char *, size_t, loff_t *);
|
|
static int cgu_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
|
|
static int cgu_open(struct inode *, struct file *);
|
|
static int cgu_release(struct inode *, struct file *);
|
|
|
|
/*
|
|
* Pre-declaration of 64-bit Unsigned Integer Operation
|
|
*/
|
|
static inline void uint64_multiply(unsigned int, unsigned int, unsigned int *);
|
|
static inline void uint64_divide(unsigned int *, unsigned int, unsigned int *, unsigned int *);
|
|
|
|
/*
|
|
* Calculate PLL Frequency
|
|
*/
|
|
static inline u32 cal_dsm(u32, u32);
|
|
static inline u32 mash_dsm(u32, u32, u32);
|
|
static inline u32 ssff_dsm_1(u32, u32, u32);
|
|
static inline u32 ssff_dsm_2(u32, u32, u32);
|
|
static inline u32 dsm(u32 M, u32, u32, int, int);
|
|
static inline u32 cgu_get_pll0_fosc(void);
|
|
static inline u32 cgu_get_pll0_fps(void);
|
|
static inline u32 cgu_get_pll0_fdiv(void);
|
|
static inline u32 cgu_get_pll1_fosc(void);
|
|
static inline u32 cgu_get_pll1_fps(void);
|
|
static inline u32 cgu_get_pll1_fdiv(void);
|
|
static inline u32 cgu_get_pll2_fosc(void);
|
|
static inline u32 cgu_get_pll2_fps(void);
|
|
|
|
/*
|
|
* Export Functions
|
|
*/
|
|
u32 cgu_get_mips_clock(int);
|
|
u32 cgu_get_cpu_clock(void);
|
|
u32 cgu_get_io_region_clock(void);
|
|
u32 cgu_get_fpi_bus_clock(int);
|
|
u32 cgu_get_pp32_clock(void);
|
|
u32 cgu_get_pci_clock(void);
|
|
u32 cgu_get_ethernet_clock(void);
|
|
u32 cgu_get_usb_clock(void);
|
|
u32 cgu_get_clockout(int);
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Local Variable
|
|
* ####################################
|
|
*/
|
|
|
|
static struct file_operations cgu_fops = {
|
|
owner: THIS_MODULE,
|
|
llseek: no_llseek,
|
|
read: cgu_read,
|
|
write: cgu_write,
|
|
ioctl: cgu_ioctl,
|
|
open: cgu_open,
|
|
release: cgu_release
|
|
};
|
|
|
|
static struct miscdevice cgu_miscdev = {
|
|
MISC_DYNAMIC_MINOR,
|
|
"danube_cgu_dev",
|
|
&cgu_fops
|
|
};
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Global Variable
|
|
* ####################################
|
|
*/
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Local Function
|
|
* ####################################
|
|
*/
|
|
|
|
static ssize_t cgu_read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
static ssize_t cgu_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
static int cgu_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct cgu_clock_rates rates;
|
|
|
|
if ( _IOC_TYPE(cmd) != CGU_IOC_MAGIC
|
|
|| _IOC_NR(cmd) >= CGU_IOC_MAXNR )
|
|
return -ENOTTY;
|
|
|
|
if ( _IOC_DIR(cmd) & _IOC_READ )
|
|
ret = !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd));
|
|
else if ( _IOC_DIR(cmd) & _IOC_WRITE )
|
|
ret = !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd));
|
|
if ( ret )
|
|
return -EFAULT;
|
|
|
|
switch ( cmd )
|
|
{
|
|
case CGU_GET_CLOCK_RATES:
|
|
/* Calculate Clock Rates */
|
|
rates.mips0 = cgu_get_mips_clock(0);
|
|
rates.mips1 = cgu_get_mips_clock(1);
|
|
rates.cpu = cgu_get_cpu_clock();
|
|
rates.io_region = cgu_get_io_region_clock();
|
|
rates.fpi_bus1 = cgu_get_fpi_bus_clock(1);
|
|
rates.fpi_bus2 = cgu_get_fpi_bus_clock(2);
|
|
rates.pp32 = cgu_get_pp32_clock();
|
|
rates.pci = cgu_get_pci_clock();
|
|
rates.ethernet = cgu_get_ethernet_clock();
|
|
rates.usb = cgu_get_usb_clock();
|
|
rates.clockout0 = cgu_get_clockout(0);
|
|
rates.clockout1 = cgu_get_clockout(1);
|
|
rates.clockout2 = cgu_get_clockout(2);
|
|
rates.clockout3 = cgu_get_clockout(3);
|
|
/* Copy to User Space */
|
|
copy_to_user((char*)arg, (char*)&rates, sizeof(rates));
|
|
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
ret = -ENOTTY;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int cgu_open(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int cgu_release(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* calculate 64-bit multiplication result of two 32-bit unsigned integer
|
|
* Input:
|
|
* u32Multiplier1 --- u32 (32-bit), one of the multipliers
|
|
* u32Multiplier2 --- u32 (32-bit), the other multiplier
|
|
* u32Result --- u32[2], array to retrieve the multiplication result,
|
|
* index 0 is high word, index 1 is low word
|
|
* Output:
|
|
* none
|
|
*/
|
|
static inline void uint64_multiply(u32 u32Multiplier1, u32 u32Multiplier2, u32 u32Result[2])
|
|
{
|
|
u32 u32Multiplier1LowWord = u32Multiplier1 & 0xFFFF;
|
|
u32 u32Multiplier1HighWord = u32Multiplier1 >> 16;
|
|
u32 u32Multiplier2LowWord = u32Multiplier2 & 0xFFFF;
|
|
u32 u32Multiplier2HighWord = u32Multiplier2 >> 16;
|
|
u32 u32Combo1, u32Combo2, u32Combo3, u32Combo4;
|
|
u32 u32Word1, u32Word2, u32Word3, u32Word4;
|
|
|
|
u32Combo1 = u32Multiplier1LowWord * u32Multiplier2LowWord;
|
|
u32Combo2 = u32Multiplier1HighWord * u32Multiplier2LowWord;
|
|
u32Combo3 = u32Multiplier1LowWord * u32Multiplier2HighWord;
|
|
u32Combo4 = u32Multiplier1HighWord * u32Multiplier2HighWord;
|
|
|
|
u32Word1 = u32Combo1 & 0xFFFF;
|
|
u32Word2 = (u32Combo1 >> 16) + (u32Combo2 & 0xFFFF) + (u32Combo3 & 0xFFFF);
|
|
u32Word3 = (u32Combo2 >> 16) + (u32Combo3 >> 16) + (u32Combo4 & 0xFFFF) + (u32Word2 >> 16);
|
|
u32Word4 = (u32Combo4 >> 16) + (u32Word3 >> 16);
|
|
|
|
u32Result[0] = (u32Word4 << 16) | u32Word3;
|
|
u32Result[1] = (u32Word2 << 16) | u32Word1;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* divide 64-bit unsigned integer with 32-bit unsigned integer
|
|
* Input:
|
|
* u32Numerator --- u32[2], index 0 is high word of numerator, while
|
|
* index 1 is low word of numerator
|
|
* u32Denominator --- u32 (32-bit), the denominator in division, this
|
|
* parameter can not be zero, or lead to unpredictable
|
|
* result
|
|
* pu32Quotient --- u32 *, the pointer to retrieve 32-bit quotient, null
|
|
* pointer means ignore quotient
|
|
* pu32Residue --- u32 *, the pointer to retrieve 32-bit residue null
|
|
* pointer means ignore residue
|
|
* Output:
|
|
* none
|
|
*/
|
|
static inline void uint64_divide(u32 u32Numerator[2], u32 u32Denominator, u32 *pu32Quotient, u32 *pu32Residue)
|
|
{
|
|
u32 u32DWord1, u32DWord2, u32DWord3;
|
|
u32 u32Quotient;
|
|
int i;
|
|
|
|
u32DWord3 = 0;
|
|
u32DWord2 = u32Numerator[0];
|
|
u32DWord1 = u32Numerator[1];
|
|
|
|
u32Quotient = 0;
|
|
|
|
for ( i = 0; i < 64; i++ )
|
|
{
|
|
u32DWord3 = (u32DWord3 << 1) | (u32DWord2 >> 31);
|
|
u32DWord2 = (u32DWord2 << 1) | (u32DWord1 >> 31);
|
|
u32DWord1 <<= 1;
|
|
u32Quotient <<= 1;
|
|
if ( u32DWord3 >= u32Denominator )
|
|
{
|
|
u32DWord3 -= u32Denominator;
|
|
u32Quotient |= 1;
|
|
}
|
|
}
|
|
if ( pu32Quotient )
|
|
*pu32Quotient = u32Quotient;
|
|
if ( pu32Residue )
|
|
*pu32Residue = u32DWord3;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* common routine to calculate PLL frequency
|
|
* Input:
|
|
* num --- u32, numerator
|
|
* den --- u32, denominator
|
|
* Output:
|
|
* u32 --- frequency the PLL output
|
|
*/
|
|
static inline u32 cal_dsm(u32 num, u32 den)
|
|
{
|
|
u32 ret;
|
|
u32 temp[2];
|
|
u32 residue;
|
|
|
|
uint64_multiply(num, BASIC_INPUT_CLOCK_FREQUENCY, temp);
|
|
uint64_divide(temp, den, &ret, &residue);
|
|
if ( (residue << 1) >= den )
|
|
ret++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* calculate PLL frequency following MASH-DSM
|
|
* Input:
|
|
* M --- u32, denominator coefficient
|
|
* N --- u32, numerator integer coefficient
|
|
* K --- u32, numerator fraction coefficient
|
|
* Output:
|
|
* u32 --- frequency the PLL output
|
|
*/
|
|
static inline u32 mash_dsm(u32 M, u32 N, u32 K)
|
|
{
|
|
u32 num = ((N + 1) << 10) + K;
|
|
u32 den = (M + 1) << 10;
|
|
|
|
return cal_dsm(num, den);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* calculate PLL frequency following SSFF-DSM (0.25 < fraction < 0.75)
|
|
* Input:
|
|
* M --- u32, denominator coefficient
|
|
* N --- u32, numerator integer coefficient
|
|
* K --- u32, numerator fraction coefficient
|
|
* Output:
|
|
* u32 --- frequency the PLL output
|
|
*/
|
|
static inline u32 ssff_dsm_1(u32 M, u32 N, u32 K)
|
|
{
|
|
u32 num = ((N + 1) << 11) + K + 512;
|
|
u32 den = (M + 1) << 11;
|
|
|
|
return cal_dsm(num, den);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* calculate PLL frequency following SSFF-DSM
|
|
* (fraction < 0.125 || fraction > 0.875)
|
|
* Input:
|
|
* M --- u32, denominator coefficient
|
|
* N --- u32, numerator integer coefficient
|
|
* K --- u32, numerator fraction coefficient
|
|
* Output:
|
|
* u32 --- frequency the PLL output
|
|
*/
|
|
static inline u32 ssff_dsm_2(u32 M, u32 N, u32 K)
|
|
{
|
|
u32 num = K >= 512 ? ((N + 1) << 12) + K - 512 : ((N + 1) << 12) + K + 3584;
|
|
u32 den = (M + 1) << 12;
|
|
|
|
return cal_dsm(num, den);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* calculate PLL frequency
|
|
* Input:
|
|
* M --- u32, denominator coefficient
|
|
* N --- u32, numerator integer coefficient
|
|
* K --- u32, numerator fraction coefficient
|
|
* dsmsel --- int, 0: MASH-DSM, 1: SSFF-DSM
|
|
* phase_div_en --- int, 0: 0.25 < fraction < 0.75
|
|
* 1: fraction < 0.125 || fraction > 0.875
|
|
* Output:
|
|
* u32 --- frequency the PLL output
|
|
*/
|
|
static inline u32 dsm(u32 M, u32 N, u32 K, int dsmsel, int phase_div_en)
|
|
{
|
|
if ( !dsmsel )
|
|
return mash_dsm(M, N, K);
|
|
else
|
|
if ( !phase_div_en )
|
|
return ssff_dsm_1(M, N, K);
|
|
else
|
|
return ssff_dsm_2(M, N, K);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get oscillate frequency of PLL0
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL0 Fosc
|
|
*/
|
|
static inline u32 cgu_get_pll0_fosc(void)
|
|
{
|
|
return CGU_PLL_SR0_PLLB ? BASIC_INPUT_CLOCK_FREQUENCY : dsm(CGU_PLL_NMK0_PLLM, CGU_PLL_NMK0_PLLN, CGU_PLL_NMK0_PLLK, CGU_PLL_SR0_DSMSEL, CGU_PLL_SR0_PHASE_DIV_EN);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get output frequency of PLL0 phase shifter
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL0 Fps
|
|
*/
|
|
static inline u32 cgu_get_pll0_fps(void)
|
|
{
|
|
register u32 fps = cgu_get_pll0_fosc();
|
|
|
|
switch ( CGU_PLL_SR0_PLLPSE )
|
|
{
|
|
case 1:
|
|
/* 1.5 */
|
|
fps = ((fps << 1) + 1) / 3; break;
|
|
case 2:
|
|
/* 1.25 */
|
|
fps = ((fps << 2) + 2) / 5; break;
|
|
case 3:
|
|
/* 3.5 */
|
|
fps = ((fps << 1) + 3) / 7;
|
|
}
|
|
return fps;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get output frequency of PLL0 output divider
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL0 Fdiv
|
|
*/
|
|
static inline u32 cgu_get_pll0_fdiv(void)
|
|
{
|
|
register u32 fdiv = cgu_get_pll0_fosc();
|
|
|
|
if ( CGU_PLL_SR0_PLLDEN )
|
|
fdiv = (fdiv + (CGU_PLL_SR0_PLLDIV + 1) / 2) / (CGU_PLL_SR0_PLLDIV + 1);
|
|
return fdiv;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get oscillate frequency of PLL1
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL1 Fosc
|
|
*/
|
|
static inline u32 cgu_get_pll1_fosc(void)
|
|
{
|
|
return CGU_PLL_SR1_PLLB ? BASIC_INPUT_CLOCK_FREQUENCY : dsm(CGU_PLL_NMK1_PLLM, CGU_PLL_NMK1_PLLN, CGU_PLL_NMK1_PLLK, CGU_PLL_SR1_DSMSEL, CGU_PLL_SR1_PHASE_DIV_EN);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get output frequency of PLL1 phase shifter
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL1 Fps
|
|
*/
|
|
static inline u32 cgu_get_pll1_fps(void)
|
|
{
|
|
register u32 fps = cgu_get_pll1_fosc();
|
|
|
|
switch ( CGU_PLL_SR1_PLLPSE )
|
|
{
|
|
case 1:
|
|
/* 1.5 */
|
|
fps = ((fps << 1) + 1) / 3; break;
|
|
case 2:
|
|
/* 1.25 */
|
|
fps = ((fps << 2) + 2) / 5; break;
|
|
case 3:
|
|
/* 3.5 */
|
|
fps = ((fps << 1) + 3) / 7;
|
|
}
|
|
return fps;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get output frequency of PLL1 output divider
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL1 Fdiv
|
|
*/
|
|
static inline u32 cgu_get_pll1_fdiv(void)
|
|
{
|
|
register u32 fdiv = cgu_get_pll1_fosc();
|
|
|
|
if ( CGU_PLL_SR1_PLLDEN )
|
|
fdiv = (fdiv + (CGU_PLL_SR1_PLLDIV + 1) / 2) / (CGU_PLL_SR1_PLLDIV + 1);
|
|
return fdiv;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get oscillate frequency of PLL2
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL2 Fosc
|
|
*/
|
|
static inline u32 cgu_get_pll2_fosc(void)
|
|
{
|
|
u32 ret;
|
|
u32 temp[2];
|
|
u32 residue;
|
|
|
|
uint64_multiply((CGU_PLL_SR2_PLLN + 1) * 8, cgu_get_pll0_fdiv(), temp);
|
|
uint64_divide(temp, CGU_PLL_SR2_PLLM + 1, &ret, &residue);
|
|
if ( (residue << 1) >= CGU_PLL_SR2_PLLM )
|
|
ret++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get output frequency of PLL2 phase shifter
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PLL2 Fps
|
|
*/
|
|
static inline u32 cgu_get_pll2_fps(void)
|
|
{
|
|
register u32 fps = cgu_get_pll2_fosc();
|
|
|
|
if ( CGU_PLL_SR2_PLLPE )
|
|
{
|
|
if ( CGU_PLL_SR2_PLLPS )
|
|
/* 1.25 */
|
|
fps = ((fps << 3) + 4) / 9;
|
|
else
|
|
/* 1.125 */
|
|
fps = ((fps << 2) + 2) / 5;
|
|
}
|
|
|
|
return fps;
|
|
}
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Global Function
|
|
* ####################################
|
|
*/
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of MIPS (0: core, 1: DSP)
|
|
* Input:
|
|
* cpu --- int, 0: core, 1: DSP
|
|
* Output:
|
|
* u32 --- frequency of MIPS coprocessor (0: core, 1: DSP)
|
|
*/
|
|
u32 cgu_get_mips_clock(int cpu)
|
|
{
|
|
register u32 ret = cgu_get_pll0_fosc();
|
|
|
|
if ( CGU_CRD_CRD )
|
|
ret = (ret + (CGU_CRD_CRD >> 1)) / (CGU_CRD_CRD + 1);
|
|
if ( cpu == 0 && CGU_CRD_CRD1 )
|
|
ret >>= CGU_CRD_CRD1;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of MIPS core
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of MIPS core
|
|
*/
|
|
u32 cgu_get_cpu_clock(void)
|
|
{
|
|
return cgu_get_mips_clock(0);
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of sub-system and memory controller
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of sub-system and memory controller
|
|
*/
|
|
u32 cgu_get_io_region_clock(void)
|
|
{
|
|
register u32 ret = (CGU_MUX_SUB_SYS > 4) ? cgu_get_pll0_fosc() : cgu_get_mips_clock(1);
|
|
|
|
switch ( CGU_MUX_SUB_SYS )
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
default:
|
|
ret = (ret + 1) >> 1; break;
|
|
case 2:
|
|
ret = (ret + 1) / 3; break;
|
|
case 3:
|
|
ret = (ret + 2) >> 2; break;
|
|
case 5:
|
|
ret = ((ret << 1) + 1) / 3; break;
|
|
case 6:
|
|
ret = ((ret << 1) + 2) / 5;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of FPI bus
|
|
* Input:
|
|
* fpi --- int, 1: FPI bus 1 (FBS1/Fast FPI Bus), 2: FPI bus 2 (FBS2)
|
|
* Output:
|
|
* u32 --- frequency of FPI bus
|
|
*/
|
|
u32 cgu_get_fpi_bus_clock(int fpi)
|
|
{
|
|
register u32 ret = cgu_get_io_region_clock();
|
|
|
|
if ( fpi == 2 )
|
|
ret >>= 1;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of PP32 processor
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PP32 processor
|
|
*/
|
|
u32 cgu_get_pp32_clock(void)
|
|
{
|
|
register u32 ret;
|
|
|
|
switch ( CGU_MUX_PP32 )
|
|
{
|
|
case 0:
|
|
default:
|
|
ret = ((cgu_get_pll2_fosc() << 2) + 2) / 5; break;
|
|
case 1:
|
|
ret = ((cgu_get_pll2_fosc() << 3) + 4) / 9; break;
|
|
case 2:
|
|
ret = cgu_get_fpi_bus_clock(1); break;
|
|
case 3:
|
|
ret = cgu_get_mips_clock(1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of PCI bus
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of PCI bus
|
|
*/
|
|
u32 cgu_get_pci_clock(void)
|
|
{
|
|
register u32 ret = 0;
|
|
|
|
if ( !CGU_IF_CLK_PCIS )
|
|
{
|
|
ret = cgu_get_pll2_fosc();
|
|
if ( CGU_IF_CLK_PCIF )
|
|
ret = (ret + 2) / 5;
|
|
else
|
|
ret = (ret + 4) / 9;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of ethernet module (MII)
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of ethernet module
|
|
*/
|
|
u32 cgu_get_ethernet_clock(void)
|
|
{
|
|
register u32 ret = 0;
|
|
|
|
if ( !CGU_IF_CLK_MIICS )
|
|
{
|
|
ret = cgu_get_pll2_fosc();
|
|
if ( CGU_MUX_MII_CLK )
|
|
ret = (ret + 3) / 6;
|
|
else
|
|
ret = (ret + 6) / 12;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of USB
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* u32 --- frequency of USB
|
|
*/
|
|
u32 cgu_get_usb_clock(void)
|
|
{
|
|
return CGU_IF_CLK_USBCS ? 12000000 : (cgu_get_pll2_fosc() + 12) / 25;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* get frequency of CLK_OUT pin
|
|
* Input:
|
|
* clkout --- int, clock out pin number
|
|
* Output:
|
|
* u32 --- frequency of CLK_OUT pin
|
|
*/
|
|
u32 cgu_get_clockout(int clkout)
|
|
{
|
|
u32 fosc1 = cgu_get_pll1_fosc();
|
|
u32 fosc2 = cgu_get_pll2_fosc();
|
|
|
|
if ( clkout > 3 || clkout < 0 )
|
|
return 0;
|
|
|
|
switch ( ((u32)clkout << 2) | GET_BITS(*DANUBE_CGU_IF_CLK, 21 + clkout * 2, 20 + clkout * 2) )
|
|
{
|
|
case 0: /* 32.768KHz */
|
|
case 14:
|
|
return (fosc1 + 6000) / 12000;
|
|
case 1: /* 1.536MHz */
|
|
return (fosc1 + 128) / 256;
|
|
case 2: /* 2.5MHz */
|
|
return (fosc2 + 60) / 120;
|
|
case 3: /* 12MHz */
|
|
case 5:
|
|
case 12:
|
|
return (fosc2 + 12) / 25;
|
|
case 4: /* 40MHz */
|
|
return (fosc2 * 2 + 7) / 15;
|
|
case 6: /* 24MHz */
|
|
return (fosc2 * 2 + 12) / 25;
|
|
case 7: /* 48MHz */
|
|
return (fosc2 * 4 + 12) / 25;
|
|
case 8: /* 25MHz */
|
|
case 15:
|
|
return (fosc2 + 6) / 12;
|
|
case 9: /* 50MHz */
|
|
case 13:
|
|
return (fosc2 + 3) / 6;
|
|
case 10:/* 30MHz */
|
|
return (fosc2 + 5) / 10;
|
|
case 11:/* 60MHz */
|
|
return (fosc2 + 2) / 5;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ####################################
|
|
* Init/Cleanup API
|
|
* ####################################
|
|
*/
|
|
|
|
/*
|
|
* Description:
|
|
* register device
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* 0 --- successful
|
|
* else --- failure, usually it is negative value of error code
|
|
*/
|
|
int __init danube_cgu_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = misc_register(&cgu_miscdev);
|
|
if ( ret )
|
|
{
|
|
printk(KERN_ERR "cgu: can't misc_register\n");
|
|
return ret;
|
|
}
|
|
else
|
|
printk(KERN_INFO "cgu: misc_register on minor = %d\n", cgu_miscdev.minor);
|
|
|
|
/*
|
|
* initialize fake registers to do testing on Amazon
|
|
*/
|
|
#if defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
|
|
#ifdef DEBUG_PRINT_INFO
|
|
#undef DEBUG_PRINT_INFO
|
|
#endif
|
|
#define DEBUG_PRINT_INFO 1
|
|
|
|
*DANUBE_CGU_DIV = 0x00010019;
|
|
*DANUBE_CGU_PLL_NMK0 = 0x416002C3;
|
|
*DANUBE_CGU_PLL_SR0 = 0x74000013;
|
|
*DANUBE_CGU_PLL_NMK1 = 0x4C60009C;
|
|
*DANUBE_CGU_PLL_SR1 = 0x54000013;
|
|
*DANUBE_CGU_PLL_SR2 = 0x58890013;
|
|
*DANUBE_CGU_IF_CLK = 0x00000000;
|
|
*DANUBE_CGU_OSC_CTRL = 0x00000000;
|
|
*DANUBE_CGU_SMD = 0x00000000;
|
|
*DANUBE_CGU_CRD = 0x00010000;
|
|
*DANUBE_CGU_CT1SR = 0x00000000;
|
|
*DANUBE_CGU_CT2SR = CGU_PLL_NMK1_PLLK;
|
|
*DANUBE_CGU_PCMCR = 0x00000000;
|
|
*DANUBE_CGU_MUX = 0x00000008;
|
|
#endif // defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
|
|
|
|
/*
|
|
* for testing only
|
|
*/
|
|
#if defined(DEBUG_PRINT_INFO) && DEBUG_PRINT_INFO
|
|
printk("pll0 N = %d, M = %d, K = %d, DIV = %d\n", CGU_PLL_NMK0_PLLN, CGU_PLL_NMK0_PLLM, CGU_PLL_NMK0_PLLK, CGU_PLL_SR0_PLLDIV);
|
|
printk("pll1 N = %d, M = %d, K = %d, DIV = %d\n", CGU_PLL_NMK1_PLLN, CGU_PLL_NMK1_PLLM, CGU_PLL_NMK1_PLLK, CGU_PLL_SR1_PLLDIV);
|
|
printk("pll2 N = %d, M = %d, DIV = %d\n", CGU_PLL_SR2_PLLN, CGU_PLL_SR2_PLLM, CGU_PLL_SR2_PLLDIV);
|
|
printk("pll0_fosc = %d\n", cgu_get_pll0_fosc());
|
|
printk("pll0_fps = %d\n", cgu_get_pll0_fps());
|
|
printk("pll0_fdiv = %d\n", cgu_get_pll0_fdiv());
|
|
printk("pll1_fosc = %d\n", cgu_get_pll1_fosc());
|
|
printk("pll1_fps = %d\n", cgu_get_pll1_fps());
|
|
printk("pll1_fdiv = %d\n", cgu_get_pll1_fdiv());
|
|
printk("pll2_fosc = %d\n", cgu_get_pll2_fosc());
|
|
printk("pll2_fps = %d\n", cgu_get_pll2_fps());
|
|
printk("mips0 clock = %d\n", cgu_get_mips_clock(0));
|
|
printk("mips1 clock = %d\n", cgu_get_mips_clock(1));
|
|
printk("cpu clock = %d\n", cgu_get_cpu_clock());
|
|
printk("IO region = %d\n", cgu_get_io_region_clock());
|
|
printk("FPI bus 1 = %d\n", cgu_get_fpi_bus_clock(1));
|
|
printk("FPI bus 2 = %d\n", cgu_get_fpi_bus_clock(2));
|
|
printk("PP32 clock = %d\n", cgu_get_pp32_clock());
|
|
printk("PCI clock = %d\n", cgu_get_pci_clock());
|
|
printk("Ethernet = %d\n", cgu_get_ethernet_clock());
|
|
printk("USB clock = %d\n", cgu_get_usb_clock());
|
|
printk("Clockout0 = %d\n", cgu_get_clockout(0));
|
|
printk("Clockout1 = %d\n", cgu_get_clockout(1));
|
|
printk("Clockout2 = %d\n", cgu_get_clockout(2));
|
|
printk("Clockout3 = %d\n", cgu_get_clockout(3));
|
|
#endif // defined(DEBUG_PRINT_INFO) && DEBUG_PRINT_INFO
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* deregister device
|
|
* Input:
|
|
* none
|
|
* Output:
|
|
* none
|
|
*/
|
|
void __exit danube_cgu_exit(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = misc_deregister(&cgu_miscdev);
|
|
if ( ret )
|
|
printk(KERN_ERR "cgu: can't misc_deregister, get error number %d\n", -ret);
|
|
else
|
|
printk(KERN_INFO "cgu: misc_deregister successfully\n");
|
|
}
|
|
|
|
module_init(danube_cgu_init);
|
|
module_exit(danube_cgu_exit);
|