| /* |
| * 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 DEFINE_SPINLOCK(lock); |
| 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; |
| } |