/* * 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. * * OHIO 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 prom_printf ---*/ #define PRINTK printk /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ /*--- #define OHIO_CLK_DEBUG ---*/ /*--- #define OHIO_CLK_SWITCH_EMULATE ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(OHIO_CLK_DEBUG) #define DEB_ERR(args...) printk(KERN_ERR args) #define DEB_WARN(args...) printk(KERN_WARNING args) #define DEB_NOTE(args...) printk(KERN_NOTICE args) #define DEB_INFO(args...) printk(KERN_INFO args) #define DEB_TRACE(args...) printk(KERN_INFO args) #else /*--- #if defined(OHIO_CLK_DEBUG) ---*/ #define DEB_ERR(args...) printk(KERN_ERR args) #define DEB_WARN(args...) #define DEB_NOTE(args...) #define DEB_INFO(args...) #define DEB_TRACE(args...) #endif /*--- #else ---*/ /*--- #if defined(OHIO_CLK_DEBUG) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static spinlock_t ohio_clock_spinlock; static unsigned int ohio_current_system_clock; #define default_ohio_lan_xtal 25000000 const unsigned int ohio_lan_xtal = default_ohio_lan_xtal; #define default_ohio_dsp_xtal 35328000 const unsigned int ohio_dsp_xtal = default_ohio_dsp_xtal; #if defined(CONFIG_OHIO_CLOCK_SWITCH) struct _ohio_clk_notify_system_clock_change { struct _ohio_clk_notify_system_clock_change *next; enum _avm_clock_id clock_id; unsigned int (*notify)(enum _avm_clock_id, unsigned int); }; static spinlock_t ohio_clock_switch_spinlock; static struct _ohio_clk_notify_system_clock_change *ohio_system_clock_notify_first; #if defined(CONFIG_PROC_FS) static struct proc_dir_entry *ohio_change_clock_entry; #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ #define MAX_CLK_LISTENTRY 10 static struct _clk_listentry { volatile unsigned int locked; struct _ohio_clk_notify_system_clock_change entry; } clk_listentry[MAX_CLK_LISTENTRY]; /*--------------------------------------------------------------------------------*\ * kmalloc darf hier nicht verwendet werden (zu frueh!) \*--------------------------------------------------------------------------------*/ static struct _ohio_clk_notify_system_clock_change *alloc_clk_listentry(void) { unsigned int i; long flags; spin_lock_irqsave(&ohio_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(&ohio_clock_spinlock, flags); /*--- DEB_ERR("[clk]alloc_clk_listentry: %d\n", i); ---*/ return &clk_listentry[i].entry; } } spin_unlock_irqrestore(&ohio_clock_spinlock, flags); DEB_ERR("[clk]alloc_clk_listentry: failed!\n"); return NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void free_clk_listentry(struct _ohio_clk_notify_system_clock_change *entry) { unsigned int i; long flags; spin_lock_irqsave(&ohio_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; /*--- DEB_ERR("[clk]free_clk_listentry: %d\n", i); ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return; } } spin_unlock_irqrestore(&ohio_clock_spinlock, flags); DEB_ERR("[clk]free_clk_listentry: failed!\n"); return; } #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_PROC_FS) static int __init ohio_clk_switch_init(void); #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ static unsigned int ohio_call_clock_notifier(enum _avm_clock_id clock_id, unsigned int clk); static unsigned int ohio_set_mips_clock(unsigned int clk, unsigned int force); static unsigned int ohio_set_system_clock(unsigned int clk, unsigned int dspclock, unsigned int force); static unsigned int ohio_set_usb_clock_notify(enum _avm_clock_id clock_id, unsigned int clk); static unsigned int ohio_set_usb_clock(unsigned int clk); static unsigned int ohio_set_dsp_clock(unsigned int clk); static unsigned int ohio_get_system_clock(void); static unsigned int ohio_get_mips_clock(void); static unsigned int ohio_get_dsp_clock(void); static unsigned int ohio_get_usb_clock(void); static unsigned int ohio_get_vlynq_clock(void); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _valid_clk { unsigned int pre_div; unsigned int mult; unsigned int post_div; unsigned int post_div2; unsigned int freq; unsigned int dspfreq; /*--- falls die DSP-Clock von diesem Takt abgeleitet wird ---*/ unsigned int startup; unsigned int prefer; /*--- preferierter Systemclock falls DSP-Clock einzustellen ---*/ }; struct _valid_clk valid_system_clk[] = { { freq: 25000000, dspfreq: 250000000, pre_div: 1, mult: 10, post_div: 10, post_div2: 1 }, { freq: 50000000, dspfreq: 250000000, pre_div: 1, mult: 10, post_div: 5, post_div2: 1 }, { freq: 75000000, dspfreq: 150000000, pre_div: 1, mult: 6, post_div: 2, post_div2: 1 }, { freq: 75000000, dspfreq: 300000000, pre_div: 1, mult: 12, post_div: 4, post_div2: 1 }, { freq: 62500000, dspfreq: 250000000, pre_div: 1, mult: 10, post_div: 4, post_div2: 1 }, { freq:100000000, dspfreq: 200000000, pre_div: 1, mult: 8, post_div: 2, post_div2: 1 }, { freq:125000000, dspfreq: 250000000, pre_div: 1, mult: 10, post_div: 2, post_div2: 1, prefer: 1, startup: 1}, { freq:137500000, dspfreq: 137500000, pre_div: 1, mult: 11, post_div: 2, post_div2: 2 }, { freq:137500000, dspfreq: 275000000, pre_div: 1, mult: 11, post_div: 2, post_div2: 1 }, { freq:150000000, dspfreq: 150000000, pre_div: 1, mult: 12, post_div: 2, post_div2: 2,}, { freq:150000000, dspfreq: 300000000, pre_div: 1, mult: 12, post_div: 2, post_div2: 1, prefer: 1 }, { freq:162500000, dspfreq: 162500000, pre_div: 2, mult: 13, post_div: 1, post_div2: 1 }, { 0, 0, 0, 0 } }; struct _valid_clk valid_mips_clk_with_dsp_xtal[] = { { freq: default_ohio_dsp_xtal * 1, pre_div: 1, mult: 1, post_div: 1 }, { freq: default_ohio_dsp_xtal * 2, pre_div: 1, mult: 2, post_div: 1 }, { freq: default_ohio_dsp_xtal * 3, pre_div: 1, mult: 3, post_div: 1 }, { freq: default_ohio_dsp_xtal * 4, pre_div: 1, mult: 4, post_div: 1 }, { freq: default_ohio_dsp_xtal * 5, pre_div: 1, mult: 5, post_div: 1 }, { startup: 1, freq: default_ohio_dsp_xtal * 6, pre_div: 1, mult: 6, post_div: 1 }, /* Frequ: 211968000 Hz */ { freq: default_ohio_dsp_xtal * 7, pre_div: 1, mult: 7, post_div: 1 }, /* 247296000 Hz */ { 0, 0, 0, 0 } }; /*--- kein hoch- und runtertakten erlauben ! ---*/ struct _valid_clk valid_mips_clk_with_lan_xtal[] = { { freq: default_ohio_lan_xtal * 1, pre_div: 1, mult: 1, post_div: 1 }, /* 25 Mhz */ { freq: default_ohio_lan_xtal * 2, pre_div: 1, mult: 2, post_div: 1 }, { freq: default_ohio_lan_xtal * 3, pre_div: 1, mult: 3, post_div: 1 }, { freq: default_ohio_lan_xtal * 4, pre_div: 1, mult: 4, post_div: 1 }, /*--- { freq: default_ohio_lan_xtal * 5, pre_div: 1, mult: 5, post_div: 1 }, ---*/ { freq: default_ohio_lan_xtal * 6, pre_div: 1, mult: 6, post_div: 1 }, /* 150 MHz */ { freq: default_ohio_lan_xtal * 7, pre_div: 1, mult: 7, post_div: 1 }, /* 175 MHz */ { freq: default_ohio_lan_xtal * 8, pre_div: 1, mult: 8, post_div: 1 }, /* 200 MHz */ { startup: 1, freq: default_ohio_lan_xtal * 9, pre_div: 1, mult: 9, post_div: 1 }, /* 225 MHz */ /*--- { freq: default_ohio_lan_xtal * 10, pre_div: 1, mult: 10, post_div: 1 }, ---*//* 250 MHz */ { 0, 0, 0, 0 } }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ union EMIF_SDRAMTimingReg ohio_SDRAM_config[3] = { /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #ifdef USE_CL2_RAMS { /*--- 125 MHz CS2 ---*/ #define EMIF_SDRAM_TIMING_125MHZ 0 Bits: { t_rfc: 10 - 1, t_rrd: 2 - 1, t_rc: 9 - 1, t_ras: 6 - 1, t_wr: 2 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } }, #define EMIF_SDRAM_TIMING_137MHZ 1 { /*--- 137,5 MHz CS2 ---*/ Bits: { t_rfc: 11 - 1, t_rrd: 2 - 1, t_rc: 9 - 1, t_ras: 6 - 1, t_wr: 2 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } }, #define EMIF_SDRAM_TIMING_150MHZ 2 { /*--- 150 MHz CS2 ---*/ Bits: { t_rfc: 12 - 1, t_rrd: 3 - 1, t_rc: 10 - 1, t_ras: 7 - 1, t_wr: 3 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #else { /*--- 125 MHz CS2 ---*/ #define EMIF_SDRAM_TIMING_125MHZ 0 Bits: { t_rfc: 10 - 1, t_rrd: 2 - 1, t_rc: 9 - 1, t_ras: 6 - 1, t_wr: 2 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } }, #define EMIF_SDRAM_TIMING_137MHZ 1 { /*--- 137,5 MHz CS2 ---*/ Bits: { t_rfc: 11 - 1, t_rrd: 2 - 1, t_rc: 9 - 1, t_ras: 6 - 1, t_wr: 2 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } }, #define EMIF_SDRAM_TIMING_150MHZ 2 { /*--- 150 MHz CS2 ---*/ Bits: { t_rfc: 12 - 1, t_rrd: 3 - 1, t_rc: 10 - 1, t_ras: 7 - 1, t_wr: 3 - 1, t_rcd: 3 - 1, t_rp: 3 - 1 } } #endif }; /*------------------------------------------------------------------------------------------*\ * HWRevision die keinen DSP Quarz besetzen mit Space getrennt \*------------------------------------------------------------------------------------------*/ char *AVM_LAN_CLK_HWREV_LIST = " 134 105 B104 B103 "; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct _valid_clk *ohio_get_valid_mips_clk_table(void) { char *hwrev; static struct _valid_clk *V = NULL; #if defined(OHIO_CLK_DEBUG) static char *table_name = NULL; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; if(V) { #if defined(OHIO_CLK_DEBUG) DEB_ERR("[ohio_get_valid_mips_clk_table] use: %s\n", table_name); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ return V; } prom_init(); hwrev = prom_getenv("HWRevision"); #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] HWRevision=\"%s\"\n", hwrev); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ if(hwrev) { char _buff[20]; char *buff = _buff; char *p; #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] hwrev=%s\n", hwrev); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ strcpy(buff, " "); p = strstr(prom_getcmdline(), "CPU_NR="); #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] %s\n", p); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ /*----------------------------------------------------------------------------------*\ * ist 2. CPU Profi-VoIP synchron haben wir nur einen 25MHz Quarz \*----------------------------------------------------------------------------------*/ if(p && (BOOT->hw_boot_config.Bits.mips_async == 0)) { p += sizeof("CPU_NR=") - 1; _buff[1] = *p - '1' + 'A'; } strcat(buff, hwrev); strcat(buff, " "); #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] hwrev=(%s)\n", buff); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ /*----------------------------------------------------------------------------------*\ * aus "94.0.0.1 " --> "94 " erzeugen \*----------------------------------------------------------------------------------*/ p = strchr(buff, '.'); if(p) { *p++ = ' '; *p++ = '\0'; } if(strstr(AVM_LAN_CLK_HWREV_LIST, buff)) { #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] has no dsp xtal\n", hwrev); table_name = "valid_mips_clk_with_lan_xtal"; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ return V = valid_mips_clk_with_lan_xtal; } else { #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] has dsp xtal\n", hwrev); table_name = "valid_mips_clk_with_dsp_xtal"; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ return V = valid_mips_clk_with_dsp_xtal; } } #if defined(OHIO_CLK_DEBUG) prom_printf("[ohio_get_valid_mips_clk_table] no HWRevision\n"); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ return NULL; } /*------------------------------------------------------------------------------------------*\ * Ausgang Post-DIV2 \*------------------------------------------------------------------------------------------*/ unsigned int ohio_clk_get_pll_factor2(struct _ohio_clock_pll *pll, unsigned int count) { if(pll->PLL_ctrl.Bits.pllen == 1) { if(pll->PLL_pre_div.Bits.diven == 1) { count /= pll->PLL_pre_div.Bits.ratio + 1; } count *= pll->PLL_mult.Bits.pllm + 1; if(pll->PLL_post_div2.Bits.diven == 1) { count /= pll->PLL_post_div2.Bits.ratio + 1; } } return count; } /*------------------------------------------------------------------------------------------*\ * Ausgang Post-DIV1 \*------------------------------------------------------------------------------------------*/ unsigned int ohio_clk_get_pll_factor(struct _ohio_clock_pll *pll, unsigned int count) { if(pll->PLL_ctrl.Bits.pllen == 1) { if(pll->PLL_pre_div.Bits.diven == 1) { count /= pll->PLL_pre_div.Bits.ratio + 1; } count *= pll->PLL_mult.Bits.pllm + 1; if(pll->PLL_post_div.Bits.diven == 1) { count /= pll->PLL_post_div.Bits.ratio + 1; } } return count; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void ohio_wait_for_gostat(struct _ohio_clock_pll *PLL) { #if 1 unsigned int Count = 0; while(PLL->PLL_stat.Bits.gostat && (Count < 1000)) { Count++; } #else udelay(1); #endif } /*------------------------------------------------------------------------------------------*\ * Fässt nur die PLL-Werte an, die unterschiedlich sind (nur möglich für post-divider) * Setzt voraus, das PLL läuft, und kein einzelner Wert disabled/enabled werden muss * post... 0: disabled * RetVal: 0 ok (nur post_div & post_div2 anfassbar) sonst komplettes Umprogrammieren notwendig * (mult und pre setzen Auschalten der PLL vorraus) \*------------------------------------------------------------------------------------------*/ static int ohio_clk_setpost_div_by_runningpll(struct _ohio_clock_pll *PLL, unsigned int enable, unsigned int mult, unsigned int pre_div, unsigned int post_div, unsigned int post_div2) { int flags; #if defined(OHIO_CLK_SWITCH_EMULATE) struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; DEB_ERR("[ohio_clk_setpost_div_by_runningpll] ignore PLL-SWITCH %s mult=%u pre_div=%u post_div=%u post_div2=%u\n", PLL == &(ohio_clock->SYSTEM_PLL) ? "system" : PLL == &(ohio_clock->MIPS_PLL) ? "mips" : PLL == &(ohio_clock->USB_PLL) ? "usb" : "unknown", mult, pre_div, post_div, post_div2); return 0; #endif /*--- #if defined(OHIO_CLK_SWITCH_EMULATE) ---*/ spin_lock_irqsave(&ohio_clock_spinlock, flags); if((PLL->PLL_ctrl.Bits.pllen == 0) || (enable == 0)) { /*--- PLL Off bzw PLL ausschalten - komplett setzen notwendig ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return 1; } if((PLL->PLL_mult.Bits.pllm + 1) != mult) { /*--- PLL On: mult darf nicht gesetzt werden - also komplett setzen notwendig ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return 1; } if((PLL->PLL_pre_div.Bits.ratio + 1) != pre_div) { /*--- PLL On: pre_div darf nicht gesetzt werden - also komplett setzen notwendig ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return 1; } if(((post_div == 0) && PLL->PLL_post_div.Bits.diven) || ( post_div && (PLL->PLL_post_div.Bits.diven == 0)) || ((post_div2 == 0) && PLL->PLL_post_div2.Bits.diven) || ( post_div2 && (PLL->PLL_post_div2.Bits.diven == 0)) ) { /*--- sollen aus/angeschaltet werden - komplett setzen notwendig (?) ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return 1; } if(post_div && (post_div != PLL->PLL_post_div.Bits.ratio + 1)) { DEB_TRACE("ohio_clk_setpost_div_by_runningpll: reconfig pll post_div %u\n", post_div); ohio_wait_for_gostat(PLL); PLL->PLL_cmd_en.Bits.goset = 1; PLL->PLL_post_div.Bits.ratio = post_div - 1; PLL->PLL_cmd.Bits.goset = 1; } if(post_div2 && (post_div2 != PLL->PLL_post_div2.Bits.ratio + 1)) { ohio_wait_for_gostat(PLL); DEB_TRACE("ohio_clk_setpost_div_by_runningpll: reconfig pll post_div2 %u\n", post_div2); PLL->PLL_cmd_en.Bits.goset = 2; PLL->PLL_post_div2.Bits.ratio = post_div2 - 1; PLL->PLL_cmd.Bits.goset = 1; } ohio_wait_for_gostat(PLL); spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ohio_clk_set_pll(struct _ohio_clock_pll *PLL, unsigned int enable, unsigned int mult, unsigned int pre_div, unsigned int post_div, unsigned int post_div2) { int flags; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; #if defined(OHIO_CLK_SWITCH_EMULATE) DEB_ERR("[ohio_clk_set_pll] ignore PLL-SWITCH %s mult=%u pre_div=%u post_div=%u post_div2=%u\n", PLL == &(ohio_clock->SYSTEM_PLL) ? "system" : PLL == &(ohio_clock->MIPS_PLL) ? "mips" : PLL == &(ohio_clock->USB_PLL) ? "usb" : "unknown", mult, pre_div, post_div, post_div2); return; #endif /*--- #if defined(OHIO_CLK_SWITCH_EMULATE) ---*/ spin_lock_irqsave(&ohio_clock_spinlock, flags); PLL->PLL_ctrl.Bits.pllen = 0; /*--- PLL Off ---*/ if(enable == 0) { spin_unlock_irqrestore(&ohio_clock_spinlock, flags); return; } PLL->PLL_ctrl.Bits.pllpwdn = 0; /*--- Power up ---*/ if(pre_div) { PLL->PLL_pre_div.Bits.ratio = pre_div - 1; PLL->PLL_pre_div.Bits.diven = 1; } else { PLL->PLL_pre_div.Bits.diven = 1; } PLL->PLL_mult.Bits.pllm = mult - 1; udelay(1); ohio_wait_for_gostat(PLL); if(post_div || post_div2) { if(PLL == &(ohio_clock->SYSTEM_PLL)) { PLL->PLL_cmd_en.Bits.goset = 3; } else { PLL->PLL_cmd_en.Bits.goset = 1; } if(post_div) { PLL->PLL_post_div.Bits.ratio = post_div - 1; PLL->PLL_post_div.Bits.diven = 1; } else { PLL->PLL_pre_div.Bits.diven = 1; } if(post_div2) { PLL->PLL_post_div2.Bits.ratio = post_div2 - 1; PLL->PLL_post_div2.Bits.diven = 1; } PLL->PLL_cmd.Bits.goset = 1; udelay(1); ohio_wait_for_gostat(PLL); } PLL->PLL_ctrl.Bits.pllen = 1; /*--- PLL On ---*/ spin_unlock_irqrestore(&ohio_clock_spinlock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int ohio_change_clock_read(char* buf, char **start, off_t offset, int count, int *eof, void *data) { unsigned int mips_clk = ohio_get_mips_clock(); unsigned int system_clk = ohio_get_system_clock(); unsigned int usb_clk = ohio_get_usb_clock(); unsigned int dsp_clk = ohio_get_dsp_clock(); unsigned int v_clk = ohio_get_vlynq_clock(); unsigned int len; sprintf(buf, "OHIO Clock: CPU: %u.%03ukHz System: %u.%03ukHz USB: %u.%03ukHz DSP: %u.%03ukHz VLYNQ: %u.%03ukHz\n", mips_clk / 1000000, (mips_clk % 1000000) / 1000, system_clk / 1000000, (system_clk % 1000000) / 1000, usb_clk / 1000000, (usb_clk % 1000000) / 1000, dsp_clk / 1000000, (dsp_clk % 1000000) / 1000, v_clk / 1000000, (v_clk % 1000000) / 1000); len = strlen(buf); return len; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int ohio_change_clock_write(struct file *file, const char __user *buff, unsigned long count, void *data) { char *p; unsigned int clk = 1; 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) DEB_TRACE("[ohio_change_clock_write] faktor is %u\n", clk); if((p = strstr(buff, "CPU:"))) { p += sizeof("CPU:") - 1; while(*p && (*p == ' ' || *p == '\t')) p++; clk *= simple_strtol(p, NULL, 0); DEB_TRACE("[ohio_change_clock_write] CPU: p=\"%s\" set to %uHz ", p, clk); if(ohio_set_mips_clock(clk, 0)) { DEB_TRACE(" failed\n"); } else { DEB_TRACE(" success\n"); } } else if((p = strstr(buff, "System:"))) { p += sizeof("System:") - 1; while(*p && (*p == ' ' || *p == '\t')) p++; clk *= simple_strtol(p, NULL, 0); DEB_TRACE("[ohio_change_clock_write] System: p=\"%s\" set to %uHz ", p, clk); if(ohio_set_system_clock(clk, 0, 0)) { DEB_TRACE(" failed\n"); } else { DEB_TRACE(" success\n"); } } else if((p = strstr(buff, "USB:"))) { p += sizeof("USB:") - 1; while(*p && (*p == ' ' || *p == '\t')) p++; clk *= simple_strtol(p, NULL, 0); DEB_TRACE("[ohio_change_clock_write] USB: p=\"%s\" set to %uHz (%u) ", p, clk, (int)simple_strtol(p, NULL, 0)); if(ohio_set_usb_clock(clk)) { DEB_TRACE(" failed\n"); } else { DEB_TRACE(" success\n"); } } else if((p = strstr(buff, "DSP:"))) { p += sizeof("DSP:") - 1; while(*p && (*p == ' ' || *p == '\t')) p++; clk *= simple_strtol(p, NULL, 0); DEB_TRACE("[ohio_change_clock_write] DSP: p=\"%s\" set to %uHz ", p, clk); if(ohio_set_dsp_clock(clk)) { DEB_TRACE(" failed\n"); } else { DEB_TRACE(" success\n"); } } else { DEB_ERR("[ohio_change_clock_write] buf=\"%s\"\n", buff); } return count; } #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init ohio_clk_init(void) { #if defined(OHIO_CLK_DEBUG) struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ struct _valid_clk *V = ohio_get_valid_mips_clk_table(); DEB_NOTE("[ohio_clk_init]: dsl xtal %uHz lan xtal %uHz\n", V->freq, ohio_lan_xtal); spin_lock_init(&ohio_clock_spinlock); #if defined(CONFIG_OHIO_CLOCK_SWITCH) spin_lock_init(&ohio_clock_switch_spinlock); ohio_system_clock_notify_first = NULL; #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ while(V) { if(V->startup) { if(ohio_set_mips_clock(V->freq, 1)) return 1; else break; } V++; } /* Initialisiere Systemclock auf 125MHz mit 250MHz fuer DSP */ DEB_NOTE("[ohio_clk_init]: clockmode=%s clock ratio %s", BOOT->hw_boot_config.Bits.mips_async ? "async":"sync", BOOT->hw_boot_config.Bits.mips_2to1 ? "2:1":"1:1"); /*--- if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 1)) { ---*/ /*--- return 0; ---*/ /* system clock nicht setzen */ /*--- } ---*/ V = valid_system_clk; while(V) { if(V->startup) { ohio_set_system_clock(V->freq, 0, 1); return 0; } V++; } return 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) #if defined(CONFIG_PROC_FS) static int __init ohio_clk_switch_init(void) { ohio_change_clock_entry = create_proc_entry("clocks", 06666, NULL); if(ohio_change_clock_entry) { ohio_change_clock_entry->read_proc = ohio_change_clock_read; ohio_change_clock_entry->write_proc = ohio_change_clock_write; ohio_change_clock_entry->data = NULL; } return 0; }; late_initcall(ohio_clk_switch_init); #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ohio_set_clock_notify(enum _avm_clock_id clock_id, unsigned int (*notify)(enum _avm_clock_id, unsigned int new_clk), int set_function) { struct _ohio_clk_notify_system_clock_change *aktuell = ohio_system_clock_notify_first; struct _ohio_clk_notify_system_clock_change *N; int flags; DEB_INFO("[ohio_set_clock_notify] %s notify %s 0x%p ", clock_id == avm_clock_id_non ? "avm_clock_id_non" : clock_id == avm_clock_id_cpu ? "avm_clock_id_cpu" : clock_id == avm_clock_id_system ? "avm_clock_id_system" : clock_id == avm_clock_id_usb ? "avm_clock_id_usb" : clock_id == avm_clock_id_dsp ? "avm_clock_id_dsp" : clock_id == avm_clock_id_vbus ? "avm_clock_id_vbus" : "unknown", set_function ? "enable" : "disable", notify); #if defined(OHIO_CLK_DEBUG) __print_symbol("0x%x\n", (unsigned long) notify); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ if(set_function) { N = alloc_clk_listentry(); if(N == NULL) return -1; spin_lock_irqsave(&ohio_clock_switch_spinlock, flags); N->next = NULL; N->clock_id = clock_id; N->notify = notify; /*----------------------------------------------------------------------------------*\ * nicht der erste, es gibt schon einen \*----------------------------------------------------------------------------------*/ if(ohio_system_clock_notify_first) { while(aktuell->next) { aktuell = aktuell->next; } aktuell->next = N; } else { ohio_system_clock_notify_first = N; } spin_unlock_irqrestore(&ohio_clock_switch_spinlock, flags); return 0; } else { /*----------------------------------------------------------------------------------*\ * der erste und einzige \*----------------------------------------------------------------------------------*/ spin_lock_irqsave(&ohio_clock_switch_spinlock, flags); if(ohio_system_clock_notify_first == NULL) { spin_unlock_irqrestore(&ohio_clock_switch_spinlock, flags); return -1; } if((ohio_system_clock_notify_first->notify == notify) && (ohio_system_clock_notify_first->clock_id == clock_id)) { N = ohio_system_clock_notify_first; ohio_system_clock_notify_first = ohio_system_clock_notify_first->next; spin_unlock_irqrestore(&ohio_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(&ohio_clock_switch_spinlock, flags); free_clk_listentry(N); return 0; } aktuell = aktuell->next; } spin_unlock_irqrestore(&ohio_clock_switch_spinlock, flags); } return -1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_call_clock_notifier(enum _avm_clock_id clock_id, unsigned int clk) { struct _ohio_clk_notify_system_clock_change *aktuell = ohio_system_clock_notify_first; unsigned int ret = 0; while(aktuell) { if((aktuell->notify) && (aktuell->clock_id == clock_id)) { #if defined(OHIO_CLK_DEBUG) DEB_TRACE("[ohio_call_clock_notifier] call 0x%p ", aktuell->notify); __print_symbol("0x%x\n", (unsigned long) aktuell->notify); #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ ret |= (aktuell->notify)(clock_id, clk); } aktuell = aktuell->next; } return ret; } #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_set_mips_clock(unsigned int clk, unsigned int force) { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; unsigned int mult, post_div, pre_div; struct _valid_clk *V, *VStart; struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; VStart = V = ohio_get_valid_mips_clk_table(); /*--- hardware is in synchrone mode 2 ---*/ if(force == 0) { if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 0)) { int ret = ohio_set_system_clock(clk, 0, 0); if(ret) return ret; #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_cpuclock, FREQUENZ_TO_PERCENT(V->freq, 212000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ goto ohio_cpu_clk_set_notify_new_clk; } } while(V->freq) { if(V->freq == clk) break; V++; } if(V->freq) { DEB_TRACE("[ohio_set_mips_clock] set freq %u Hz pre_div=%u post_div=%u mult=%u\n", V->freq, V->pre_div, V->post_div, V->mult); mult = V->mult; pre_div = V->pre_div; post_div = V->post_div; #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_cpuclock, FREQUENZ_TO_PERCENT(V->freq, 212000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ } else { #if 0 DEB_ERR("[ohio_set_mips_clock]: possible value are:"); while(VStart->freq) { printk(" %u.%u KHz", VStart->freq / 1000, VStart->freq % 1000); VStart++; } printk("\n"); #endif return 1; } ohio_clk_set_pll(&(ohio_clock->MIPS_PLL), 1 /* enable */, mult, pre_div /* pre div */, post_div /* post div */, 0 /* post div2 */); /*--------------------------------------------------------------------------------------*\ * Da im Synchron mode die Systemfrequenz mit beeinflusst wird muessen alle die diese * Information benoetigen auch informiert werden. \*--------------------------------------------------------------------------------------*/ if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 1)) { /*--- hardware is in synchrone mode ---*/ #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_systemclock, FREQUENZ_TO_PERCENT(clk / 2, 150000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) ohio_call_clock_notifier(avm_clock_id_system, clk / 2); #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } ohio_cpu_clk_set_notify_new_clk: #if defined(CONFIG_OHIO_CLOCK_SWITCH) return ohio_call_clock_notifier(avm_clock_id_cpu, clk); #else /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ return 0; #endif /*--- #else ---*/ /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } /*------------------------------------------------------------------------------------------*\ * dsp_clk: 0: versuche die aktuelle DSP-Clock beizubehalten, aber keine Fehlermeldung falls * nicht möglich * sonst: gewünschte DSP-Clock mit dieser Systemfrequenz (Aufruf von set_dsp_clock) \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_set_system_clock(unsigned int clk, unsigned int dsp_clk, unsigned int force) { struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; unsigned int mult = 0; unsigned int pre_div = 0; unsigned int post_div = 0; unsigned int post_div2 = 0; unsigned int i, dsp_clk_from_system_clk = dsp_clk, first_match = 0; unsigned int sdram_timing = 0; if(force == 0) { if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 1)) { /*--- hardware is in synchrone mode ---*/ /*--- unterstuetzung fuer sync mode 2 ---*/ int ret = ohio_set_mips_clock(clk * 2, 0); if(ret) return ret; #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_systemclock, FREQUENZ_TO_PERCENT(clk * 2, 150000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ goto ohio_system_clk_set_notify_new_clk; } } if((dsp_clk == 0) && (BOOT->hw_boot_config.Bits.pll_byp == 0) && (BOOT->hw_boot_adsl_vserclksel.Bits.ohio_250_mode == 1)) { /*--- DSP-Clock wird vom System-Clock abgeleitet: maximale Uebereinstimmung checken ---*/ dsp_clk_from_system_clk = ohio_get_dsp_clock(); } for(i = 0 ; valid_system_clk[i].freq ; i++) { if(valid_system_clk[i].freq == clk) { if(first_match == 0) first_match = i+1; if(dsp_clk_from_system_clk == 0) { /*--- DSP-Clock unerheblich ---*/ DEB_INFO("ohio_set_system_clock:dsp-clock from mips-clock\n"); break; } if(valid_system_clk[i].dspfreq == dsp_clk_from_system_clk) { DEB_INFO("ohio_set_system_clock:dsp-clock(%d) possible with system-clock(%d)\n", dsp_clk_from_system_clk, clk); break; } } } if((valid_system_clk[i].freq == 0) && first_match) { /*--- dann nehmen wir eben keine Rücksicht auf dsp ---*/ i = first_match - 1; DEB_INFO("ohio_set_system_clock:dsp-clock will unlikley changed from systemclock\n"); } switch(clk) { default: case 125000000: sdram_timing = ohio_SDRAM_config[EMIF_SDRAM_TIMING_125MHZ].i; break; case 137500000: sdram_timing = ohio_SDRAM_config[EMIF_SDRAM_TIMING_137MHZ].i; break; case 150000000: sdram_timing = ohio_SDRAM_config[EMIF_SDRAM_TIMING_150MHZ].i; break; } if(ohio_current_system_clock == clk) { #ifdef USE_CL2_RAMS struct EMIF_register_memory_map *EMIF = (struct EMIF_register_memory_map *)OHIO_EMIF_BASE; DEB_TRACE("[speedup] CL%d -> CL2 \n", EMIF->SDRAMBankCR.Bits.cl ? 3 : 2); EMIF->SDRAMBankCR.i = EMIF->SDRAMBankCR.i & ~(1 << 13); #endif /*--- #ifdef USE_CL2_RAMS ---*/ sdram_timing = 0; } else if(ohio_current_system_clock < clk) { #if defined(OHIO_CLK_DEBUG) struct EMIF_register_memory_map *EMIF = (struct EMIF_register_memory_map *)OHIO_EMIF_BASE; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ #ifdef USE_CL2_RAMS DEB_TRACE("[speedup] (pre) (0x%x) CL%d -> CL2 Timing 0x%x -> 0x%x\n", EMIF->SDRAMBankCR.i, EMIF->SDRAMBankCR.Bits.cl ? 3 : 2, EMIF->SDRAMTimingReg.i, sdram_timing); EMIF->SDRAMBankCR.i = EMIF->SDRAMBankCR.i & ~(1 << 13); #else /*--- #ifdef USE_CL2_RAMS ---*/ DEB_TRACE("[speedup] (pre) Timing 0x%x -> 0x%x\n", EMIF->SDRAMTimingReg.i, sdram_timing); #endif /*--- #else ---*/ /*--- #ifdef USE_CL2_RAMS ---*/ /*--- EMIF->SDRAMTimingReg.i = sdram_timing; ---*/ sdram_timing = 0; } if(valid_system_clk[i].freq) { mult = valid_system_clk[i].mult; pre_div = valid_system_clk[i].pre_div; post_div = valid_system_clk[i].post_div; post_div2 = valid_system_clk[i].post_div2; #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_systemclock, FREQUENZ_TO_PERCENT(valid_system_clk[i].freq, 150000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ } else { DEB_ERR("[ohio_set_system_clock]: possible value are:"); for(i = 0 ; valid_system_clk[i].freq ; i++) { printk(" %u.%u KHz", valid_system_clk[i].freq / 1000, valid_system_clk[i].freq % 1000); } printk("\n"); return 1; } DEB_TRACE("[ohio_set_system_clock] clk=%d, multi=%d, pre_div=%d, post_div=%d, post_div2=%d\n", clk, mult, pre_div, post_div, post_div2); if(ohio_clk_setpost_div_by_runningpll( &(ohio_clock->SYSTEM_PLL), 1 /* enable */, mult, pre_div , post_div , post_div2)) { /*--- ging nicht im laufenden PLL-Betrieb: komplett umprogrammieren ---*/ ohio_clk_set_pll(&(ohio_clock->SYSTEM_PLL), 1 /* enable */, mult, pre_div /* pre div */, post_div /* post div */, post_div2 /* post div2 */); } if(sdram_timing) { #if defined(OHIO_CLK_DEBUG) struct EMIF_register_memory_map *EMIF = (struct EMIF_register_memory_map *)OHIO_EMIF_BASE; #endif /*--- #if defined(OHIO_CLK_DEBUG) ---*/ #ifdef USE_CL2_RAMS DEB_TRACE("[speedup] (post) (0x%x) CL%d -> CL2 Timing 0x%x -> 0x%x\n", EMIF->SDRAMBankCR.i, EMIF->SDRAMBankCR.Bits.cl ? 3 : 2, EMIF->SDRAMTimingReg.i, sdram_timing); EMIF->SDRAMBankCR.i = EMIF->SDRAMBankCR.i & ~(1 << 13); #else /*--- #ifdef USE_CL2_RAMS ---*/ DEB_TRACE("[speedup] (post) Timing 0x%x -> 0x%x\n", EMIF->SDRAMTimingReg.i, sdram_timing); #endif /*--- #else ---*/ /*--- #ifdef USE_CL2_RAMS ---*/ /*--- EMIF->SDRAMTimingReg.i = sdram_timing; ---*/ } /*--------------------------------------------------------------------------------------*\ * Da im Synchon mode die Systemfrequenz mit beeinflusst wird muessen alle die diese * Information benoetigen auch informiert werden. \*--------------------------------------------------------------------------------------*/ if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 0)) { /*--- hardware is in synchrone mode ---*/ #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_cpuclock, FREQUENZ_TO_PERCENT(clk, 212000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) ohio_call_clock_notifier(avm_clock_id_cpu, clk); #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } ohio_system_clk_set_notify_new_clk: ohio_current_system_clock = clk; #if defined(CONFIG_OHIO_CLOCK_SWITCH) return ohio_call_clock_notifier(avm_clock_id_system, clk); #else /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ return 0; #endif /*--- #else ---*/ /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) static unsigned int current_usb_clock = 48000000; static unsigned int ohio_set_usb_clock_notify(enum _avm_clock_id clock_id, unsigned int clk) { if(clock_id != avm_clock_id_system) return 1; return ohio_set_usb_clock(current_usb_clock); } #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_set_usb_clock(unsigned int clk) { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; unsigned int pre_div = 1, post_div = 1, mul = 1, system; #if defined(CONFIG_OHIO_CLOCK_SWITCH) ohio_set_clock_notify(avm_clock_id_system, ohio_set_usb_clock_notify, 0); /*--- deinstall ---*/ #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ switch(clk) { case 25000000: case 48000000: case 50000000: case 100000000: case 200000000: break; default: return 1; } system = ohio_clk_get_pll_factor(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); DEB_TRACE("[ohio_set_usb_clock] usb pll input %u Hz want %u Hz\n", system, clk); switch(system) { case 25000000: case 50000000: case 75000000: case 100000000: case 137500000: default: return 1; case 150000000: if(clk == 25000000) pre_div = 6, post_div = 1, mul = 1; else if(clk == 100000000) pre_div = 6, post_div = 1, mul = 4; else if(clk == 200000000) pre_div = 3, post_div = 1, mul = 4; else if(clk == 50000000) pre_div = 3, post_div = 1, mul = 1; else pre_div = 25, post_div = 1, mul = 8; break; case 125000000: if(clk == 25000000) pre_div = 5, post_div = 1, mul = 1; else if(clk == 100000000) pre_div = 5, post_div = 1, mul = 4; else if(clk == 200000000) pre_div = 5, post_div = 1, mul = 8; else if(clk == 50000000) pre_div = 5, post_div = 1, mul = 2; else pre_div = 26, post_div = 1, mul = 10; break; case 62500000: if(clk == 25000000) pre_div = 1, post_div = 5, mul = 2; else if(clk == 100000000) pre_div = 1, post_div = 5, mul = 8; else if(clk == 50000000) pre_div = 1, post_div = 5, mul = 4; else pre_div = 13, post_div = 1, mul = 10; break; case 162500000: break; } ohio_clk_set_pll(&(ohio_clock->USB_PLL), 1, mul, pre_div, post_div, 0 /* post div2 */); #if defined(CONFIG_OHIO_CLOCK_SWITCH) current_usb_clock = clk; ohio_set_clock_notify(avm_clock_id_system, ohio_set_usb_clock_notify, 1); /*--- install ---*/ return ohio_call_clock_notifier(avm_clock_id_usb, clk); #else /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ return 0; #endif /*--- #else ---*/ /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } /*------------------------------------------------------------------------------------------*\ * Vorrausetzung ist, das die Frequenz der MIPS-Frequenz oder den DSP-Frequenzwerten in valid_system_clk * entspricht * Es wird zuerst versucht, ob sich die Frequenz: * a) von der aktuellen Systemfrequenz ableiten lässt -> nein * b) von einer prefererierten Systemfrequenz ableiten lässt -> nein * c) nehme passenden Eintrag ->nein * d) checke ob mit MIPS-Frequenz identisch -> nein * Umsetzung fehlgschlagen \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_set_dsp_clock(unsigned int clk) { struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _valid_clk *V = ohio_get_valid_mips_clk_table(); struct _valid_clk *Vdsp = valid_system_clk, *Vdspprefer = NULL, *VdspMatch = NULL; unsigned int test_clk; if(BOOT->hw_boot_config.Bits.pll_byp == 0) { if(BOOT->hw_boot_adsl_vserclksel.Bits.ohio_250_mode == 1) { test_clk = ohio_clk_get_pll_factor2(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); if(test_clk == clk) { DEB_INFO("[ohio_set_dsp_clock] (system pll), clk = %d (no change)\n", clk); goto good; } } while(Vdsp->freq) { test_clk = ohio_clk_get_pll_factor(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); if(Vdsp->dspfreq == clk) { if(Vdsp->freq == test_clk) { /*--- a) versuche aktuelle System-Frequenz beizubehalten ---*/ DEB_INFO("[ohio_set_dsp_clock] (system pll actual clk=%d), dspclk = %d\n", Vdsp->freq, clk); break; } if(Vdsp->prefer) { /*--- b) falls aktuelle System-Frequenz nicht haltbar: preferierten Wert nehmen ---*/ Vdspprefer = Vdsp; } else { /*--- c) falls aktuelle System-Frequenz nicht haltbar und keine Preferenz: Wert nehmen ---*/ VdspMatch = Vdsp; } } Vdsp++; } if(Vdsp->freq == 0) { if(Vdspprefer) { DEB_INFO("[ohio_set_dsp_clock] (prefered system pll clk=%d), dspclk = %d\n", Vdsp->freq, clk); Vdsp = Vdspprefer; } else { Vdsp = VdspMatch; } } if(Vdsp && Vdsp->freq) { DEB_INFO("[ohio_set_dsp_clock] (system pll clk=%d), dspclk = %d\n", Vdsp->freq, clk); ohio_set_system_clock(Vdsp->freq, clk, 0); BOOT->hw_boot_adsl_vserclksel.Bits.ohio_250_mode = 1; goto good; } } /*--- d) nun schauen, ob sich DSP-Frequenz mit MIPS-Frequenz deckt ---*/ test_clk = ohio_clk_get_pll_factor(&(ohio_clock->MIPS_PLL), V->freq); DEB_INFO("[ohio_set_dsp_clock]: test_clk = %d (mips pll), clk = %d\n", test_clk, clk); if(test_clk == clk) { BOOT->hw_boot_adsl_vserclksel.Bits.ohio_250_mode = 0; goto good; } return 1; good: #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_dspclock, FREQUENZ_TO_PERCENT(clk, 250000000)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) return ohio_call_clock_notifier(avm_clock_id_dsp, clk); #else /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ return 0; #endif /*--- #else ---*/ /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_get_system_clock(void) { struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; unsigned int clk = 0; /*--- hardware is in synchrone mode 1 ---*/ if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 1)) { DEB_WARN("[ohio_get_system_clock]: sync mode 1:2\n"); clk = ohio_get_mips_clock() / 2; } else { clk = ohio_clk_get_pll_factor(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); } DEB_TRACE(KERN_ERR "[ohio_get_system_clock]: clk = %u\n", clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_get_mips_clock(void) { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _valid_clk *V = ohio_get_valid_mips_clk_table(); struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; unsigned int clk; if((BOOT->hw_boot_config.Bits.mips_async == 0) && (BOOT->hw_boot_config.Bits.mips_2to1 == 0)) { DEB_WARN("[ohio_get_mips_clock, sync mode 1:1]: \n"); return ohio_get_system_clock(); } clk = ohio_clk_get_pll_factor(&(ohio_clock->MIPS_PLL), V->freq); DEB_TRACE("[ohio_get_mips_clock]: clk = %u\n", clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_get_dsp_clock(void) { struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _valid_clk *V = ohio_get_valid_mips_clk_table(); unsigned int clk; if((BOOT->hw_boot_config.Bits.pll_byp == 0) && (BOOT->hw_boot_adsl_vserclksel.Bits.ohio_250_mode == 1)) { clk = ohio_clk_get_pll_factor(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); clk *= ohio_clock->SYSTEM_PLL.PLL_post_div.Bits.ratio + 1; clk /= ohio_clock->SYSTEM_PLL.PLL_post_div2.Bits.ratio + 1; } else { clk = ohio_clk_get_pll_factor(&(ohio_clock->MIPS_PLL), V->freq); } DEB_TRACE("[ohio_get_dsp_clock]: clk = %u\n", clk); return clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ohio_get_usb_clock(void) { struct _hw_boot *BOOT = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; unsigned int clk; if(BOOT->hw_boot_config.Bits.pll_byp == 0) { clk = ohio_clk_get_pll_factor(&(ohio_clock->SYSTEM_PLL), ohio_lan_xtal); } else { clk = ohio_lan_xtal; } clk = ohio_clk_get_pll_factor(&(ohio_clock->USB_PLL), clk); DEB_TRACE("[ohio_get_usb_clock]: clk = %u\n", clk); return clk; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int ohio_set_vlynq_clock(unsigned int vbus_clk) { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _hw_boot *hw_boot = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_reset *RESET = (struct _hw_reset *)OHIO_RESET_BASE; struct _vlynq_registers_half *vlynq = (struct _vlynq_registers_half *)OHIO_VLYNQ0_CTRL_BASE; if((RESET->non_reset.Bits.vlynq0_unreset == 0) && (RESET->non_reset.Bits.vlynq1_unreset == 0)) { /*--- Vlynq im reset ---*/ return 0; } if((ohio_clock->PDCR.Bits.vlynq0p == 1) && (ohio_clock->PDCR.Bits.vlynq1p == 1)) { /*--- Vlynq im poerdown ---*/ return 0; } switch(vbus_clk) { case 62500000: hw_boot->hw_boot_adsl_vserclksel.Bits.vlync_clk_sel = 0; /* 0: 125 MHz 1: 211 MHz */ vlynq->Control.Bits.clkdir = 1; /* 1 clk from divisor */ vlynq->Control.Bits.clkdiv = 1; break; case 125000000: hw_boot->hw_boot_adsl_vserclksel.Bits.vlync_clk_sel = 0; /* 0: 125 MHz 1: 211 MHz */ vlynq->Control.Bits.clkdir = 1; /* 1 clk from divisor */ vlynq->Control.Bits.clkdiv = 0; break; case 70656000: vlynq->Control.Bits.clkdiv = 2; vlynq->Control.Bits.clkdir = 1; /* 1 clk from divisor */ ohio_clk_set_pll(&ohio_clock->MIPS_PLL, 1, /*--- enable ---*/ 6, /*--- mult ---*/ 1, /*--- pre_div ---*/ 1, /*--- post_div ---*/ 1 /*--- post_div2 ---*/ ); hw_boot->hw_boot_adsl_vserclksel.Bits.vlync_clk_sel = 1; /* 0: 125 MHz 1: 211 MHz */ break; } return ohio_get_vlynq_clock() == vbus_clk; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int ohio_get_vlynq_clock(void) { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _hw_boot *hw_boot = (struct _hw_boot *)OHIO_DEVICE_CONFIG_BASE; struct _hw_reset *RESET = (struct _hw_reset *)OHIO_RESET_BASE; struct _vlynq_registers_half *vlynq = (struct _vlynq_registers_half *)OHIO_VLYNQ0_CTRL_BASE; unsigned int base_clk, div = 0; if((RESET->non_reset.Bits.vlynq0_unreset == 0) && (RESET->non_reset.Bits.vlynq1_unreset == 0)) { /*--- Vlynq im reset ---*/ return 0; } if((ohio_clock->PDCR.Bits.vlynq0p == 1) && (ohio_clock->PDCR.Bits.vlynq1p == 1)) { /*--- Vlynq im powerdown ---*/ return 0; } /* baseclock is 0: 125 MHz 1: 211 MHz */ if(hw_boot->hw_boot_adsl_vserclksel.Bits.vlync_clk_sel == 0) { base_clk = ohio_get_system_clock(); } else { struct _hw_clock *ohio_clock = (struct _hw_clock *)OHIO_CLOCK_BASE; struct _valid_clk *V = ohio_get_valid_mips_clk_table(); base_clk = ohio_clk_get_pll_factor(&(ohio_clock->MIPS_PLL), V->freq); } /*--- divider ---*/ if(vlynq->Control.Bits.clkdir) { /* 1 clk from divisor */ div = vlynq->Control.Bits.clkdiv; } else { /*--- vlynq-clk is input ---*/ return 0; } base_clk /= (div + 1); /*--- printk("[ohio_get_vlynq_clock]: base_clk = %u div = %u\n", base_clk, div); ---*/ return base_clk; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ohio_get_clock(enum _avm_clock_id clock_id) { switch(clock_id) { case avm_clock_id_cpu: return ohio_get_mips_clock(); case avm_clock_id_peripheral: return ohio_get_system_clock() / 2; case avm_clock_id_system: return ohio_get_system_clock(); case avm_clock_id_vbus: return ohio_get_system_clock() / 2; case avm_clock_id_usb: return ohio_get_usb_clock(); case avm_clock_id_dsp: return ohio_get_dsp_clock(); case avm_clock_id_vlynq: return ohio_get_vlynq_clock(); default: break; } return 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int ohio_set_clock(enum _avm_clock_id clock_id, unsigned int clk) { DEB_TRACE(KERN_ERR "[ohio_set_clock]: %s clk = %u \n", clock_id == avm_clock_id_cpu ? "cpu" : clock_id == avm_clock_id_system ? "system" : clock_id == avm_clock_id_usb ? "usb" : clock_id == avm_clock_id_dsp ? "dsp" : clock_id == avm_clock_id_vbus ? "vbus" : "?", clock_id == avm_clock_id_vlynq ? "vlynq" : "?", clk); switch(clock_id) { case avm_clock_id_cpu: return ohio_set_mips_clock(clk, 0); case avm_clock_id_system: return ohio_set_system_clock(clk, 0, 0); case avm_clock_id_usb: return ohio_set_usb_clock(clk); case avm_clock_id_dsp: return ohio_set_dsp_clock(clk); case avm_clock_id_vlynq: return ohio_set_vlynq_clock(clk); case avm_clock_id_vbus: default: break; } return 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) unsigned int ohio_get_clock_notify(enum _avm_clock_id clock_id, unsigned int (*handler)(enum _avm_clock_id, unsigned int new_clk)) { ohio_set_clock_notify(clock_id, handler, 0); /*--- deinstall ---*/ ohio_set_clock_notify(clock_id, handler, 1); /*--- install ---*/ return ohio_get_clock(clock_id); } EXPORT_SYMBOL(ohio_get_clock_notify); #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) ---*/ EXPORT_SYMBOL(ohio_get_clock); EXPORT_SYMBOL(ohio_set_clock);