c8004a2818
PSC drivers should not access the CDM registers directly. Instead provide a common routine for setting the PSC clock parameters with the required locking. Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
314 lines
7.7 KiB
C
314 lines
7.7 KiB
C
/*
|
|
* Common code for the boards based on Freescale MPC52xx embedded CPU.
|
|
*
|
|
*
|
|
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
|
|
*
|
|
* Support for other bootloaders than UBoot by Dale Farnsworth
|
|
* <dfarnsworth@mvista.com>
|
|
*
|
|
* Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
|
|
* Copyright (C) 2003 Montavista Software, Inc
|
|
*
|
|
* This file is licensed under the terms of the GNU General Public License
|
|
* version 2. This program is licensed "as is" without any warranty of any
|
|
* kind, whether express or implied.
|
|
*/
|
|
|
|
|
|
#include <linux/spinlock.h>
|
|
#include <asm/io.h>
|
|
#include <asm/time.h>
|
|
#include <asm/mpc52xx.h>
|
|
#include <asm/mpc52xx_psc.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/ppcboot.h>
|
|
|
|
#include <syslib/mpc52xx_pci.h>
|
|
|
|
extern bd_t __res;
|
|
|
|
static int core_mult[] = { /* CPU Frequency multiplier, taken */
|
|
0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */
|
|
30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */
|
|
30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */
|
|
0, 55, 40, 50, 80, 60, 35, 0
|
|
};
|
|
|
|
void
|
|
mpc52xx_restart(char *cmd)
|
|
{
|
|
struct mpc52xx_gpt __iomem *gpt0 = MPC52xx_VA(MPC52xx_GPTx_OFFSET(0));
|
|
|
|
local_irq_disable();
|
|
|
|
/* Turn on the watchdog and wait for it to expire. It effectively
|
|
does a reset */
|
|
out_be32(&gpt0->count, 0x000000ff);
|
|
out_be32(&gpt0->mode, 0x00009004);
|
|
|
|
while (1);
|
|
}
|
|
|
|
void
|
|
mpc52xx_halt(void)
|
|
{
|
|
local_irq_disable();
|
|
|
|
while (1);
|
|
}
|
|
|
|
void
|
|
mpc52xx_power_off(void)
|
|
{
|
|
/* By default we don't have any way of shut down.
|
|
If a specific board wants to, it can set the power down
|
|
code to any hardware implementation dependent code */
|
|
mpc52xx_halt();
|
|
}
|
|
|
|
|
|
void __init
|
|
mpc52xx_set_bat(void)
|
|
{
|
|
/* Set BAT 2 to map the 0xf0000000 area */
|
|
/* This mapping is used during mpc52xx_progress,
|
|
* mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug
|
|
*/
|
|
mb();
|
|
mtspr(SPRN_DBAT2U, 0xf0001ffe);
|
|
mtspr(SPRN_DBAT2L, 0xf000002a);
|
|
mb();
|
|
}
|
|
|
|
void __init
|
|
mpc52xx_map_io(void)
|
|
{
|
|
/* Here we map the MBAR and the whole upper zone. MBAR is only
|
|
64k but we can't map only 64k with BATs. Map the whole
|
|
0xf0000000 range is ok and helps eventual lpb devices placed there */
|
|
io_block_mapping(
|
|
MPC52xx_MBAR_VIRT, MPC52xx_MBAR, 0x10000000, _PAGE_IO);
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_TEXT_DEBUG
|
|
#ifndef MPC52xx_PF_CONSOLE_PORT
|
|
#error "mpc52xx PSC for console not selected"
|
|
#endif
|
|
|
|
static void
|
|
mpc52xx_psc_putc(struct mpc52xx_psc __iomem *psc, unsigned char c)
|
|
{
|
|
while (!(in_be16(&psc->mpc52xx_psc_status) &
|
|
MPC52xx_PSC_SR_TXRDY));
|
|
out_8(&psc->mpc52xx_psc_buffer_8, c);
|
|
}
|
|
|
|
void
|
|
mpc52xx_progress(char *s, unsigned short hex)
|
|
{
|
|
char c;
|
|
struct mpc52xx_psc __iomem *psc;
|
|
|
|
psc = MPC52xx_VA(MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT));
|
|
|
|
while ((c = *s++) != 0) {
|
|
if (c == '\n')
|
|
mpc52xx_psc_putc(psc, '\r');
|
|
mpc52xx_psc_putc(psc, c);
|
|
}
|
|
|
|
mpc52xx_psc_putc(psc, '\r');
|
|
mpc52xx_psc_putc(psc, '\n');
|
|
}
|
|
|
|
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
|
|
|
|
|
|
unsigned long __init
|
|
mpc52xx_find_end_of_memory(void)
|
|
{
|
|
u32 ramsize = __res.bi_memsize;
|
|
|
|
/*
|
|
* if bootloader passed a memsize, just use it
|
|
* else get size from sdram config registers
|
|
*/
|
|
if (ramsize == 0) {
|
|
struct mpc52xx_mmap_ctl __iomem *mmap_ctl;
|
|
u32 sdram_config_0, sdram_config_1;
|
|
|
|
/* Temp BAT2 mapping active when this is called ! */
|
|
mmap_ctl = MPC52xx_VA(MPC52xx_MMAP_CTL_OFFSET);
|
|
|
|
sdram_config_0 = in_be32(&mmap_ctl->sdram0);
|
|
sdram_config_1 = in_be32(&mmap_ctl->sdram1);
|
|
|
|
if ((sdram_config_0 & 0x1f) >= 0x13)
|
|
ramsize = 1 << ((sdram_config_0 & 0xf) + 17);
|
|
|
|
if (((sdram_config_1 & 0x1f) >= 0x13) &&
|
|
((sdram_config_1 & 0xfff00000) == ramsize))
|
|
ramsize += 1 << ((sdram_config_1 & 0xf) + 17);
|
|
}
|
|
|
|
return ramsize;
|
|
}
|
|
|
|
void __init
|
|
mpc52xx_calibrate_decr(void)
|
|
{
|
|
int current_time, previous_time;
|
|
int tbl_start, tbl_end;
|
|
unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor;
|
|
|
|
xlbfreq = __res.bi_busfreq;
|
|
/* if bootloader didn't pass bus frequencies, calculate them */
|
|
if (xlbfreq == 0) {
|
|
/* Get RTC & Clock manager modules */
|
|
struct mpc52xx_rtc __iomem *rtc;
|
|
struct mpc52xx_cdm __iomem *cdm;
|
|
|
|
rtc = ioremap(MPC52xx_PA(MPC52xx_RTC_OFFSET), MPC52xx_RTC_SIZE);
|
|
cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
|
|
|
|
if ((rtc==NULL) || (cdm==NULL))
|
|
panic("Can't ioremap RTC/CDM while computing bus freq");
|
|
|
|
/* Count bus clock during 1/64 sec */
|
|
out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
|
|
previous_time = in_be32(&rtc->time);
|
|
while ((current_time = in_be32(&rtc->time)) == previous_time) ;
|
|
tbl_start = get_tbl();
|
|
previous_time = current_time;
|
|
while ((current_time = in_be32(&rtc->time)) == previous_time) ;
|
|
tbl_end = get_tbl();
|
|
out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
|
|
|
|
/* Compute all frequency from that & CDM settings */
|
|
xlbfreq = (tbl_end - tbl_start) << 8;
|
|
cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10;
|
|
ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ?
|
|
xlbfreq / 2 : xlbfreq;
|
|
switch (in_8(&cdm->pci_clk_sel) & 3) {
|
|
case 0:
|
|
pcifreq = ipbfreq;
|
|
break;
|
|
case 1:
|
|
pcifreq = ipbfreq / 2;
|
|
break;
|
|
default:
|
|
pcifreq = xlbfreq / 4;
|
|
break;
|
|
}
|
|
__res.bi_busfreq = xlbfreq;
|
|
__res.bi_intfreq = cpufreq;
|
|
__res.bi_ipbfreq = ipbfreq;
|
|
__res.bi_pcifreq = pcifreq;
|
|
|
|
/* Release mapping */
|
|
iounmap(rtc);
|
|
iounmap(cdm);
|
|
}
|
|
|
|
divisor = 4;
|
|
|
|
tb_ticks_per_jiffy = xlbfreq / HZ / divisor;
|
|
tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
|
|
}
|
|
|
|
|
|
void __init
|
|
mpc52xx_setup_cpu(void)
|
|
{
|
|
struct mpc52xx_cdm __iomem *cdm;
|
|
struct mpc52xx_xlb __iomem *xlb;
|
|
|
|
/* Map zones */
|
|
cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
|
|
xlb = ioremap(MPC52xx_PA(MPC52xx_XLB_OFFSET), MPC52xx_XLB_SIZE);
|
|
|
|
if (!cdm || !xlb) {
|
|
printk(KERN_ERR __FILE__ ": "
|
|
"Error while mapping CDM/XLB during "
|
|
"mpc52xx_setup_cpu\n");
|
|
goto unmap_regs;
|
|
}
|
|
|
|
/* Use internal 48 Mhz */
|
|
out_8(&cdm->ext_48mhz_en, 0x00);
|
|
out_8(&cdm->fd_enable, 0x01);
|
|
if (in_be32(&cdm->rstcfg) & 0x40) /* Assumes 33Mhz clock */
|
|
out_be16(&cdm->fd_counters, 0x0001);
|
|
else
|
|
out_be16(&cdm->fd_counters, 0x5555);
|
|
|
|
/* Configure the XLB Arbiter priorities */
|
|
out_be32(&xlb->master_pri_enable, 0xff);
|
|
out_be32(&xlb->master_priority, 0x11111111);
|
|
|
|
/* Enable ram snooping for 1GB window */
|
|
out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_SNOOP);
|
|
out_be32(&xlb->snoop_window, MPC52xx_PCI_TARGET_MEM | 0x1d);
|
|
|
|
/* Disable XLB pipelining */
|
|
/* (cfr errata 292. We could do this only just before ATA PIO
|
|
transaction and re-enable it after ...) */
|
|
out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS);
|
|
|
|
/* Unmap reg zone */
|
|
unmap_regs:
|
|
if (cdm) iounmap(cdm);
|
|
if (xlb) iounmap(xlb);
|
|
}
|
|
|
|
|
|
int mpc52xx_match_psc_function(int psc_idx, const char *func)
|
|
{
|
|
struct mpc52xx_psc_func *cf = mpc52xx_psc_functions;
|
|
|
|
while ((cf->id != -1) && (cf->func != NULL)) {
|
|
if ((cf->id == psc_idx) && !strcmp(cf->func,func))
|
|
return 1;
|
|
cf++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv)
|
|
{
|
|
static spinlock_t lock = SPIN_LOCK_UNLOCKED;
|
|
struct mpc52xx_cdm __iomem *cdm;
|
|
unsigned long flags;
|
|
u16 mclken_div;
|
|
u16 __iomem *reg;
|
|
u32 mask;
|
|
|
|
cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
|
|
if (!cdm) {
|
|
printk(KERN_ERR __FILE__ ": Error mapping CDM\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
mclken_div = 0x8000 | (clkdiv & 0x1FF);
|
|
switch (psc_id) {
|
|
case 1: reg = &cdm->mclken_div_psc1; mask = 0x20; break;
|
|
case 2: reg = &cdm->mclken_div_psc2; mask = 0x40; break;
|
|
case 3: reg = &cdm->mclken_div_psc3; mask = 0x80; break;
|
|
case 6: reg = &cdm->mclken_div_psc6; mask = 0x10; break;
|
|
default:
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Set the rate and enable the clock */
|
|
spin_lock_irqsave(&lock, flags);
|
|
out_be16(reg, mclken_div);
|
|
out_be32(&cdm->clk_enables, in_be32(&cdm->clk_enables) | mask);
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
|
|
iounmap(cdm);
|
|
return 0;
|
|
}
|