diff options
Diffstat (limited to 'target/linux/ifxmips/image/u-boot/files/cpu/mips/danube/ifx_cgu.c')
-rw-r--r-- | target/linux/ifxmips/image/u-boot/files/cpu/mips/danube/ifx_cgu.c | 1086 |
1 files changed, 1086 insertions, 0 deletions
diff --git a/target/linux/ifxmips/image/u-boot/files/cpu/mips/danube/ifx_cgu.c b/target/linux/ifxmips/image/u-boot/files/cpu/mips/danube/ifx_cgu.c new file mode 100644 index 0000000000..bfaea37b46 --- /dev/null +++ b/target/linux/ifxmips/image/u-boot/files/cpu/mips/danube/ifx_cgu.c @@ -0,0 +1,1086 @@ +/* + * ######################################################################## + * + * 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); |