/* * Carsten Langgaard, carstenl@mips.com * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved. * * 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. * * IKAN specific setup. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_FUSIV_VX180) #include #elif defined(CONFIG_FUSIV_VX185) #include #endif #ifdef CONFIG_AVM_POWERMETER #include #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ /*--- #define ENABLE_CLOCK_GATING ---*/ #define PRINTK printk /*--- #define IKAN_CLK_DEBUG ---*/ #if defined(IKAN_CLK_DEBUG) #define DBG_TRC PRINTK #else/*--- #if defined(IKAN_CLK_DEBUG) ---*/ #define DBG_TRC(arg...) #endif/*--- #else ---*//*--- #if defined(IKAN_CLK_DEBUG) ---*/ /* * Proc Filesystem */ #ifdef CONFIG_PROC_FS static struct proc_dir_entry* g_clk_dir = NULL; static inline void proc_file_create(void); static inline void proc_file_delete(void); #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init ikan_clk_switch_init(void); static inline unsigned int ikan_norm_clock(unsigned int clk, int flag); unsigned int ikan_get_clock(enum _avm_clock_id clock_id); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(ENABLE_CLOCK_GATING) static void ikan_clock_status(struct seq_file *seq){ unsigned int status = *(unsigned int *)VX180_CLK_GATE_CONTROL; void print_status(char *name, unsigned int bit){ seq_printf(seq, "\t%.31s: %s\n", name, status & (1 << bit) ? "enabled" : "disabled"); } print_status("PCI System clock (PCI_CLK_EN)", 0); print_status("VPHY System clock (VPHY_CLK_EN)", 1); print_status("AMC System clock (EBI_CLK_EN)", 2); print_status("EMAC1 System clock (GIGE1_CLK_EN)", 3); print_status("GIGE1 APU clock(GIGE1_APU_CLK_EN) ", 4); print_status("GIGE2 System clock (GIGE2_CLK_EN)", 5); print_status("GIGE2 APU clock (GIGE2_APU_CLK_EN)", 6); print_status("VAP System clock (VAP_SYS_CLK_EN)", 7); print_status("VAP APU clock (VAP_APU_CLK_EN)", 8); print_status("BMU APU clock (BMU_APU_CLK_EN)", 9); print_status("BMU System clock (BMU_SYS_CLK_EN)", 10); print_status("WAP APU clock (WAP_APU_CLK_EN)", 11); print_status("WAP System clock (WAP_SYS_CLK_EN)", 12); print_status("USB Host System clock (USBH_CLK_EN)", 13); print_status("SAP APU clock (SAP_APU_CLK_EN)", 14); print_status("SAP System clock (SAP_APU_CLK_EN)", 15); } #endif /*--- #if defined(ENABLE_CLOCK_GATING) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int proc_change_clock_show(struct seq_file *seq, void *offset) { unsigned int cpu_clk = ikan_get_clock(avm_clock_id_cpu); unsigned int system_clk = ikan_get_clock(avm_clock_id_system); unsigned int usb_clk = ikan_get_clock(avm_clock_id_usb); unsigned int usb2_clk = ikan_get_clock(avm_clock_id_usb2); unsigned int ddr_clk = ikan_get_clock(avm_clock_id_ddr); unsigned int ephy_clk = ikan_get_clock(avm_clock_id_ephy); unsigned int pci_clk = ikan_get_clock(avm_clock_id_pci); unsigned int pcl66_clk = ikan_get_clock(avm_clock_id_pci66); unsigned int ap_clk = ikan_get_clock(avm_clock_id_ap); unsigned int _21xx_clk = ikan_get_clock(avm_clock_id_21xx); seq_printf(seq, "Clocks: " " CPU: %u %cHz" " SYSTEM: %u %cHz" " USB: %u %cHz" " USB2: %u %cHz" " DDR: %u %cHz" " EPHY: %u %cHz" " PCI: %u %cHz" " PCL66: %u %cHz" " AP: %u %cHz" " 21xx: %u %cHz" "\n", ikan_norm_clock(cpu_clk, 0), ikan_norm_clock(cpu_clk, 1), ikan_norm_clock(system_clk, 0), ikan_norm_clock(system_clk, 1), ikan_norm_clock(usb_clk, 0), ikan_norm_clock(usb_clk, 1), ikan_norm_clock(usb2_clk, 0), ikan_norm_clock(usb2_clk, 1), ikan_norm_clock(ddr_clk, 0), ikan_norm_clock(ddr_clk, 1), ikan_norm_clock(ephy_clk, 0), ikan_norm_clock(ephy_clk, 1), ikan_norm_clock(pci_clk, 0), ikan_norm_clock(pci_clk, 1), ikan_norm_clock(pcl66_clk, 0), ikan_norm_clock(pcl66_clk, 1), ikan_norm_clock(ap_clk, 0), ikan_norm_clock(ap_clk, 1), ikan_norm_clock(_21xx_clk, 0), ikan_norm_clock(_21xx_clk, 1)); #if defined(ENABLE_CLOCK_GATING) ikan_clock_status(seq); #endif /*--- #if defined(ENABLE_CLOCK_GATING) ---*/ return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(ENABLE_CLOCK_GATING) int ikan_change_clock_write(struct file *file, const char __user *buff, unsigned long count, void *data){ unsigned int bit; if(strstr(buff, "SAP_SYS_CLK_EN")){bit = 0; } else if(strstr(buff, "SAP_APU_CLK_EN")){bit = 1; } else if(strstr(buff, "USBH_CLK_EN")){bit = 2; } else if(strstr(buff, "WAP_SYS_CLK_EN")){bit = 3; } else if(strstr(buff, "WAP_APU_CLK_EN")){bit = 4; } else if(strstr(buff, "BMU_SYS_CLK_EN")){bit = 5; } else if(strstr(buff, "BMU_APU_CLK_EN")){bit = 6; } else if(strstr(buff, "VAP_APU_CLK_EN")){bit = 7; } else if(strstr(buff, "VAP_SYS_CLK_EN")){bit = 8; } else if(strstr(buff, "GIGE2_APU_CLK_EN")){bit = 9; } else if(strstr(buff, "GIGE2_CLK_EN")){bit = 10; } else if(strstr(buff, "GIGE1_APU_CLK_EN")){bit = 11; } else if(strstr(buff, "GIGE1_CLK_EN")){bit = 12; } else if(strstr(buff, "EBI_CLK_EN")){bit = 13; } else if(strstr(buff, "VPHY_CLK_EN")){bit = 14; } else if(strstr(buff, "PCI_CLK_EN")){bit = 15; } else{printk(KERN_ERR "Invallid keyword, availble: \n" "\tSAP_SYS_CLK_EN SAP_APU_CLK_EN USBH_CLK_EN WAP_SYS_CLK_EN WAP_APU_CLK_EN\n" "\tBMU_SYS_CLK_EN BMU_APU_CLK_EN VAP_APU_CLK_EN VAP_SYS_CLK_EN GIGE2_APU_CLK_EN\n" "\tGIGE2_CLK_EN GIGE1_APU_CLK_EN GIGE1_CLK_EN EBI_CLK_EN VPHY_CLK_EN PCI_CLK_EN\n"); return -EFAULT; } if(strstr(buff, "disable-all")){ printk(KERN_ERR "*VX180_CLK_GATE_CONTROL = 0\n"); *(unsigned int *)VX180_CLK_GATE_CONTROL = 0; } else if(strstr(buff, "disable")){ printk(KERN_ERR "*VX180_CLK_GATE_CONTROL &= ~(1 << %d) 0x%x\n", bit, ~(1 << bit)); *(unsigned int *)VX180_CLK_GATE_CONTROL &= ~(1 << bit); } else if(strstr(buff, "enable")){ printk(KERN_ERR "*VX180_CLK_GATE_CONTROL |= (1 << %d) 0x%x\n", bit, (1 << bit)); *(unsigned int *)VX180_CLK_GATE_CONTROL |= 1 << bit; } return count; } #endif /*--- #if defined(ENABLE_CLOCK_GATING) ---*/ static int proc_clk_setting_open(struct inode *inode, struct file *file) { return single_open(file, proc_change_clock_show, NULL); } static struct file_operations fops_clk_setting = { .open = proc_clk_setting_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static inline void proc_file_create(void) { g_clk_dir = proc_mkdir("driver/fusiv", NULL); proc_create("clocks", S_IRUGO | S_IWUSR, g_clk_dir, &fops_clk_setting); } static inline void proc_file_delete(void) { remove_proc_entry("clocks", g_clk_dir); remove_proc_entry("driver/fusiv", NULL); } static int __init ikan_clk_switch_init(void) { proc_file_create(); return 0; } late_initcall(ikan_clk_switch_init); #if defined(IKAN_CLK_DEBUG) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const char *ikan_name_clock_id(enum _avm_clock_id clock_id){ return clock_id == avm_clock_id_non ? "id_non" : clock_id == avm_clock_id_cpu ? "id_cpu" : clock_id == avm_clock_id_system ? "id_system" : clock_id == avm_clock_id_usb ? "id_usb" : clock_id == avm_clock_id_usb2 ? "id_usb2" : clock_id == avm_clock_id_ddr ? "id_ddr" : clock_id == avm_clock_id_ephy ? "id_ephy" : clock_id == avm_clock_id_pci ? "id_pci" : clock_id == avm_clock_id_pci66 ? "id_pci66" : clock_id == avm_clock_id_ap ? "id_ap" : clock_id == avm_clock_id_21xx ? "id_21xx" : "id_unknown"; } #endif/*--- #if defined(IKAN_CLK_DEBUG) ---*/ /*--------------------------------------------------------------------------------*\ * flag = 0: liefere NormFrequenz sonst 'K', 'M', ' ' \*--------------------------------------------------------------------------------*/ static inline unsigned int ikan_norm_clock(unsigned int clk, int flag) { const unsigned int MHZ = 1000000; if(flag == 0){ return ((clk / MHZ) * MHZ) == clk ? clk / MHZ : ((clk / 1000) * 1000) == clk ? clk / 1000 : clk; } return ((clk / MHZ) * MHZ) == clk ? 'M' : ((clk / 1000) * 1000) == clk ? 'K' : ' '; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init ikan_clk_init(void) { return 0; } arch_initcall(ikan_clk_init); #if defined(CONFIG_FUSIV_VX180) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ikan_compute_clock(unsigned int xtal, unsigned int pre_div, void *pll, unsigned int shift, unsigned int bits, unsigned int post_div){ unsigned int clk = xtal / pre_div; DBG_TRC("[ikan_clk] clk(%d) = xtal(%d) / pre_div(%d)\n", clk, xtal, pre_div); if((shift == 0) && (bits == 0)){ clk *= (unsigned int)pll; DBG_TRC("\tclk(%d) *= mult(%d)\n", clk, (unsigned int)pll); } else{ int mult = (*(volatile unsigned int *)pll >> shift) & ((1 << bits) - 1); clk *= mult; DBG_TRC("\tclk(%d) *= mult(%d) (Register 0x%08x)\n", clk, mult, *(unsigned int *)pll); } clk /= post_div; DBG_TRC("\tclk(%d) /= post_div(%d)\n", clk, post_div); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ikan_get_clock(enum _avm_clock_id clock_id){ unsigned int clk = 0; switch(clock_id){ /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_21xx: clk = ikan_compute_clock( default_ikan_vdsl_xtal, 2, /* prae div */ (void *)VX180_DSP_PLL_CONTROL, /* PLL */ 8 /* shift */, 6 /* bits */, 1 /* post div */ ); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_cpu: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)VX180_PROC_PLL_CONTROL, 8 /* shift */, 6 /* bits */, 2 ); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_ephy: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)40, /* fester Wert */ 0 /* shift */, 0 /* bits */, 8 ); break; case avm_clock_id_pci: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)40, /* fester Wert */ 0 /* shift */, 0 /* bits */, 30 ); break; case avm_clock_id_pci66: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)40, /* fester Wert */ 0 /* shift */, 0 /* bits */, 15 ); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_system: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)VX180_SYSTEM_PLL_CONTROL, 8 /* shift */, 6 /* bits */, 6 ); break; case avm_clock_id_ap: clk = ikan_get_clock(avm_clock_id_system) * 2; break; case avm_clock_id_ddr: clk = ikan_compute_clock( default_ikan_lan_xtal, 1, (void *)VX180_SYSTEM_PLL_CONTROL, 8 /* shift */, 6 /* bits */, 3 ); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ clk = 100000000; break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_usb: clk = ikan_compute_clock( default_ikan_lan_xtal, 5, (void *)48 /* fester Wert 48 */, 0 /* shift */, 0 /* bits */, 20 ); break; case avm_clock_id_usb2: clk = ikan_compute_clock( default_ikan_lan_xtal, 5, (void *)48 /* fester Wert 48 */, 0 /* shift */, 0 /* bits */, 5 ); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ default: PRINTK(KERN_ERR"ikan_get_clock: unknown id=%d\n", clock_id); break; } DBG_TRC("ikan_get_clock: %s %u %cHz\n", ikan_name_clock_id(clock_id), ikan_norm_clock(clk, 0), ikan_norm_clock(clk, 1)); return clk; } #elif defined(CONFIG_FUSIV_VX185) extern struct clock_values fusiv_718x_clks; unsigned int ikan_get_clock(enum _avm_clock_id clock_id) { unsigned int clk = 0; switch(clock_id){ /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_21xx: clk = (PLL_PD1_DSP_DIV128 * default_ikan_lan_xtal) / (PLL_PD2_DSP_PD1_DIV * PLL_PD2_DSP_PD2); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_cpu: clk = (PLL_PD1_GW_DIV128 * 1 * default_ikan_lan_xtal) / (PLL_PD2_HOST_PD1_DIV * PLL_PD2_HOST_PD2); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_ephy: clk = 125000000; /* fester Wert */ break; case avm_clock_id_pci: clk = 12000000; break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_system: clk = (PLL_PD1_GW_DIV128 * default_ikan_lan_xtal) / (PLL_PD2_SYS_PD1_DIV * PLL_PD2_SYS_PD2 * 2); break; case avm_clock_id_ap: clk = ikan_get_clock(avm_clock_id_system) * 2; break; case avm_clock_id_ddr: // 2 * system clock clk = 2 * (PLL_PD1_GW_DIV128 * default_ikan_lan_xtal) / (PLL_PD2_SYS_PD1_DIV * PLL_PD2_SYS_PD2 * 2); break; case avm_clock_id_bme: clk = (PLL_PD1_BME_DIV128 * default_ikan_lan_xtal) / (PLL_PD2_BME_PD1_DIV * PLL_PD2_BME_PD2); break; case avm_clock_id_fd: clk = (PLL_PD1_BME_DIV128 * default_ikan_lan_xtal) / (PLL_PD2_FD_PD1_DIV * PLL_PD2_FD_PD2); break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ case avm_clock_id_usb: clk = 12000000; break; case avm_clock_id_usb2: clk = 48000000; break; /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ default: PRINTK(KERN_ERR"ikan_get_clock: unknown id=%d\n", clock_id); break; } DBG_TRC("ikan_get_clock: %s %u %cHz\n", ikan_name_clock_id(clock_id), ikan_norm_clock(clk, 0), ikan_norm_clock(clk, 1)); return clk; } #endif EXPORT_SYMBOL(ikan_get_clock);