diff options
Diffstat (limited to 'arch/sh/boards/overdrive')
-rw-r--r-- | arch/sh/boards/overdrive/Makefile | 8 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/fpga.c | 134 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/galileo.c | 588 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/io.c | 173 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/irq.c | 192 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/led.c | 59 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/mach.c | 62 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/pcidma.c | 46 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/setup.c | 41 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/time.c | 119 |
10 files changed, 1422 insertions, 0 deletions
diff --git a/arch/sh/boards/overdrive/Makefile b/arch/sh/boards/overdrive/Makefile new file mode 100644 index 00000000000..1762b59e927 --- /dev/null +++ b/arch/sh/boards/overdrive/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the STMicroelectronics Overdrive specific parts of the kernel +# + +obj-y := mach.o setup.o io.o irq.o led.o time.o + +obj-$(CONFIG_PCI) += fpga.o galileo.o pcidma.o + diff --git a/arch/sh/boards/overdrive/fpga.c b/arch/sh/boards/overdrive/fpga.c new file mode 100644 index 00000000000..3a1ec940344 --- /dev/null +++ b/arch/sh/boards/overdrive/fpga.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2000 David J. Mckay (david.mckay@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * This file handles programming up the Altera Flex10K that interfaces to + * the Galileo, and does the PS/2 keyboard and mouse + * + */ + + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> + + +#include <asm/overdriver/gt64111.h> +#include <asm/overdrive/overdrive.h> +#include <asm/overdrive/fpga.h> + +#define FPGA_NotConfigHigh() (*FPGA_ControlReg) = (*FPGA_ControlReg) | ENABLE_FPGA_BIT +#define FPGA_NotConfigLow() (*FPGA_ControlReg) = (*FPGA_ControlReg) & RESET_FPGA_MASK + +/* I need to find out what (if any) the real delay factor here is */ +/* The delay is definately not critical */ +#define long_delay() {int i;for(i=0;i<10000;i++);} +#define short_delay() {int i;for(i=0;i<100;i++);} + +static void __init program_overdrive_fpga(const unsigned char *fpgacode, + int size) +{ + int timeout = 0; + int i, j; + unsigned char b; + static volatile unsigned char *FPGA_ControlReg = + (volatile unsigned char *) (OVERDRIVE_CTRL); + static volatile unsigned char *FPGA_ProgramReg = + (volatile unsigned char *) (FPGA_DCLK_ADDRESS); + + printk("FPGA: Commencing FPGA Programming\n"); + + /* The PCI reset but MUST be low when programming the FPGA !!! */ + b = (*FPGA_ControlReg) & RESET_PCI_MASK; + + (*FPGA_ControlReg) = b; + + /* Prepare FPGA to program */ + + FPGA_NotConfigHigh(); + long_delay(); + + FPGA_NotConfigLow(); + short_delay(); + + while ((*FPGA_ProgramReg & FPGA_NOT_STATUS) != 0) { + printk("FPGA: Waiting for NotStatus to go Low ... \n"); + } + + FPGA_NotConfigHigh(); + + /* Wait for FPGA "ready to be programmed" signal */ + printk("FPGA: Waiting for NotStatus to go high (FPGA ready)... \n"); + + for (timeout = 0; + (((*FPGA_ProgramReg & FPGA_NOT_STATUS) == 0) + && (timeout < FPGA_TIMEOUT)); timeout++); + + /* Check if timeout condition occured - i.e. an error */ + + if (timeout == FPGA_TIMEOUT) { + printk + ("FPGA: Failed to program - Timeout waiting for notSTATUS to go high\n"); + return; + } + + printk("FPGA: Copying data to FPGA ... %d bytes\n", size); + + /* Copy array to FPGA - bit at a time */ + + for (i = 0; i < size; i++) { + volatile unsigned w = 0; + + for (j = 0; j < 8; j++) { + *FPGA_ProgramReg = (fpgacode[i] >> j) & 0x01; + short_delay(); + } + if ((i & 0x3ff) == 0) { + printk("."); + } + } + + /* Waiting for CONFDONE to go high - means the program is complete */ + + for (timeout = 0; + (((*FPGA_ProgramReg & FPGA_CONFDONE) == 0) + && (timeout < FPGA_TIMEOUT)); timeout++) { + + *FPGA_ProgramReg = 0x0; + long_delay(); + } + + if (timeout == FPGA_TIMEOUT) { + printk + ("FPGA: Failed to program - Timeout waiting for CONFDONE to go high\n"); + return; + } else { /* Clock another 10 times - gets the device into a working state */ + for (i = 0; i < 10; i++) { + *FPGA_ProgramReg = 0x0; + short_delay(); + } + } + + printk("FPGA: Programming complete\n"); +} + + +static const unsigned char __init fpgacode[] = { +#include "./overdrive.ttf" /* Code from maxplus2 compiler */ + , 0, 0 +}; + + +int __init init_overdrive_fpga(void) +{ + program_overdrive_fpga(fpgacode, sizeof(fpgacode)); + + return 0; +} diff --git a/arch/sh/boards/overdrive/galileo.c b/arch/sh/boards/overdrive/galileo.c new file mode 100644 index 00000000000..276fa11ee4c --- /dev/null +++ b/arch/sh/boards/overdrive/galileo.c @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2000 David J. Mckay (david.mckay@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * This file contains the PCI routines required for the Galileo GT6411 + * PCI bridge as used on the Orion and Overdrive boards. + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/ioport.h> + +#include <asm/overdrive/overdrive.h> +#include <asm/overdrive/gt64111.h> + + +/* After boot, we shift the Galileo registers so that they appear + * in BANK6, along with IO space. This means we can have one contingous + * lump of PCI address space without these registers appearing in the + * middle of them + */ + +#define GT64111_BASE_ADDRESS 0xbb000000 +#define GT64111_IO_BASE_ADDRESS 0x1000 +/* The GT64111 registers appear at this address to the SH4 after reset */ +#define RESET_GT64111_BASE_ADDRESS 0xb4000000 + +/* Macros used to access the Galileo registers */ +#define RESET_GT64111_REG(x) (RESET_GT64111_BASE_ADDRESS+x) +#define GT64111_REG(x) (GT64111_BASE_ADDRESS+x) + +#define RESET_GT_WRITE(x,v) writel((v),RESET_GT64111_REG(x)) + +#define RESET_GT_READ(x) readl(RESET_GT64111_REG(x)) + +#define GT_WRITE(x,v) writel((v),GT64111_REG(x)) +#define GT_WRITE_BYTE(x,v) writeb((v),GT64111_REG(x)) +#define GT_WRITE_SHORT(x,v) writew((v),GT64111_REG(x)) + +#define GT_READ(x) readl(GT64111_REG(x)) +#define GT_READ_BYTE(x) readb(GT64111_REG(x)) +#define GT_READ_SHORT(x) readw(GT64111_REG(x)) + + +/* Where the various SH banks start at */ +#define SH_BANK4_ADR 0xb0000000 +#define SH_BANK5_ADR 0xb4000000 +#define SH_BANK6_ADR 0xb8000000 + +/* Masks out everything but lines 28,27,26 */ +#define BANK_SELECT_MASK 0x1c000000 + +#define SH4_TO_BANK(x) ( (x) & BANK_SELECT_MASK) + +/* + * Masks used for address conversaion. Bank 6 is used for IO and + * has all the address bits zeroed by the FPGA. Special case this + */ +#define MEMORY_BANK_MASK 0x1fffffff +#define IO_BANK_MASK 0x03ffffff + +/* Mark bank 6 as the bank used for IO. You can change this in the FPGA code + * if you want + */ +#define IO_BANK_ADR PCI_GTIO_BASE + +/* Will select the correct mask to apply depending on the SH$ address */ +#define SELECT_BANK_MASK(x) \ + ( (SH4_TO_BANK(x)==SH4_TO_BANK(IO_BANK_ADR)) ? IO_BANK_MASK : MEMORY_BANK_MASK) + +/* Converts between PCI space and P2 region */ +#define SH4_TO_PCI(x) ((x)&SELECT_BANK_MASK(x)) + +/* Various macros for figuring out what to stick in the Galileo registers. + * You *really* don't want to figure this stuff out by hand, you always get + * it wrong + */ +#define GT_MEM_LO_ADR(x) ((((unsigned)((x)&SELECT_BANK_MASK(x)))>>21)&0x7ff) +#define GT_MEM_HI_ADR(x) ((((unsigned)((x)&SELECT_BANK_MASK(x)))>>21)&0x7f) +#define GT_MEM_SUB_ADR(x) ((((unsigned)((x)&SELECT_BANK_MASK(x)))>>20)&0xff) + +#define PROGRAM_HI_LO(block,a,s) \ + GT_WRITE(block##_LO_DEC_ADR,GT_MEM_LO_ADR(a));\ + GT_WRITE(block##_HI_DEC_ADR,GT_MEM_HI_ADR(a+s-1)) + +#define PROGRAM_SUB_HI_LO(block,a,s) \ + GT_WRITE(block##_LO_DEC_ADR,GT_MEM_SUB_ADR(a));\ + GT_WRITE(block##_HI_DEC_ADR,GT_MEM_SUB_ADR(a+s-1)) + +/* We need to set the size, and the offset register */ + +#define GT_BAR_MASK(x) ((x)&~0xfff) + +/* Macro to set up the BAR in the Galileo. Essentially used for the DRAM */ +#define PROGRAM_GT_BAR(block,a,s) \ + GT_WRITE(PCI_##block##_BANK_SIZE,GT_BAR_MASK((s-1)));\ + write_config_to_galileo(PCI_CONFIG_##block##_BASE_ADR,\ + GT_BAR_MASK(a)) + +#define DISABLE_GT_BAR(block) \ + GT_WRITE(PCI_##block##_BANK_SIZE,0),\ + GT_CONFIG_WRITE(PCI_CONFIG_##block##_BASE_ADR,\ + 0x80000000) + +/* Macros to disable things we are not going to use */ +#define DISABLE_DECODE(x) GT_WRITE(x##_LO_DEC_ADR,0x7ff);\ + GT_WRITE(x##_HI_DEC_ADR,0x00) + +#define DISABLE_SUB_DECODE(x) GT_WRITE(x##_LO_DEC_ADR,0xff);\ + GT_WRITE(x##_HI_DEC_ADR,0x00) + +static void __init reset_pci(void) +{ + /* Set RESET_PCI bit high */ + writeb(readb(OVERDRIVE_CTRL) | ENABLE_PCI_BIT, OVERDRIVE_CTRL); + udelay(250); + + /* Set RESET_PCI bit low */ + writeb(readb(OVERDRIVE_CTRL) & RESET_PCI_MASK, OVERDRIVE_CTRL); + udelay(250); + + writeb(readb(OVERDRIVE_CTRL) | ENABLE_PCI_BIT, OVERDRIVE_CTRL); + udelay(250); +} + +static int write_config_to_galileo(int where, u32 val); +#define GT_CONFIG_WRITE(where,val) write_config_to_galileo(where,val) + +#define ENABLE_PCI_DRAM + + +#ifdef TEST_DRAM +/* Test function to check out if the PCI DRAM is working OK */ +static int /* __init */ test_dram(unsigned *base, unsigned size) +{ + unsigned *p = base; + unsigned *end = (unsigned *) (((unsigned) base) + size); + unsigned w; + + for (p = base; p < end; p++) { + *p = 0xffffffff; + if (*p != 0xffffffff) { + printk("AAARGH -write failed!!! at %p is %x\n", p, + *p); + return 0; + } + *p = 0x0; + if (*p != 0x0) { + printk("AAARGH -write failed!!!\n"); + return 0; + } + } + + for (p = base; p < end; p++) { + *p = (unsigned) p; + if (*p != (unsigned) p) { + printk("Failed at 0x%p, actually is 0x%x\n", p, + *p); + return 0; + } + } + + for (p = base; p < end; p++) { + w = ((unsigned) p & 0xffff0000); + *p = w | (w >> 16); + } + + for (p = base; p < end; p++) { + w = ((unsigned) p & 0xffff0000); + w |= (w >> 16); + if (*p != w) { + printk + ("Failed at 0x%p, should be 0x%x actually is 0x%x\n", + p, w, *p); + return 0; + } + } + + return 1; +} +#endif + + +/* Function to set up and initialise the galileo. This sets up the BARS, + * maps the DRAM into the address space etc,etc + */ +int __init galileo_init(void) +{ + reset_pci(); + + /* Now shift the galileo regs into this block */ + RESET_GT_WRITE(INTERNAL_SPACE_DEC, + GT_MEM_LO_ADR(GT64111_BASE_ADDRESS)); + + /* Should have a sanity check here, that you can read back at the new + * address what you just wrote + */ + + /* Disable decode for all regions */ + DISABLE_DECODE(RAS10); + DISABLE_DECODE(RAS32); + DISABLE_DECODE(CS20); + DISABLE_DECODE(CS3); + DISABLE_DECODE(PCI_IO); + DISABLE_DECODE(PCI_MEM0); + DISABLE_DECODE(PCI_MEM1); + + /* Disable all BARS */ + GT_WRITE(BAR_ENABLE_ADR, 0x1ff); + DISABLE_GT_BAR(RAS10); + DISABLE_GT_BAR(RAS32); + DISABLE_GT_BAR(CS20); + DISABLE_GT_BAR(CS3); + + /* Tell the BAR where the IO registers now are */ + GT_CONFIG_WRITE(PCI_CONFIG_INT_REG_IO_ADR,GT_BAR_MASK( + (GT64111_IO_BASE_ADDRESS & + IO_BANK_MASK))); + /* set up a 112 Mb decode */ + PROGRAM_HI_LO(PCI_MEM0, SH_BANK4_ADR, 112 * 1024 * 1024); + + /* Set up a 32 MB io space decode */ + PROGRAM_HI_LO(PCI_IO, IO_BANK_ADR, 32 * 1024 * 1024); + +#ifdef ENABLE_PCI_DRAM + /* Program up the DRAM configuration - there is DRAM only in bank 0 */ + /* Now set up the DRAM decode */ + PROGRAM_HI_LO(RAS10, PCI_DRAM_BASE, PCI_DRAM_SIZE); + /* And the sub decode */ + PROGRAM_SUB_HI_LO(RAS0, PCI_DRAM_BASE, PCI_DRAM_SIZE); + + DISABLE_SUB_DECODE(RAS1); + + /* Set refresh rate */ + GT_WRITE(DRAM_BANK0_PARMS, 0x3f); + GT_WRITE(DRAM_CFG, 0x100); + + /* we have to lob off the top bits rememeber!! */ + PROGRAM_GT_BAR(RAS10, SH4_TO_PCI(PCI_DRAM_BASE), PCI_DRAM_SIZE); + +#endif + + /* We are only interested in decoding RAS10 and the Galileo's internal + * registers (as IO) on the PCI bus + */ +#ifdef ENABLE_PCI_DRAM + GT_WRITE(BAR_ENABLE_ADR, (~((1 << 8) | (1 << 3))) & 0x1ff); +#else + GT_WRITE(BAR_ENABLE_ADR, (~(1 << 3)) & 0x1ff); +#endif + + /* Change the class code to host bridge, it actually powers up + * as a memory controller + */ + GT_CONFIG_WRITE(8, 0x06000011); + + /* Allow the galileo to master the PCI bus */ + GT_CONFIG_WRITE(PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_IO); + + +#if 0 + printk("Testing PCI DRAM - "); + if(test_dram(PCI_DRAM_BASE,PCI_DRAM_SIZE)) { + printk("Passed\n"); + }else { + printk("FAILED\n"); + } +#endif + return 0; + +} + + +#define SET_CONFIG_BITS(bus,devfn,where)\ + ((1<<31) | ((bus) << 16) | ((devfn) << 8) | ((where) & ~3)) + +#define CONFIG_CMD(dev, where) SET_CONFIG_BITS((dev)->bus->number,(dev)->devfn,where) + +/* This write to the galileo config registers, unlike the functions below, can + * be used before the PCI subsystem has started up + */ +static int __init write_config_to_galileo(int where, u32 val) +{ + GT_WRITE(PCI_CFG_ADR, SET_CONFIG_BITS(0, 0, where)); + + GT_WRITE(PCI_CFG_DATA, val); + return 0; +} + +/* We exclude the galileo and slot 31, the galileo because I don't know how to stop + * the setup code shagging up the setup I have done on it, and 31 because the whole + * thing locks up if you try to access that slot (which doesn't exist of course anyway + */ + +#define EXCLUDED_DEV(dev) ((dev->bus->number==0) && ((PCI_SLOT(dev->devfn)==0) || (PCI_SLOT(dev->devfn) == 31))) + +static int galileo_read_config_byte(struct pci_dev *dev, int where, + u8 * val) +{ + + + /* I suspect this doesn't work because this drives a special cycle ? */ + if (EXCLUDED_DEV(dev)) { + *val = 0xff; + return PCIBIOS_SUCCESSFUL; + } + /* Start the config cycle */ + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + /* Read back the result */ + *val = GT_READ_BYTE(PCI_CFG_DATA + (where & 3)); + + return PCIBIOS_SUCCESSFUL; +} + + +static int galileo_read_config_word(struct pci_dev *dev, int where, + u16 * val) +{ + + if (EXCLUDED_DEV(dev)) { + *val = 0xffff; + return PCIBIOS_SUCCESSFUL; + } + + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + *val = GT_READ_SHORT(PCI_CFG_DATA + (where & 2)); + + return PCIBIOS_SUCCESSFUL; +} + + +static int galileo_read_config_dword(struct pci_dev *dev, int where, + u32 * val) +{ + if (EXCLUDED_DEV(dev)) { + *val = 0xffffffff; + return PCIBIOS_SUCCESSFUL; + } + + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + *val = GT_READ(PCI_CFG_DATA); + + return PCIBIOS_SUCCESSFUL; +} + +static int galileo_write_config_byte(struct pci_dev *dev, int where, + u8 val) +{ + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + + GT_WRITE_BYTE(PCI_CFG_DATA + (where & 3), val); + + return PCIBIOS_SUCCESSFUL; +} + + +static int galileo_write_config_word(struct pci_dev *dev, int where, + u16 val) +{ + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + + GT_WRITE_SHORT(PCI_CFG_DATA + (where & 2), val); + + return PCIBIOS_SUCCESSFUL; +} + +static int galileo_write_config_dword(struct pci_dev *dev, int where, + u32 val) +{ + GT_WRITE(PCI_CFG_ADR, CONFIG_CMD(dev, where)); + + GT_WRITE(PCI_CFG_DATA, val); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops pci_config_ops = { + galileo_read_config_byte, + galileo_read_config_word, + galileo_read_config_dword, + galileo_write_config_byte, + galileo_write_config_word, + galileo_write_config_dword +}; + + +/* Everything hangs off this */ +static struct pci_bus *pci_root_bus; + + +static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin) +{ + return PCI_SLOT(dev->devfn); +} + +static int __init map_od_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + /* Slot 1: Galileo + * Slot 2: PCI Slot 1 + * Slot 3: PCI Slot 2 + * Slot 4: ESS + */ + switch (slot) { + case 2: + return OVERDRIVE_PCI_IRQ1; + case 3: + /* Note this assumes you have a hacked card in slot 2 */ + return OVERDRIVE_PCI_IRQ2; + case 4: + return OVERDRIVE_ESS_IRQ; + default: + /* printk("PCI: Unexpected IRQ mapping request for slot %d\n", slot); */ + return -1; + } +} + + + +void __init +pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) +{ + ranges->io_start -= bus->resource[0]->start; + ranges->io_end -= bus->resource[0]->start; + ranges->mem_start -= bus->resource[1]->start; + ranges->mem_end -= bus->resource[1]->start; +} + +static void __init pci_fixup_ide_bases(struct pci_dev *d) +{ + int i; + + /* + * PCI IDE controllers use non-standard I/O port decoding, respect it. + */ + if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + printk("PCI: IDE base address fixup for %s\n", pci_name(d)); + for(i=0; i<4; i++) { + struct resource *r = &d->resource[i]; + if ((r->start & ~0x80) == 0x374) { + r->start |= 2; + r->end = r->start; + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); + +void __init pcibios_init(void) +{ + static struct resource galio,galmem; + + /* Allocate the registers used by the Galileo */ + galio.flags = IORESOURCE_IO; + galio.name = "Galileo GT64011"; + galmem.flags = IORESOURCE_MEM|IORESOURCE_PREFETCH; + galmem.name = "Galileo GT64011 DRAM"; + + allocate_resource(&ioport_resource, &galio, 256, + GT64111_IO_BASE_ADDRESS,GT64111_IO_BASE_ADDRESS+256, 256, NULL, NULL); + allocate_resource(&iomem_resource, &galmem,PCI_DRAM_SIZE, + PHYSADDR(PCI_DRAM_BASE), PHYSADDR(PCI_DRAM_BASE)+PCI_DRAM_SIZE, + PCI_DRAM_SIZE, NULL, NULL); + + /* ok, do the scan man */ + pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL); + + pci_assign_unassigned_resources(); + pci_fixup_irqs(no_swizzle, map_od_irq); + +#ifdef TEST_DRAM + printk("Testing PCI DRAM - "); + if(test_dram(PCI_DRAM_BASE,PCI_DRAM_SIZE)) { + printk("Passed\n"); + }else { + printk("FAILED\n"); + } +#endif + +} + +char * __init pcibios_setup(char *str) +{ + return str; +} + + + +int pcibios_enable_device(struct pci_dev *dev) +{ + + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx = 0; idx < 6; idx++) { + r = dev->resource + idx; + if (!r->start && r->end) { + printk(KERN_ERR + "PCI: Device %s not available because" + " of resource collisions\n", + pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: enabling device %s (%04x -> %04x)\n", + pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; + +} + +/* We should do some optimisation work here I think. Ok for now though */ +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + +} + +void pcibios_align_resource(void *data, struct resource *res, + unsigned long size) +{ +} + +void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + + unsigned long where, size; + u32 reg; + + + printk("PCI: Assigning %3s %08lx to %s\n", + res->flags & IORESOURCE_IO ? "IO" : "MEM", + res->start, dev->name); + + where = PCI_BASE_ADDRESS_0 + resource * 4; + size = res->end - res->start; + + pci_read_config_dword(dev, where, ®); + reg = (reg & size) | (((u32) (res->start - root->start)) & ~size); + pci_write_config_dword(dev, where, reg); +} + + +void __init pcibios_update_irq(struct pci_dev *dev, int irq) +{ + printk("PCI: Assigning IRQ %02d to %s\n", irq, dev->name); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk("PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} diff --git a/arch/sh/boards/overdrive/io.c b/arch/sh/boards/overdrive/io.c new file mode 100644 index 00000000000..65f3fd0563d --- /dev/null +++ b/arch/sh/boards/overdrive/io.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2000 David J. Mckay (david.mckay@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * This file contains the I/O routines for use on the overdrive board + * + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/addrspace.h> + +#include <asm/overdrive/overdrive.h> + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the SuperH architecture, we just read/write the + * memory location directly. + */ + +#define dprintk(x...) + +/* Translates an IO address to where it is mapped in memory */ + +#define io_addr(x) (((unsigned)(x))|PCI_GTIO_BASE) + +unsigned char od_inb(unsigned long port) +{ +dprintk("od_inb(%x)\n", port); + return readb(io_addr(port)) & 0xff; +} + + +unsigned short od_inw(unsigned long port) +{ +dprintk("od_inw(%x)\n", port); + return readw(io_addr(port)) & 0xffff; +} + +unsigned int od_inl(unsigned long port) +{ +dprintk("od_inl(%x)\n", port); + return readl(io_addr(port)); +} + +void od_outb(unsigned char value, unsigned long port) +{ +dprintk("od_outb(%x, %x)\n", value, port); + writeb(value, io_addr(port)); +} + +void od_outw(unsigned short value, unsigned long port) +{ +dprintk("od_outw(%x, %x)\n", value, port); + writew(value, io_addr(port)); +} + +void od_outl(unsigned int value, unsigned long port) +{ +dprintk("od_outl(%x, %x)\n", value, port); + writel(value, io_addr(port)); +} + +/* This is horrible at the moment - needs more work to do something sensible */ +#define IO_DELAY() udelay(10) + +#define OUT_DELAY(x,type) \ +void od_out##x##_p(unsigned type value,unsigned long port){out##x(value,port);IO_DELAY();} + +#define IN_DELAY(x,type) \ +unsigned type od_in##x##_p(unsigned long port) {unsigned type tmp=in##x(port);IO_DELAY();return tmp;} + + +OUT_DELAY(b,char) +OUT_DELAY(w,short) +OUT_DELAY(l,int) + +IN_DELAY(b,char) +IN_DELAY(w,short) +IN_DELAY(l,int) + + +/* Now for the string version of these functions */ +void od_outsb(unsigned long port, const void *addr, unsigned long count) +{ + int i; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p++) { + outb(*p, port); + } +} + + +void od_insb(unsigned long port, void *addr, unsigned long count) +{ + int i; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p++) { + *p = inb(port); + } +} + +/* For the 16 and 32 bit string functions, we have to worry about alignment. + * The SH does not do unaligned accesses, so we have to read as bytes and + * then write as a word or dword. + * This can be optimised a lot more, especially in the case where the data + * is aligned + */ + +void od_outsw(unsigned long port, const void *addr, unsigned long count) +{ + int i; + unsigned short tmp; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p += 2) { + tmp = (*p) | ((*(p + 1)) << 8); + outw(tmp, port); + } +} + + +void od_insw(unsigned long port, void *addr, unsigned long count) +{ + int i; + unsigned short tmp; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p += 2) { + tmp = inw(port); + p[0] = tmp & 0xff; + p[1] = (tmp >> 8) & 0xff; + } +} + + +void od_outsl(unsigned long port, const void *addr, unsigned long count) +{ + int i; + unsigned tmp; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p += 4) { + tmp = (*p) | ((*(p + 1)) << 8) | ((*(p + 2)) << 16) | + ((*(p + 3)) << 24); + outl(tmp, port); + } +} + + +void od_insl(unsigned long port, void *addr, unsigned long count) +{ + int i; + unsigned tmp; + unsigned char *p = (unsigned char *) addr; + + for (i = 0; i < count; i++, p += 4) { + tmp = inl(port); + p[0] = tmp & 0xff; + p[1] = (tmp >> 8) & 0xff; + p[2] = (tmp >> 16) & 0xff; + p[3] = (tmp >> 24) & 0xff; + + } +} diff --git a/arch/sh/boards/overdrive/irq.c b/arch/sh/boards/overdrive/irq.c new file mode 100644 index 00000000000..23adc6be71e --- /dev/null +++ b/arch/sh/boards/overdrive/irq.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2000 David J. Mckay (david.mckay@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Looks after interrupts on the overdrive board. + * + * Bases on the IPR irq system + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/irq.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include <asm/overdrive/overdrive.h> + +struct od_data { + int overdrive_irq; + int irq_mask; +}; + +#define NUM_EXTERNAL_IRQS 16 +#define EXTERNAL_IRQ_NOT_IN_USE (-1) +#define EXTERNAL_IRQ_NOT_ASSIGNED (-1) + +/* + * This table is used to determine what to program into the FPGA's CT register + * for the specified Linux IRQ. + * + * The irq_mask gives the interrupt number from the PCI board (PCI_Int(6:0)) + * but is one greater than that because the because the FPGA treats 0 + * as disabled, a value of 1 asserts PCI_Int0, and so on. + * + * The overdrive_irq specifies which of the eight interrupt sources generates + * that interrupt, and but is multiplied by four to give the bit offset into + * the CT register. + * + * The seven interrupts levels (SH4 IRL's) we have available here is hardwired + * by the EPLD. The assignments here of which PCI interrupt generates each + * level is arbitary. + */ +static struct od_data od_data_table[NUM_EXTERNAL_IRQS] = { + /* overdrive_irq , irq_mask */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 0 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 7}, /* 1 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 6}, /* 2 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 3 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 5}, /* 4 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 5 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 6 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 4}, /* 7 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 8 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 9 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 3}, /* 10 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 2}, /* 11 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 12 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, 1}, /* 13 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 14 */ + {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE} /* 15 */ +}; + +static void set_od_data(int overdrive_irq, int irq) +{ + if (irq >= NUM_EXTERNAL_IRQS || irq < 0) + return; + od_data_table[irq].overdrive_irq = overdrive_irq << 2; +} + +static void enable_od_irq(unsigned int irq); +void disable_od_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_od_irq disable_od_irq + +static void mask_and_ack_od(unsigned int); +static void end_od_irq(unsigned int irq); + +static unsigned int startup_od_irq(unsigned int irq) +{ + enable_od_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type od_irq_type = { + "Overdrive-IRQ", + startup_od_irq, + shutdown_od_irq, + enable_od_irq, + disable_od_irq, + mask_and_ack_od, + end_od_irq +}; + +static void disable_od_irq(unsigned int irq) +{ + unsigned val, flags; + int overdrive_irq; + unsigned mask; + + /* Not a valid interrupt */ + if (irq < 0 || irq >= NUM_EXTERNAL_IRQS) + return; + + /* Is is necessary to use a cli here? Would a spinlock not be + * mroe efficient? + */ + local_irq_save(flags); + overdrive_irq = od_data_table[irq].overdrive_irq; + if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) { + mask = ~(0x7 << overdrive_irq); + val = ctrl_inl(OVERDRIVE_INT_CT); + val &= mask; + ctrl_outl(val, OVERDRIVE_INT_CT); + } + local_irq_restore(flags); +} + +static void enable_od_irq(unsigned int irq) +{ + unsigned val, flags; + int overdrive_irq; + unsigned mask; + + /* Not a valid interrupt */ + if (irq < 0 || irq >= NUM_EXTERNAL_IRQS) + return; + + /* Set priority in OD back to original value */ + local_irq_save(flags); + /* This one is not in use currently */ + overdrive_irq = od_data_table[irq].overdrive_irq; + if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) { + val = ctrl_inl(OVERDRIVE_INT_CT); + mask = ~(0x7 << overdrive_irq); + val &= mask; + mask = od_data_table[irq].irq_mask << overdrive_irq; + val |= mask; + ctrl_outl(val, OVERDRIVE_INT_CT); + } + local_irq_restore(flags); +} + + + +/* this functions sets the desired irq handler to be an overdrive type */ +static void __init make_od_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &od_irq_type; + disable_od_irq(irq); +} + + +static void mask_and_ack_od(unsigned int irq) +{ + disable_od_irq(irq); +} + +static void end_od_irq(unsigned int irq) +{ + enable_od_irq(irq); +} + +void __init init_overdrive_irq(void) +{ + int i; + + /* Disable all interrupts */ + ctrl_outl(0, OVERDRIVE_INT_CT); + + /* Update interrupt pin mode to use encoded interrupts */ + i = ctrl_inw(INTC_ICR); + i &= ~INTC_ICR_IRLM; + ctrl_outw(i, INTC_ICR); + + for (i = 0; i < NUM_EXTERNAL_IRQS; i++) { + if (od_data_table[i].irq_mask != EXTERNAL_IRQ_NOT_IN_USE) { + make_od_irq(i); + } else if (i != 15) { // Cannot use imask on level 15 + make_imask_irq(i); + } + } + + /* Set up the interrupts */ + set_od_data(OVERDRIVE_PCI_INTA, OVERDRIVE_PCI_IRQ1); + set_od_data(OVERDRIVE_PCI_INTB, OVERDRIVE_PCI_IRQ2); + set_od_data(OVERDRIVE_AUDIO_INT, OVERDRIVE_ESS_IRQ); +} diff --git a/arch/sh/boards/overdrive/led.c b/arch/sh/boards/overdrive/led.c new file mode 100644 index 00000000000..734742e9227 --- /dev/null +++ b/arch/sh/boards/overdrive/led.c @@ -0,0 +1,59 @@ +/* + * linux/arch/sh/overdrive/led.c + * + * Copyright (C) 1999 Stuart Menefy <stuart.menefy@st.com> + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * This file contains an Overdrive specific LED feature. + */ + +#include <linux/config.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/overdrive/overdrive.h> + +static void mach_led(int position, int value) +{ + unsigned long flags; + unsigned long reg; + + local_irq_save(flags); + + reg = readl(OVERDRIVE_CTRL); + if (value) { + reg |= (1<<3); + } else { + reg &= ~(1<<3); + } + writel(reg, OVERDRIVE_CTRL); + + local_irq_restore(flags); +} + +#ifdef CONFIG_HEARTBEAT + +#include <linux/sched.h> + +/* acts like an actual heart beat -- ie thump-thump-pause... */ +void heartbeat_od(void) +{ + static unsigned cnt = 0, period = 0, dist = 0; + + if (cnt == 0 || cnt == dist) + mach_led( -1, 1); + else if (cnt == 7 || cnt == dist+7) + mach_led( -1, 0); + + if (++cnt > period) { + cnt = 0; + /* The hyperbolic function below modifies the heartbeat period + * length in dependency of the current (5min) load. It goes + * through the points f(0)=126, f(1)=86, f(5)=51, + * f(inf)->30. */ + period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30; + dist = period / 4; + } +} +#endif /* CONFIG_HEARTBEAT */ diff --git a/arch/sh/boards/overdrive/mach.c b/arch/sh/boards/overdrive/mach.c new file mode 100644 index 00000000000..2834a03ae47 --- /dev/null +++ b/arch/sh/boards/overdrive/mach.c @@ -0,0 +1,62 @@ +/* + * linux/arch/sh/overdrive/mach.c + * + * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Machine vector for the STMicroelectronics Overdrive + */ + +#include <linux/init.h> + +#include <asm/machvec.h> +#include <asm/rtc.h> +#include <asm/machvec_init.h> + +#include <asm/io_unknown.h> +#include <asm/io_generic.h> +#include <asm/overdrive/io.h> + +void heartbeat_od(void); +void init_overdrive_irq(void); +void galileo_pcibios_init(void); + +/* + * The Machine Vector + */ + +struct sh_machine_vector mv_od __initmv = { + .mv_nr_irqs = 48, + + .mv_inb = od_inb, + .mv_inw = od_inw, + .mv_inl = od_inl, + .mv_outb = od_outb, + .mv_outw = od_outw, + .mv_outl = od_outl, + + .mv_inb_p = od_inb_p, + .mv_inw_p = od_inw_p, + .mv_inl_p = od_inl_p, + .mv_outb_p = od_outb_p, + .mv_outw_p = od_outw_p, + .mv_outl_p = od_outl_p, + + .mv_insb = od_insb, + .mv_insw = od_insw, + .mv_insl = od_insl, + .mv_outsb = od_outsb, + .mv_outsw = od_outsw, + .mv_outsl = od_outsl, + +#ifdef CONFIG_PCI + .mv_init_irq = init_overdrive_irq, +#endif +#ifdef CONFIG_HEARTBEAT + .mv_heartbeat = heartbeat_od, +#endif +}; + +ALIAS_MV(od) diff --git a/arch/sh/boards/overdrive/pcidma.c b/arch/sh/boards/overdrive/pcidma.c new file mode 100644 index 00000000000..1c9bfeda00b --- /dev/null +++ b/arch/sh/boards/overdrive/pcidma.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 David J. Mckay (david.mckay@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Dynamic DMA mapping support. + * + * On the overdrive, we can only DMA from memory behind the PCI bus! + * this means that all DMA'able memory must come from there. + * this restriction will not apply to later boards. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <asm/io.h> + +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t * dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + printk("BUG: pci_alloc_consistent() called - not yet supported\n"); + /* We ALWAYS need DMA memory on the overdrive hardware, + * due to it's extreme weirdness + * Need to flush the cache here as well, since the memory + * can still be seen through the cache! + */ + gfp |= GFP_DMA; + ret = (void *) __get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long) vaddr, get_order(size)); +} diff --git a/arch/sh/boards/overdrive/setup.c b/arch/sh/boards/overdrive/setup.c new file mode 100644 index 00000000000..a36ce0284ed --- /dev/null +++ b/arch/sh/boards/overdrive/setup.c @@ -0,0 +1,41 @@ +/* + * arch/sh/overdrive/setup.c + * + * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * STMicroelectronics Overdrive Support. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> + +#include <asm/overdrive/overdrive.h> +#include <asm/overdrive/fpga.h> + +extern void od_time_init(void); + +const char *get_system_type(void) +{ + return "SH7750 Overdrive"; +} + +/* + * Initialize the board + */ +int __init platform_setup(void) +{ +#ifdef CONFIG_PCI + init_overdrive_fpga(); + galileo_init(); +#endif + + board_time_init = od_time_init; + + /* Enable RS232 receive buffers */ + writel(0x1e, OVERDRIVE_CTRL); +} diff --git a/arch/sh/boards/overdrive/time.c b/arch/sh/boards/overdrive/time.c new file mode 100644 index 00000000000..68533690e09 --- /dev/null +++ b/arch/sh/boards/overdrive/time.c @@ -0,0 +1,119 @@ +/* + * arch/sh/boards/overdrive/time.c + * + * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com) + * Copyright (C) 2002 Paul Mundt (lethal@chaoticdreams.org) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * STMicroelectronics Overdrive Support. + */ + +void od_time_init(void) +{ + struct frqcr_data { + unsigned short frqcr; + struct { + unsigned char multiplier; + unsigned char divisor; + } factor[3]; + }; + + static struct frqcr_data st40_frqcr_table[] = { + { 0x000, {{1,1}, {1,1}, {1,2}}}, + { 0x002, {{1,1}, {1,1}, {1,4}}}, + { 0x004, {{1,1}, {1,1}, {1,8}}}, + { 0x008, {{1,1}, {1,2}, {1,2}}}, + { 0x00A, {{1,1}, {1,2}, {1,4}}}, + { 0x00C, {{1,1}, {1,2}, {1,8}}}, + { 0x011, {{1,1}, {2,3}, {1,6}}}, + { 0x013, {{1,1}, {2,3}, {1,3}}}, + { 0x01A, {{1,1}, {1,2}, {1,4}}}, + { 0x01C, {{1,1}, {1,2}, {1,8}}}, + { 0x023, {{1,1}, {2,3}, {1,3}}}, + { 0x02C, {{1,1}, {1,2}, {1,8}}}, + { 0x048, {{1,2}, {1,2}, {1,4}}}, + { 0x04A, {{1,2}, {1,2}, {1,6}}}, + { 0x04C, {{1,2}, {1,2}, {1,8}}}, + { 0x05A, {{1,2}, {1,3}, {1,6}}}, + { 0x05C, {{1,2}, {1,3}, {1,6}}}, + { 0x063, {{1,2}, {1,4}, {1,4}}}, + { 0x06C, {{1,2}, {1,4}, {1,8}}}, + { 0x091, {{1,3}, {1,3}, {1,6}}}, + { 0x093, {{1,3}, {1,3}, {1,6}}}, + { 0x0A3, {{1,3}, {1,6}, {1,6}}}, + { 0x0DA, {{1,4}, {1,4}, {1,8}}}, + { 0x0DC, {{1,4}, {1,4}, {1,8}}}, + { 0x0EC, {{1,4}, {1,8}, {1,8}}}, + { 0x123, {{1,4}, {1,4}, {1,8}}}, + { 0x16C, {{1,4}, {1,8}, {1,8}}}, + }; + + struct memclk_data { + unsigned char multiplier; + unsigned char divisor; + }; + static struct memclk_data st40_memclk_table[8] = { + {1,1}, // 000 + {1,2}, // 001 + {1,3}, // 010 + {2,3}, // 011 + {1,4}, // 100 + {1,6}, // 101 + {1,8}, // 110 + {1,8} // 111 + }; + + unsigned long pvr; + + /* + * This should probably be moved into the SH3 probing code, and then + * use the processor structure to determine which CPU we are running + * on. + */ + pvr = ctrl_inl(CCN_PVR); + printk("PVR %08x\n", pvr); + + if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1) { + /* + * Unfortunatly the STB1 FRQCR values are different from the + * 7750 ones. + */ + struct frqcr_data *d; + int a; + unsigned long memclkcr; + struct memclk_data *e; + + for (a=0; a<ARRAY_SIZE(st40_frqcr_table); a++) { + d = &st40_frqcr_table[a]; + if (d->frqcr == (frqcr & 0x1ff)) + break; + } + if (a == ARRAY_SIZE(st40_frqcr_table)) { + d = st40_frqcr_table; + printk("ERROR: Unrecognised FRQCR value, using default multipliers\n"); + } + + memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR); + e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK]; + + printk("Clock multipliers: CPU: %d/%d Bus: %d/%d Mem: %d/%d Periph: %d/%d\n", + d->factor[0].multiplier, d->factor[0].divisor, + d->factor[1].multiplier, d->factor[1].divisor, + e->multiplier, e->divisor, + d->factor[2].multiplier, d->factor[2].divisor); + + current_cpu_data.master_clock = current_cpu_data.module_clock * + d->factor[2].divisor / + d->factor[2].multiplier; + current_cpu_data.bus_clock = current_cpu_data.master_clock * + d->factor[1].multiplier / + d->factor[1].divisor; + current_cpu_data.memory_clock = current_cpu_data.master_clock * + e->multiplier / e->divisor; + current_cpu_data.cpu_clock = current_cpu_data.master_clock * + d->factor[0].multiplier / + d->factor[0].divisor; +} + |