/* * 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. * * UR8 specific setup. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_AVM_POWERMETER #include #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #define PRINTK printk /*--- #define UR8_CLK_DEBUG ---*/ #if defined(UR8_CLK_DEBUG) #define DBG_TRC PRINTK #else/*--- #if defined(UR8_CLK_DEBUG) ---*/ #define DBG_TRC(arg...) #endif/*--- #else ---*//*--- #if defined(UR8_CLK_DEBUG) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static spinlock_t ur8_clock_spinlock; #define default_ur8_lan_xtal 24000000 const unsigned int ur8_lan_xtal = default_ur8_lan_xtal; #if defined(CONFIG_UR8_CLOCK_SWITCH) struct _ur8_clk_notify_system_clock_change { struct _ur8_clk_notify_system_clock_change *next; enum _avm_clock_id clock_id; unsigned int (*notify)(enum _avm_clock_id, unsigned int); }; static spinlock_t ur8_clock_switch_spinlock; static struct _ur8_clk_notify_system_clock_change *ur8_system_clock_notify_first; static struct proc_dir_entry *ur8_change_clock_entry; #define MAX_CLK_LISTENTRY 10 static struct _clk_listentry { volatile unsigned int locked; struct _ur8_clk_notify_system_clock_change entry; } clk_listentry[MAX_CLK_LISTENTRY]; /*--------------------------------------------------------------------------------*\ * kmalloc darf hier nicht verwendet werden (zu frueh!) \*--------------------------------------------------------------------------------*/ static struct _ur8_clk_notify_system_clock_change *alloc_clk_listentry(void) { unsigned int i; long flags; spin_lock_irqsave(&ur8_clock_spinlock, flags); for(i = 0; i < MAX_CLK_LISTENTRY; i++) { if(clk_listentry[i].locked == 0) { clk_listentry[i].locked = 1; spin_unlock_irqrestore(&ur8_clock_spinlock, flags); /*--- PRINTK("[clk]alloc_clk_listentry: %d\n", i); ---*/ return &clk_listentry[i].entry; } } spin_unlock_irqrestore(&ur8_clock_spinlock, flags); PRINTK("[clk]alloc_clk_listentry: failed!\n"); return NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void free_clk_listentry(struct _ur8_clk_notify_system_clock_change *entry) { unsigned int i; long flags; spin_lock_irqsave(&ur8_clock_spinlock, flags); for(i = 0; i < MAX_CLK_LISTENTRY; i++) { if(clk_listentry[i].locked && (&clk_listentry[i].entry == entry)) { clk_listentry[i].locked = 0; /*--- PRINTK("[clk]free_clk_listentry: %d\n", i); ---*/ spin_unlock_irqrestore(&ur8_clock_spinlock, flags); return; } } spin_unlock_irqrestore(&ur8_clock_spinlock, flags); PRINTK("[clk]free_clk_listentry: failed!\n"); return; } #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init ur8_clk_switch_init(void); static unsigned int ur8_call_clock_notifier(enum _avm_clock_id clock_id); static unsigned int ur8_set_mips_clock(unsigned int clk); static unsigned int ur8_set_system_clock(unsigned int clk); static unsigned int ur8_set_usb_clock(unsigned int clk); static unsigned int ur8_set_peripheral_clock(unsigned int clk); static unsigned int ur8_set_dsp_clock(unsigned int clk); static unsigned int ur8_trigger_notifier(enum _avm_clock_id clock_id); static unsigned int ur8_set_c55x_clock(unsigned int clk); static unsigned int ur8_set_vlynq_clock(unsigned int clk); static unsigned int ur8_set_tdm_clock(unsigned int clk); static unsigned int ur8_get_mips_clock(void); static unsigned int ur8_get_usb_clock(void); static unsigned int ur8_get_ephy_clock(void); static unsigned int ur8_get_pci_clock(void); static unsigned int ur8_get_tdm_clock(void); static unsigned int ur8_get_dsp_c55_system_clock(enum _avm_clock_id clock_id); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _valid_clk { unsigned int pre_div; unsigned int mult; unsigned int freq; unsigned int startup; }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _valid_dep_clk { unsigned int pll1_freq; unsigned int pll2_freq; unsigned int pll3_freq; unsigned int freq; /*--- Zielfrequenz ---*/ unsigned int mipsasync; /*--- fest durch Hw vorgegeben ---*/ unsigned int bootcfg; /*--- fest durch Hw vorgegeben ---*/ unsigned int startup; }; struct _valid_dep_clk valid_system_clk[] = { { pll1_freq: 120000000, freq: 60000000, mipsasync: 0, bootcfg: 1}, { pll1_freq: 216000000, freq: 108000000, mipsasync: 0, bootcfg: 1}, { startup: 1, pll1_freq: 240000000, freq: 120000000, mipsasync: 0, bootcfg: 1}, { pll1_freq: 264000000, freq: 132000000, mipsasync: 0, bootcfg: 1}, { pll1_freq: 300000000, freq: 150000000, mipsasync: 0, bootcfg: 1}, /*--- { pll1_freq: 120000000, freq: 400000000, mipsasync: 0, bootcfg: 2}, ---*/ /*--- { pll1_freq: 180000000, freq: 60000000, mipsasync: 0, bootcfg: 2}, ---*/ /*--- { startup: 1, pll1_freq: 360000000, freq: 120000000, mipsasync: 0, bootcfg: 2}, ---*/ /*--- { startup: 1, pll1_freq: 360000000, freq: 90000000, mipsasync: 0, bootcfg: 3}, ---*/ { 0, 0, 0, 0, 0, 0, 0 } }; struct _valid_dep_clk valid_dsp_clk[] = { { pll1_freq: 120000000, freq: 120000000, mipsasync: 0}, { startup: 1, pll1_freq: 216000000, freq: 216000000, mipsasync: 0}, { pll1_freq: 240000000, freq: 240000000, mipsasync: 0}, { pll1_freq: 264000000, freq: 264000000, mipsasync: 0}, { pll1_freq: 300000000, freq: 300000000, mipsasync: 0}, /*--- { startup: 1, pll1_freq: 360000000, freq:360000000, mipsasync: 0}, ---*/ /*--- { pll1_freq: 180000000, freq:180000000, mipsasync: 0}, ---*/ /*--- { pll1_freq: 120000000, freq:120000000, mipsasync: 0}, ---*/ { 0, 0, 0, 0, 0, 0, 0 } }; struct _valid_clk valid_mips_clk[] = { { freq: default_ur8_lan_xtal * 5, pre_div: 2, mult:14}, /* 120 MHz */ { freq: default_ur8_lan_xtal * 9, pre_div: 0, mult:8}, /* 216 MHz */ { freq: default_ur8_lan_xtal * 10, pre_div: 0, mult:9}, /* 240 MHz */ { freq: (default_ur8_lan_xtal * 22) / 2, pre_div: 1, mult:21}, /* 264 MHz */ { freq: (default_ur8_lan_xtal * 25) / 2, pre_div: 1, mult:24}, /* 300 MHz */ { 0, 0, 0, 0 } }; struct _valid_clk valid_tdm_clk[] = { { freq: 1024000, pre_div: 2, mult:15}, { startup: 1, freq: 2048000, pre_div: 2, mult:31}, { 0, 0, 0, 0 } }; #if defined(UR8_CLK_DEBUG) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const char *ur8_name_clock_id(enum _avm_clock_id clock_id) { return 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_dsp ? "id_dsp" : clock_id == avm_clock_id_vbus ? "id_vbus" : clock_id == avm_clock_id_peripheral ? "id_peripheral" : clock_id == avm_clock_id_c55x ? "id_c55x" : clock_id == avm_clock_id_ephy ? "id_ephy" : clock_id == avm_clock_id_pci ? "id_pci" : clock_id == avm_clock_id_tdm ? "id_tdm" : "unkown"; } #endif/*--- #if defined(UR8_CLK_DEBUG) ---*/ /*--------------------------------------------------------------------------------*\ * flag = 0: liefere NormFrequenz sonst 'K', 'M', ' ' \*--------------------------------------------------------------------------------*/ static inline unsigned int ur8_norm_clock(unsigned int clk, int flag) { if(flag == 0) { return ((clk / 1000000) * 1000000) == clk ? clk / 1000000 : ((clk / 1000) * 1000) == clk ? clk / 1000 : clk; } return ((clk / 1000000) * 1000000) == clk ? 'M': ((clk / 1000) * 1000) == clk ? 'K' : ' '; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_clk_get_pll_factor(struct _ur8_clock_pll *pll, unsigned int count) { long flags; DBG_TRC("ur8_clk_get_pll_factor: %p pll->Bits.pll_disable=%d nobypass=%d hfenable=%d prediv=%d mul=%d\n", pll, pll->PLL_ctrl.Bits.pll_disable, pll->PLL_ctrl.Bits.nobypass, pll->PLL_ctrl.Bits.half_enable, pll->PLL_ctrl.Bits.prediv, pll->PLL_ctrl.Bits.mult); spin_lock_irqsave(&ur8_clock_spinlock, flags); if(pll->PLL_ctrl.Bits.pll_disable) { /*--- PLL disabled. The output may high or low but now switching ---*/ spin_unlock_irqrestore(&ur8_clock_spinlock, flags); return 0; } if(pll->PLL_ctrl.Bits.pwr_dwn) { /*--- PLL in powerdown ?? bypassed clock ??? or what ---*/ spin_unlock_irqrestore(&ur8_clock_spinlock, flags); return count; } if(pll->PLL_ctrl.Bits.nobypass == 0) { /*--- PLL bypassed ---*/ spin_unlock_irqrestore(&ur8_clock_spinlock, flags); return count; } count = (count / (pll->PLL_ctrl.Bits.prediv + 1)) * (pll->PLL_ctrl.Bits.mult + 1); if(pll->PLL_ctrl.Bits.half_enable) { /*--- auch bei Bypass ?? ---*/ count /= 2; } spin_unlock_irqrestore(&ur8_clock_spinlock, flags); DBG_TRC("ur8_clk_get_pll_factor: %d\n", count); return count; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ur8_clk_set_pll(struct _ur8_clock_pll *Pll, unsigned int enable, unsigned int mult, unsigned int pre_div) { long flags; DBG_TRC("ur8_clk_set_pll: %p enable=%d mult=%d pre_div=%d\n", Pll, enable, mult, pre_div); spin_lock_irqsave(&ur8_clock_spinlock, flags); if(enable == 0) { Pll->PLL_ctrl.Bits.pll_disable = 1; spin_unlock_irqrestore(&ur8_clock_spinlock, flags); return; } Pll->PLL_ctrl.Bits.pll_disable = 0; Pll->PLL_ctrl.Bits.ext_bypass = 1; Pll->PLL_ctrl.Bits.unreset = 0; Pll->PLL_ctrl.Bits.nobypass = 0; udelay(1); Pll->PLL_ctrl.Bits.mult = mult; Pll->PLL_ctrl.Bits.prediv = pre_div; Pll->PLL_ctrl.Bits.nobypass = 1; udelay(1); Pll->PLL_ctrl.Bits.unreset = 1; udelay(5); Pll->PLL_ctrl.Bits.ext_bypass = 0; spin_unlock_irqrestore(&ur8_clock_spinlock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int ur8_change_clock_read(char* buf, char **start, off_t offset, int count, int *eof, void *data) { unsigned int mips_clk = ur8_get_clock(avm_clock_id_cpu); unsigned int system_clk = ur8_get_clock(avm_clock_id_system); unsigned int dsp_clk = ur8_get_clock(avm_clock_id_dsp); unsigned int c55x_clk = ur8_get_clock(avm_clock_id_c55x); unsigned int tdm_clk = ur8_get_clock(avm_clock_id_tdm); unsigned int vbus_clk = ur8_get_clock(avm_clock_id_vbus); unsigned int len; sprintf(buf, "UR8 Clock: CPU: %u %cHz System: %u %cHz DSP: %u %cHz C55x: %u %cHz TDM: %u %cHz VBUS: %u %cHz\n", ur8_norm_clock(mips_clk, 0), ur8_norm_clock(mips_clk, 1), ur8_norm_clock(system_clk, 0), ur8_norm_clock(system_clk, 1), ur8_norm_clock(dsp_clk, 0), ur8_norm_clock(dsp_clk, 1), ur8_norm_clock(c55x_clk, 0), ur8_norm_clock(c55x_clk, 1), ur8_norm_clock(tdm_clk, 0), ur8_norm_clock(tdm_clk, 1), ur8_norm_clock(vbus_clk, 0), ur8_norm_clock(vbus_clk, 1)); len = strlen(buf); return len; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void ur8_printvalid_clock(enum _avm_clock_id clock_id){ switch(clock_id) { case avm_clock_id_cpu: ur8_set_mips_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; case avm_clock_id_system: ur8_set_system_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; case avm_clock_id_dsp: ur8_set_dsp_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; case avm_clock_id_c55x: ur8_set_c55x_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; case avm_clock_id_vbus: ur8_set_vlynq_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; case avm_clock_id_tdm: ur8_set_tdm_clock(0); /*--- clk == 0: printe gueltige Werte ---*/ break; default: break; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _clock_eval { const char *Name; unsigned int NameSize; enum _avm_clock_id clock_id; } clock_eval[] = { { "CPU:", sizeof("CPU:") - 1, avm_clock_id_cpu}, { "System:", sizeof("System:") - 1, avm_clock_id_system}, { "DSP:", sizeof("DSP:") - 1, avm_clock_id_dsp}, { "C55x:", sizeof("C55x:") - 1, avm_clock_id_c55x}, { "TDM:", sizeof("TDM:") - 1, avm_clock_id_tdm}, { "VBUS:", sizeof("VBUS:") - 1, avm_clock_id_vbus}, { NULL, 0, avm_clock_id_non} }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int ur8_change_clock_write(struct file *file, const char __user *buff, unsigned long count, void *data) { struct _clock_eval *ce = clock_eval; unsigned int clk = 1; char *p; if(strstr(buff, "kHz")) clk = 1000; if(strstr(buff, "KHz")) clk = 1000; if(strstr(buff, "mHz")) clk = 1000 * 1000; if(strstr(buff, "MHz")) clk = 1000 * 1000; if(clk) { DBG_TRC("[ur8_change_clock_write] faktor is %u\n", clk); } while(ce->Name) { if((p = strstr(buff, ce->Name))) { p += ce->NameSize; while(*p && (*p == ' ' || *p == '\t')) p++; clk *= simple_strtol(p, NULL, 0); if(ur8_set_clock(ce->clock_id, clk)) { PRINTK(KERN_ERR" failed - valid values are: "); ur8_printvalid_clock(ce->clock_id); PRINTK("\n"); } else { PRINTK(KERN_ERR" success\n"); } break; } ce++; } return count; } #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init ur8_clk_init(void) { struct _valid_dep_clk *pval = valid_system_clk; struct _valid_clk *pval2 = valid_tdm_clk; struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; int mips_async = BOOT->hw_boot_config.Bits.mips_async; int bootcfg = BOOT->hw_boot_config.Bits.clk_ratio; DBG_TRC("ur8_clk_init()\n"); #if defined(CONFIG_UR8_CLOCK_SWITCH) spin_lock_init(&ur8_clock_switch_spinlock); ur8_system_clock_notify_first = NULL; #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ spin_lock_init(&ur8_clock_spinlock); while(pval->freq) { if((pval->startup) && (pval->bootcfg == bootcfg) && (pval->mipsasync == mips_async)){ ur8_set_clock(avm_clock_id_system, pval->freq); break; } pval++; } while(pval2->freq) { if(pval2->startup){ ur8_set_clock(avm_clock_id_tdm, pval2->freq); break; } pval2++; } ur8_set_clock(avm_clock_id_vbus, pval->freq / 2); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) static int __init ur8_clk_switch_init(void) { ur8_change_clock_entry = create_proc_entry("clocks", 06666, NULL); if(ur8_change_clock_entry) { ur8_change_clock_entry->read_proc = ur8_change_clock_read; ur8_change_clock_entry->write_proc = ur8_change_clock_write; ur8_change_clock_entry->data = NULL; } return 0; }; late_initcall(ur8_clk_switch_init); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_set_clock_notify(enum _avm_clock_id clock_id, unsigned int (*notify)(enum _avm_clock_id, unsigned int new_clk), int set_function) { struct _ur8_clk_notify_system_clock_change *aktuell = ur8_system_clock_notify_first; struct _ur8_clk_notify_system_clock_change *N; int flags; #if defined(UR8_CLK_DEBUG) DBG_TRC("[ur8_set_clock_notify] %s notify %s 0x%p ", ur8_name_clock_id(clock_id), set_function ? "enable" : "disable", notify); __print_symbol("0x%x\n", (unsigned long) notify); #endif /*--- #if defined(UR8_CLK_DEBUG) ---*/ if(set_function) { N = alloc_clk_listentry(); if(N == NULL) return -1; spin_lock_irqsave(&ur8_clock_switch_spinlock, flags); N->next = NULL; N->clock_id = clock_id; N->notify = notify; /*----------------------------------------------------------------------------------*\ * nicht der erste, es gibt schon einen \*----------------------------------------------------------------------------------*/ if(ur8_system_clock_notify_first) { while(aktuell->next) { aktuell = aktuell->next; } aktuell->next = N; } else { ur8_system_clock_notify_first = N; } spin_unlock_irqrestore(&ur8_clock_switch_spinlock, flags); return 0; } else { /*----------------------------------------------------------------------------------*\ * der erste und einzige \*----------------------------------------------------------------------------------*/ spin_lock_irqsave(&ur8_clock_switch_spinlock, flags); if(ur8_system_clock_notify_first == NULL) { spin_unlock_irqrestore(&ur8_clock_switch_spinlock, flags); return -1; } if((ur8_system_clock_notify_first->notify == notify) && (ur8_system_clock_notify_first->clock_id == clock_id)) { N = ur8_system_clock_notify_first; ur8_system_clock_notify_first = ur8_system_clock_notify_first->next; spin_unlock_irqrestore(&ur8_clock_switch_spinlock, flags); free_clk_listentry(N); return 0; } /*----------------------------------------------------------------------------------*\ * der erste ist es nicht, gibt es noch weitere \*----------------------------------------------------------------------------------*/ while(aktuell->next) { if(aktuell->next->notify == notify) { N = aktuell->next; aktuell->next = aktuell->next->next; spin_unlock_irqrestore(&ur8_clock_switch_spinlock, flags); free_clk_listentry(N); return 0; } aktuell = aktuell->next; } spin_unlock_irqrestore(&ur8_clock_switch_spinlock, flags); } return -1; return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_call_clock_notifier(enum _avm_clock_id clock_id) { struct _ur8_clk_notify_system_clock_change *aktuell = ur8_system_clock_notify_first; unsigned int clk = ur8_get_clock(clock_id); unsigned int ret = 0; while(aktuell) { if((aktuell->notify) && (aktuell->clock_id == clock_id)) { #if defined(UR8_CLK_DEBUG) DBG_TRC("[ur8_call_clock_notifier] call 0x%p ", aktuell->notify); __print_symbol("0x%x\n", (unsigned long) aktuell->notify); #endif /*--- #if defined(UR8_CLK_DEBUG) ---*/ ret |= (aktuell->notify)(clock_id, clk); } aktuell = aktuell->next; } return ret; } #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ /*--------------------------------------------------------------------------------*\ * triggert alle notifier entsprechend clock_id-Mask \*--------------------------------------------------------------------------------*/ static unsigned int ur8_trigger_notifier(enum _avm_clock_id clock_idmask){ int ret = 0; if(clock_idmask & avm_clock_id_cpu) { #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_cpuclock, FREQUENZ_TO_PERCENT(ur8_get_clock(avm_clock_id_cpu), 240000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) ret |= ur8_call_clock_notifier(avm_clock_id_cpu); #endif/*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ } if(clock_idmask & avm_clock_id_system) { #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_systemclock, FREQUENZ_TO_PERCENT(ur8_get_clock(avm_clock_id_system), 120000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) ret |= ur8_call_clock_notifier(avm_clock_id_system); #endif/*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ } if(clock_idmask & avm_clock_id_dsp) { #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_dspclock, FREQUENZ_TO_PERCENT(ur8_get_clock(avm_clock_id_dsp), 240000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) ret |= ur8_call_clock_notifier(avm_clock_id_dsp); #endif/*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ } if(clock_idmask & avm_clock_id_usb) { } if(clock_idmask & avm_clock_id_vbus) { } if(clock_idmask & avm_clock_id_peripheral) { #if defined(CONFIG_UR8_CLOCK_SWITCH) ret |= ur8_call_clock_notifier(avm_clock_id_peripheral); #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ } if(clock_idmask & avm_clock_id_c55x) { #if defined(CONFIG_UR8_CLOCK_SWITCH) ret |= ur8_call_clock_notifier(avm_clock_id_c55x); #endif/*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ } if(clock_idmask & avm_clock_id_ephy) { } if(clock_idmask & avm_clock_id_pci) { } if(clock_idmask & avm_clock_id_tdm) { } return ret; } /*------------------------------------------------------------------------------------------*\ Returnwert: Mask mit allen Notifieren: 0 -> Fehler! clk == 0: printen aller gueltigen Frequenzen \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_set_mips_clock(unsigned int clk) { struct _valid_clk *pval = valid_mips_clk; struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); int mips_async = BOOT->hw_boot_config.Bits.mips_async; while(pval->freq) { if(clk == 0) { PRINTK("%u %cHZ ", ur8_norm_clock(pval->freq, 0), ur8_norm_clock(pval->freq, 1)); } else if((pval->freq == clk)){ break; } pval++; } if(pval->freq == 0) { if(clk) PRINTK(KERN_ERR"ur8_set_mips_clock %d failed\n", clk); return 0; } ur8_clk_set_pll(&CLOCK->PLL1, 1, pval->mult, pval->pre_div); if(mips_async) { return avm_clock_id_cpu; } return avm_clock_id_cpu | avm_clock_id_system | avm_clock_id_dsp | avm_clock_id_pci | avm_clock_id_ephy | avm_clock_id_c55x | avm_clock_id_peripheral | avm_clock_id_vbus; } /*------------------------------------------------------------------------------------------*\ Returnwert: Mask mit allen Notifieren: 0 -> Fehler! clk == 0: printen aller gueltigen Frequenzen \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_set_system_clock(unsigned int clk) { struct _valid_dep_clk *pval = valid_system_clk; struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; int mips_async = BOOT->hw_boot_config.Bits.mips_async; int bootcfg = BOOT->hw_boot_config.Bits.clk_ratio; while(pval->freq) { if((pval->bootcfg == bootcfg) && (pval->mipsasync == mips_async)){ if(clk == 0) { PRINTK("%u %cHZ ", ur8_norm_clock(pval->freq, 0), ur8_norm_clock(pval->freq, 1)); } else if(pval->freq == clk){ break; } } pval++; } if(pval->freq == 0) { if(clk)PRINTK(KERN_ERR"ur8_set_system_clock %d failed\n", clk); return 0; } if(pval->mipsasync) { PRINTK(KERN_ERR"ur8_set_system_clock: async mips %d todo\n", clk); return 0; } if(ur8_set_mips_clock(clk * (bootcfg + 1)) == 0) { PRINTK(KERN_ERR"ur8_set_system_clock: error to set system-clock: %d todo\n", clk); return 0; } return avm_clock_id_cpu | avm_clock_id_system | avm_clock_id_dsp | avm_clock_id_pci | avm_clock_id_ephy | avm_clock_id_c55x | avm_clock_id_peripheral | avm_clock_id_vbus; } /*------------------------------------------------------------------------------------------*\ Returnwert: Mask mit allen Notifieren: 0 -> Fehler! \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_set_usb_clock(unsigned int clk) { if(clk != default_ur8_lan_xtal) { return 0; } return avm_clock_id_usb; } /*------------------------------------------------------------------------------------------*\ Returnwert: Mask mit allen Notifieren: 0 -> Fehler! \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_set_pci_clock(unsigned int clk) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); CLOCK->CLOCK_CFG.Bits.pci_mux_sel = 0; // 300MHz PLL2 ur8_clk_set_pll(&CLOCK->PLL2, 1, 24, 1); prom_printf("ur8_clk_get_pll_factor: %u\n", ur8_clk_get_pll_factor(&CLOCK->PLL2, default_ur8_lan_xtal)); CLOCK->CLOCK_CFG.Bits.freeze_div_by = 1; CLOCK->CLOCK_CFG.Bits.pci_clk_dir = 0; // Output from TNETD7531 CLOCK->CLOCK_CFG.Bits.pci_div_sel = 8; CLOCK->CLOCK_CFG.Bits.freeze_div_by = 0; prom_printf("ur8_get_pci_clock %u\n", ur8_get_pci_clock()); return ur8_get_pci_clock(); } /*------------------------------------------------------------------------------------------*\ Returnwert: Mask mit allen notifieren: 0 -> Fehler! clk == 0: printen aller gueltigen Frequenzen \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_set_dsp_clock(unsigned int clk) { struct _valid_dep_clk *pval = valid_dsp_clk; struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; int mips_async = BOOT->hw_boot_config.Bits.mips_async; while(pval->freq) { if(pval->mipsasync == mips_async) { if(clk == 0) { PRINTK("ur8_norm_clock: %u %cHZ\n", ur8_norm_clock(pval->freq, 0), ur8_norm_clock(pval->freq, 1)); } else if(pval->freq == clk){ break; } } pval++; } if(pval->freq == 0) { if(clk) PRINTK(KERN_ERR "ur8_set_dsp_clock %d failed\n", clk); return 0; } if(pval->mipsasync) { PRINTK(KERN_ERR"ur8_set_dsp_clock: async mips %d todo\n", clk); return 0; } /*--- wird von PLL1 abgeleitet: MIPS-Clock aendern ---*/ if(ur8_set_mips_clock(clk) == 0) { return 0; } return avm_clock_id_cpu | avm_clock_id_system | avm_clock_id_dsp | avm_clock_id_pci | avm_clock_id_ephy | avm_clock_id_c55x | avm_clock_id_peripheral | avm_clock_id_vbus; } /*--------------------------------------------------------------------------------*\ Returnwert: Mask mit allen notifieren: 0 -> Fehler! clk == 0: printen aller gueltigen Frequenzen \*--------------------------------------------------------------------------------*/ static unsigned int ur8_set_tdm_clock(unsigned int clk) { struct _valid_clk *pval = valid_tdm_clk; struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; if(BOOT->hw_boot_config.Bits.mips_async) { PRINTK(KERN_ERR"ur8_set_tdm_clock %d async todo\n", clk); return 0; } while(pval->freq) { if(clk == 0) { PRINTK("%u %cHZ ", ur8_norm_clock(pval->freq, 0), ur8_norm_clock(pval->freq, 1)); } else if((pval->freq == clk)){ break; } pval++; } if(pval->freq == 0) { if(clk) PRINTK(KERN_ERR"ur8_set_tdm_clock %d failed\n", clk); return 0; } /*--- derived from PLL3 divided by 125 ---*/ ur8_clk_set_pll(&CLOCK->PLL3, 1, pval->mult, pval->pre_div); return avm_clock_id_tdm; } /*--------------------------------------------------------------------------------*\ * aendert nur den Divider im Clk-Config ! clk == 0: printen aller gueltigen Frequenzen \*--------------------------------------------------------------------------------*/ static unsigned int ur8_set_c55x_clock(unsigned int clk) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; struct _ur8_clock_pll *pll; unsigned int refclk, c55ss_div_sel; if(BOOT->hw_boot_config.Bits.mips_async) { /*--- MIPS asynchron: PLL2 oder PLL3 ---*/ pll = CLOCK->CLOCK_CFG.Bits.sys_asyc_clk_sel ? &CLOCK->PLL3 : &CLOCK->PLL2; } else { /*--- MIPS synchron: nur PLL1 moeglich ---*/ pll = &CLOCK->PLL1; } refclk = ur8_clk_get_pll_factor(pll, default_ur8_lan_xtal); for(c55ss_div_sel = 1; c55ss_div_sel < 8; c55ss_div_sel++) { if(clk == 0) { PRINTK("%u %cHZ ", ur8_norm_clock(refclk / c55ss_div_sel, 0), ur8_norm_clock(refclk / c55ss_div_sel, 1)); } else if(refclk / c55ss_div_sel == clk) { CLOCK->CLOCK_CFG.Bits.c55ss_div_sel= c55ss_div_sel- 1; return avm_clock_id_c55x; } } return 0; } /*--------------------------------------------------------------------------------*\ * aendert nur den Divider im Clk-Config ! clk == 0: printen aller gueltigen Frequenzen \*--------------------------------------------------------------------------------*/ static unsigned int ur8_set_vlynq_clock(unsigned int clk) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; unsigned int refclk, vlynq_div_sel; if(BOOT->hw_boot_config.Bits.mips_async) { refclk = ur8_clk_get_pll_factor(&CLOCK->PLL2, default_ur8_lan_xtal); /*--- MIPS asynchron: PLL2 ---*/ } else { refclk = ur8_clk_get_pll_factor(&CLOCK->PLL1, default_ur8_lan_xtal); /*--- MIPS synchron: PLL1 ---*/ } for(vlynq_div_sel = 1; vlynq_div_sel < 8; vlynq_div_sel++) { if(clk == 0) { PRINTK("%u %cHZ ", ur8_norm_clock(refclk / vlynq_div_sel, 0), ur8_norm_clock(refclk / vlynq_div_sel, 1)); } else { if(refclk / vlynq_div_sel == clk) { CLOCK->CLOCK_CFG.Bits.vlynq_div_sel = vlynq_div_sel - 1; return avm_clock_id_vbus; } } } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int ur8_set_peripheral_clock(unsigned int clk) { return ur8_set_system_clock(clk / 2); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_mips_clock(void) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); #if defined(UR8_CLK_DEBUG) struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; #endif/*--- #if defined(UR8_CLK_DEBUG) ---*/ unsigned clk; /*--- MIPS-Clock comes only from PLL1 ---*/ clk = ur8_clk_get_pll_factor(&CLOCK->PLL1, default_ur8_lan_xtal); DBG_TRC("ur8_get_mips_clock: %d %s\n", clk, BOOT->hw_boot_config.Bits.mips_async ? "async" : "sync"); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_dsp_c55_system_clock(enum _avm_clock_id clock_id) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; struct _ur8_clock_pll *pll; unsigned int clk; if(BOOT->hw_boot_config.Bits.mips_async) { /*--- MIPS asynchron: PLL2 oder PLL3 ---*/ pll = CLOCK->CLOCK_CFG.Bits.sys_asyc_clk_sel ? &CLOCK->PLL3 : &CLOCK->PLL2; } else { /*--- MIPS synchron: nur PLL1 moeglich ---*/ pll = &CLOCK->PLL1; } clk = ur8_clk_get_pll_factor(pll, default_ur8_lan_xtal); switch(clock_id) { case avm_clock_id_peripheral: clk /= BOOT->hw_boot_config.Bits.clk_ratio + 1; clk /= 2; break; case avm_clock_id_system: clk /= BOOT->hw_boot_config.Bits.clk_ratio + 1; break; case avm_clock_id_dsp: /*--- no change ---*/ break; case avm_clock_id_c55x: clk /= CLOCK->CLOCK_CFG.Bits.c55ss_div_sel + 1; break; default: PRINTK(KERN_ERR"ur8_get_dsp_c55_system_clock: unknown id\n"); break; } return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_usb_clock(void) { /*--- only xtal MHz possible ---*/ return default_ur8_lan_xtal; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_ephy_clock(void) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); unsigned int clk; /*--- derived from PLL2 ---*/ clk = ur8_clk_get_pll_factor(&CLOCK->PLL2, default_ur8_lan_xtal); clk /= CLOCK->CLOCK_CFG.Bits.ephy_div_sel + 1; DBG_TRC("ur8_get_ephy_clock: %d\n", clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_pci_clock(void) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _ur8_clock_pll *pll; unsigned int clk; if(CLOCK->CLOCK_CFG.Bits.pci_clk_dir) { /*--- is input to tnetd7531 ---*/ PRINTK(KERN_ERR"ur8_get_pci_clock: is input\n"); return 0; } pll = CLOCK->CLOCK_CFG.Bits.pci_mux_sel ? &CLOCK->PLL2 : &CLOCK->PLL1; clk = ur8_clk_get_pll_factor(pll, default_ur8_lan_xtal); clk /= CLOCK->CLOCK_CFG.Bits.pci_div_sel + 1; DBG_TRC("ur8_get_pci_clock: %d\n", clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ur8_get_tdm_clock(void) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); /*--- derived from PLL3 divided by 125 ---*/ unsigned int clk = ur8_clk_get_pll_factor(&CLOCK->PLL3, default_ur8_lan_xtal) / 125; DBG_TRC("ur8_get_tdm_clock: %d\n", clk); return clk; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int ur8_get_vlynq_clock(void) { struct _hw_clock *const CLOCK = (struct _hw_clock *)&(*(volatile unsigned int *)(UR8_CLOCK_BASE)); struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; struct _ur8_clock_pll *pll; unsigned int clk; if(BOOT->hw_boot_config.Bits.mips_async) { pll = &CLOCK->PLL2; /*--- MIPS asynchron: PLL2 ---*/ } else { pll = &CLOCK->PLL1; /*--- MIPS synchron: PLL1 ---*/ } clk = ur8_clk_get_pll_factor(pll, default_ur8_lan_xtal); clk /= CLOCK->CLOCK_CFG.Bits.vlynq_div_sel + 1; DBG_TRC("ur8_get_vlynq_clock: divsel=%d clock=%d\n", CLOCK->CLOCK_CFG.Bits.vlynq_div_sel, clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_get_clock(enum _avm_clock_id clock_id) { #if defined(UR8_CLK_DEBUG) struct _hw_boot *BOOT = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE; #endif/*--- #if defined(UR8_CLK_DEBUG) ---*/ unsigned int clk = 0; switch(clock_id) { case avm_clock_id_cpu: clk = ur8_get_mips_clock(); break; case avm_clock_id_c55x: case avm_clock_id_system: case avm_clock_id_dsp: case avm_clock_id_peripheral: clk = ur8_get_dsp_c55_system_clock(clock_id); break; case avm_clock_id_vbus: clk = ur8_get_vlynq_clock(); break; case avm_clock_id_usb: clk = ur8_get_usb_clock(); break; case avm_clock_id_ephy: clk = ur8_get_ephy_clock(); break; case avm_clock_id_pci: clk = ur8_get_pci_clock(); break; case avm_clock_id_tdm: clk = ur8_get_tdm_clock(); break; default: PRINTK(KERN_ERR"ur8_get_clock: unknown id=%d\n", clock_id); break; } DBG_TRC("ur8_get_clock: %s %u %cHz %s\n", ur8_name_clock_id(clock_id), ur8_norm_clock(clk, 0), ur8_norm_clock(clk, 1), BOOT->hw_boot_config.Bits.mips_async ? "async" : "sync"); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ur8_set_clock(enum _avm_clock_id clock_id, unsigned int clk) { int mask = 0, ret = 1; if(clk == 0) { return ret; } switch(clock_id) { case avm_clock_id_cpu: mask = ur8_set_mips_clock(clk); break; case avm_clock_id_dsp: mask = ur8_set_dsp_clock(clk); break; case avm_clock_id_system: mask = ur8_set_system_clock(clk); break; case avm_clock_id_c55x: mask = ur8_set_c55x_clock(clk); break; case avm_clock_id_usb: mask = ur8_set_usb_clock(clk); break; case avm_clock_id_peripheral: mask = ur8_set_peripheral_clock(clk); break; case avm_clock_id_tdm: mask = ur8_set_tdm_clock(clk); break; case avm_clock_id_vbus: mask = ur8_set_vlynq_clock(clk); break; case avm_clock_id_ephy: PRINTK(KERN_ERR"ur8_set_clock: id=%d clk-change todo\n", clock_id); break; case avm_clock_id_pci: mask = ur8_set_pci_clock(clk); break; default: PRINTK(KERN_ERR"ur8_get_clock: unknown id\n"); break; } if(mask) { ret = ur8_trigger_notifier(mask); } DBG_TRC("ur8_set_clock: %s %u %cHz -> %s\n", ur8_name_clock_id(clock_id), ur8_norm_clock(clk, 0), ur8_norm_clock(clk, 1), ret ? "failed" : "ok"); return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_UR8_CLOCK_SWITCH) unsigned int ur8_get_clock_notify(enum _avm_clock_id clock_id, unsigned int (*handler)(enum _avm_clock_id, unsigned int new_clk)) { ur8_set_clock_notify(clock_id, handler, 0); /*--- deinstall ---*/ ur8_set_clock_notify(clock_id, handler, 1); /*--- install ---*/ return ur8_get_clock(clock_id); } EXPORT_SYMBOL(ur8_get_clock_notify); #endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ EXPORT_SYMBOL(ur8_get_clock); EXPORT_SYMBOL(ur8_set_clock);