/* * We read the real processor speed from the PLL. This is important * because it is more accurate than computing it from the 32KHz * counter, if it exists. If we don't have an accurate processor * speed, all of the peripherals that derive their clocks based on * this advertised speed will introduce error and sometimes not work * properly. This function is futher convoluted to still allow configurations * to do that in case they have really, really old silicon with a * write-only PLL register, that we need the 32KHz when power management * "wait" is enabled, and we need to detect if the 32KHz isn't present * but requested......got it? :-) -- Dan */ unsigned long cal_r4koff(void) { unsigned long cpu_speed; unsigned long flags; unsigned long counter; spin_lock_irqsave(&time_lock, flags); /* Power management cares if we don't have a 32KHz counter. */ no_au1xxx_32khz = 0; counter = au_readl(SYS_COUNTER_CNTRL); if (counter & SYS_CNTRL_E0) { int trim_divide = 16; au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); /* RTC now ticks at 32.768/16 kHz */ au_writel(trim_divide-1, SYS_RTCTRIM); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); au_writel (0, SYS_TOYWRITE); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; } else { /* The 32KHz oscillator isn't running, so assume there * isn't one and grab the processor speed from the PLL. * NOTE: some old silicon doesn't allow reading the PLL. */ cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; no_au1xxx_32khz = 1; } mips_hpt_frequency = cpu_speed; // Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16)); spin_unlock_irqrestore(&time_lock, flags); return (cpu_speed / HZ); }
/* * Figure out the r4k offset, the amount to increment the compare * register for each time tick. * Use the Programmable Counter 1 to do this. */ unsigned long cal_r4koff(void) { unsigned long count; unsigned long cpu_speed; unsigned long start, end; unsigned long counter; int trim_divide = 16; unsigned long flags; spin_lock_irqsave(&time_lock, flags); counter = au_readl(SYS_COUNTER_CNTRL); au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); au_writel(trim_divide-1, SYS_RTCTRIM); /* RTC now ticks at 32.768/16 kHz */ while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); au_writel (0, SYS_TOYWRITE); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); start = au_readl(SYS_RTCREAD); start += 2; /* wait for the beginning of a new tick */ while (au_readl(SYS_RTCREAD) < start); /* Start r4k counter. */ write_c0_count(0); end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */ while (end > au_readl(SYS_RTCREAD)); count = read_c0_count(); cpu_speed = count * 2; mips_counter_frequency = count; set_au1x00_uart_baud_base(((cpu_speed) / 4) / 16); spin_unlock_irqrestore(&time_lock, flags); return (cpu_speed / HZ); }
/* * We read the real processor speed from the PLL. This is important * because it is more accurate than computing it from the 32KHz * counter, if it exists. If we don't have an accurate processor * speed, all of the peripherals that derive their clocks based on * this advertised speed will introduce error and sometimes not work * properly. This function is futher convoluted to still allow configurations * to do that in case they have really, really old silicon with a * write-only PLL register, that we need the 32KHz when power management * "wait" is enabled, and we need to detect if the 32KHz isn't present * but requested......got it? :-) -- Dan */ unsigned long cal_r4koff(void) { unsigned long cpu_speed; unsigned long flags; unsigned long counter; spin_lock_irqsave(&time_lock, flags); /* Power management cares if we don't have a 32KHz counter. */ no_au1xxx_32khz = 0; counter = au_readl(SYS_COUNTER_CNTRL); if (counter & SYS_CNTRL_E0) { int trim_divide = 16; au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); /* RTC now ticks at 32.768/16 kHz */ au_writel(trim_divide-1, SYS_RTCTRIM); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); au_writel (0, SYS_TOYWRITE); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); #if defined(CONFIG_AU1000_USE32K) { unsigned long start, end, count; start = au_readl(SYS_RTCREAD); start += 2; /* wait for the beginning of a new tick */ while (au_readl(SYS_RTCREAD) < start); /* Start r4k counter. */ write_c0_count(0); /* Wait 0.5 seconds. */ end = start + (32768 / trim_divide)/2; while (end > au_readl(SYS_RTCREAD)); count = read_c0_count(); cpu_speed = count * 2; } #else cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; #endif } else { /* The 32KHz oscillator isn't running, so assume there * isn't one and grab the processor speed from the PLL. * NOTE: some old silicon doesn't allow reading the PLL. */ cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; no_au1xxx_32khz = 1; } mips_hpt_frequency = cpu_speed; // Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16)); spin_unlock_irqrestore(&time_lock, flags); return (cpu_speed / HZ); }
static int pm_do_freq(ctl_table * ctl, int write, struct file *file, void *buffer, size_t * len) { int retval = 0, i; unsigned long val, pll; #define TMPBUFLEN 64 #define MAX_CPU_FREQ 396 char buf[TMPBUFLEN], *p; unsigned long flags, intc0_mask, intc1_mask; unsigned long old_baud_base, old_cpu_freq, baud_rate, old_clk, old_refresh; unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh; spin_lock_irqsave(&pm_lock, flags); if (!write) { *len = 0; } else { /* Parse the new frequency */ if (*len > TMPBUFLEN - 1) { spin_unlock_irqrestore(&pm_lock, flags); return -EFAULT; } if (copy_from_user(buf, buffer, *len)) { spin_unlock_irqrestore(&pm_lock, flags); return -EFAULT; } buf[*len] = 0; p = buf; val = simple_strtoul(p, &p, 0); if (val > MAX_CPU_FREQ) { spin_unlock_irqrestore(&pm_lock, flags); return -EFAULT; } pll = val / 12; if ((pll > 33) || (pll < 7)) { /* 396 MHz max, 84 MHz min */ /* revisit this for higher speed cpus */ spin_unlock_irqrestore(&pm_lock, flags); return -EFAULT; } old_baud_base = get_au1x00_uart_baud_base(); old_cpu_freq = get_au1x00_speed(); new_cpu_freq = pll * 12 * 1000000; new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16)); set_au1x00_speed(new_cpu_freq); set_au1x00_uart_baud_base(new_baud_base); old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff; new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) | (au_readl(MEM_SDREFCFG) & ~0x1ffffff); au_writel(pll, SYS_CPUPLL); au_sync_delay(1); au_writel(new_refresh, MEM_SDREFCFG); au_sync_delay(1); for (i = 0; i < 4; i++) { if (au_readl (UART_BASE + UART_MOD_CNTRL + i * 0x00100000) == 3) { old_clk = au_readl(UART_BASE + UART_CLK + i * 0x00100000); // baud_rate = baud_base/clk baud_rate = old_baud_base / old_clk; /* we won't get an exact baud rate and the error * could be significant enough that our new * calculation will result in a clock that will * give us a baud rate that's too far off from * what we really want. */ if (baud_rate > 100000) baud_rate = 115200; else if (baud_rate > 50000) baud_rate = 57600; else if (baud_rate > 30000) baud_rate = 38400; else if (baud_rate > 17000) baud_rate = 19200; else (baud_rate = 9600); // new_clk = new_baud_base/baud_rate new_clk = new_baud_base / baud_rate; au_writel(new_clk, UART_BASE + UART_CLK + i * 0x00100000); au_sync_delay(10); } } } /* We don't want _any_ interrupts other than * match20. Otherwise our calibrate_delay() * calculation will be off, potentially a lot. */ intc0_mask = save_local_and_disable(0); intc1_mask = save_local_and_disable(1); local_enable_irq(AU1000_TOY_MATCH2_INT); spin_unlock_irqrestore(&pm_lock, flags); calibrate_delay(); restore_local_and_enable(0, intc0_mask); restore_local_and_enable(1, intc1_mask); return retval; }