diff options
Diffstat (limited to 'arch/mips/ddb5xxx/common')
-rw-r--r-- | arch/mips/ddb5xxx/common/Makefile | 18 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/irq_cpu.c | 115 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/nile4.c | 135 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/pci.c | 179 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/pci_auto.c | 396 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/prom.c | 58 | ||||
-rw-r--r-- | arch/mips/ddb5xxx/common/rtc_ds1386.c | 267 |
7 files changed, 1168 insertions, 0 deletions
diff --git a/arch/mips/ddb5xxx/common/Makefile b/arch/mips/ddb5xxx/common/Makefile new file mode 100644 index 000000000..fc625d7dd --- /dev/null +++ b/arch/mips/ddb5xxx/common/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the common code of NEC DDB-Vrc5xxx board +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +O_TARGET:= ddb5xxx.o + +obj-y += irq_cpu.o nile4.o prom.o pci.o pci_auto.o rtc_ds1386.o + +include $(TOPDIR)/Rules.make diff --git a/arch/mips/ddb5xxx/common/irq_cpu.c b/arch/mips/ddb5xxx/common/irq_cpu.c new file mode 100644 index 000000000..7776ef181 --- /dev/null +++ b/arch/mips/ddb5xxx/common/irq_cpu.c @@ -0,0 +1,115 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/common/irq_cpu.c + * This file define the irq handler for MIPS CPU interrupts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + *********************************************************************** + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts. The last one is + * usually cpu timer interrupt if coutner register is present. + * + * This file exports one global function: + * mips_cpu_irq_init(u32 irq_base); + */ + +#include <linux/irq.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <asm/mipsregs.h> + +/* [jsun] sooner or later we should move this debug stuff to MIPS common */ +#include <asm/ddb5xxx/debug.h> + +static int mips_cpu_irq_base=-1; + +static void +mips_cpu_irq_enable(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + clear_cp0_cause( 1 << (irq - mips_cpu_irq_base + 8)); + set_cp0_status(1 << (irq - mips_cpu_irq_base + 8)); +} + +static void +mips_cpu_irq_disable(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + clear_cp0_status(1 << (irq - mips_cpu_irq_base + 8)); +} + +static unsigned int mips_cpu_irq_startup(unsigned int irq) +{ + mips_cpu_irq_enable(irq); + return 0; +} + +#define mips_cpu_irq_shutdown mips_cpu_irq_disable + +static void +mips_cpu_irq_ack(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + + /* although we attemp to clear the IP bit in cause reigster, I think + * usually it is cleared by device (irq source) + */ + clear_cp0_cause( 1 << (irq - mips_cpu_irq_base + 8)); + + /* I am not fully convinced that I should disable irq here */ +} + +static void +mips_cpu_irq_end(unsigned int irq) +{ + MIPS_ASSERT(mips_cpu_irq_base != -1); + MIPS_ASSERT(irq >= mips_cpu_irq_base); + MIPS_ASSERT(irq < mips_cpu_irq_base+8); + /* I am not fully convinced that I should enable irq here */ +} + +static hw_irq_controller mips_cpu_irq_controller = { + "CPU_irq", + mips_cpu_irq_startup, + mips_cpu_irq_shutdown, + mips_cpu_irq_enable, + mips_cpu_irq_disable, + mips_cpu_irq_ack, + mips_cpu_irq_end, + NULL /* no affinity stuff for UP */ +}; + + +void +mips_cpu_irq_init(u32 irq_base) +{ + extern irq_desc_t irq_desc[]; + u32 i; + + for (i= irq_base; i< irq_base+8; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &mips_cpu_irq_controller; + } + + mips_cpu_irq_base = irq_base; +} diff --git a/arch/mips/ddb5xxx/common/nile4.c b/arch/mips/ddb5xxx/common/nile4.c new file mode 100644 index 000000000..e8a8d013b --- /dev/null +++ b/arch/mips/ddb5xxx/common/nile4.c @@ -0,0 +1,135 @@ +/*********************************************************************** + * + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/common/nile4.c + * misc low-level routines for vrc-5xxx controllers. + * + * derived from original code by Geert Uytterhoeven <geert@sonycom.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + *********************************************************************** + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <asm/ddb5xxx/ddb5xxx.h> +#include <asm/ddb5xxx/debug.h> + +u32 +ddb_calc_pdar(u32 phys, u32 size, int width, + int on_memory_bus, int pci_visible) +{ + u32 maskbits; + u32 widthbits; + + switch (size) { +#if 0 /* We don't support 4 GB yet */ + case 0x100000000: /* 4 GB */ + maskbits = 4; + break; +#endif + case 0x80000000: /* 2 GB */ + maskbits = 5; + break; + case 0x40000000: /* 1 GB */ + maskbits = 6; + break; + case 0x20000000: /* 512 MB */ + maskbits = 7; + break; + case 0x10000000: /* 256 MB */ + maskbits = 8; + break; + case 0x08000000: /* 128 MB */ + maskbits = 9; + break; + case 0x04000000: /* 64 MB */ + maskbits = 10; + break; + case 0x02000000: /* 32 MB */ + maskbits = 11; + break; + case 0x01000000: /* 16 MB */ + maskbits = 12; + break; + case 0x00800000: /* 8 MB */ + maskbits = 13; + break; + case 0x00400000: /* 4 MB */ + maskbits = 14; + break; + case 0x00200000: /* 2 MB */ + maskbits = 15; + break; + case 0: /* OFF */ + maskbits = 0; + break; + default: + panic("nile4_set_pdar: unsupported size %p\n", (void *) size); + } + switch (width) { + case 8: + widthbits = 0; + break; + case 16: + widthbits = 1; + break; + case 32: + widthbits = 2; + break; + case 64: + widthbits = 3; + break; + default: + panic("nile4_set_pdar: unsupported width %d\n", width); + } + + return maskbits | (on_memory_bus ? 0x10 : 0) | + (pci_visible ? 0x20 : 0) | (widthbits << 6) | + (phys & 0xffe00000); +} + +void +ddb_set_pdar(u32 pdar, u32 phys, u32 size, int width, + int on_memory_bus, int pci_visible) +{ + u32 temp= ddb_calc_pdar(phys, size, width, on_memory_bus, pci_visible); + ddb_out32(pdar, temp); + ddb_out32(pdar + 4, 0); + + /* + * When programming a PDAR, the register should be read immediately + * after writing it. This ensures that address decoders are properly + * configured. + * [jsun] is this really necesary? + */ + ddb_in32(pdar); + ddb_in32(pdar + 4); +} + +/* + * routines that mess with PCIINITx registers + */ + +void ddb_set_pmr(u32 pmr, u32 type, u32 addr, u32 options) +{ + switch (type) { + case DDB_PCICMD_IACK: /* PCI Interrupt Acknowledge */ + case DDB_PCICMD_IO: /* PCI I/O Space */ + case DDB_PCICMD_MEM: /* PCI Memory Space */ + case DDB_PCICMD_CFG: /* PCI Configuration Space */ + break; + default: + panic("nile4_set_pmr: invalid type %d\n", type); + } + ddb_out32(pmr, (type << 1) | (addr & 0xffe00000) | options ); + ddb_out32(pmr + 4, 0); +} diff --git a/arch/mips/ddb5xxx/common/pci.c b/arch/mips/ddb5xxx/common/pci.c new file mode 100644 index 000000000..33cc327d8 --- /dev/null +++ b/arch/mips/ddb5xxx/common/pci.c @@ -0,0 +1,179 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/common/pci.c + * Common PCI routines for DDB5xxx - as a matter of fact, meant for all + * MIPS machines. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + *********************************************************************** + */ + +/* + * This file contains common PCI routines meant to be shared for + * all MIPS machines. + * + * Strategies: + * + * . We rely on pci_auto.c file to assign PCI resources (MEM and IO) + * TODO: this shold be optional for some machines where they do have + * a real "pcibios" that does resource assignment. + * + * . We then use pci_scan_bus() to "discover" all the resources for + * later use by Linux. + * + * . We finally reply on a board supplied function, pcibios_fixup_irq(), to + * to assign the interrupts. We may use setup-irq.c under drivers/pci + * later. + * + * . Specifically, we will *NOT* use pci_assign_unassigned_resources(), + * because we assume all PCI devices should have the resources correctly + * assigned and recorded. + * + * Limitations: + * + * . We "collapse" all IO and MEM spaces in sub-buses under a top-level bus + * into a contiguous range. + * + * . In the case of Memory space, the rnage is 1:1 mapping with CPU physical + * address space. + * + * . In the case of IO space, it starts from 0, and the beginning address + * is mapped to KSEG0ADDR(mips_io_port) in the CPU physical address. + * + * . These are the current MIPS limitations (by ioremap, etc). In the + * future, we may remove them. + * + * Credits: + * Most of the code are derived from the pci routines from PPC and Alpha, + * which were mostly writtne by + * Cort Dougan, cort@fsmlabs.com + * Matt Porter, mporter@mvista.com + * Dave Rusling david.rusling@reo.mts.dec.com + * David Mosberger davidm@cs.arizona.edu + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/ddb5xxx/pci.h> +#include <asm/ddb5xxx/debug.h> + + +struct pci_fixup pcibios_fixups[] = { {0} }; + + +extern int pciauto_assign_resources(int busno, struct pci_channel * hose); +void __init pcibios_init(void) +{ + struct pci_channel *p; + struct pci_bus *bus; + int busno; + + /* assign resources */ + busno=0; + for (p= mips_pci_channels; p->pci_ops != NULL; p++) { + busno = pciauto_assign_resources(busno, p) + 1; + } + + /* scan the buses */ + busno = 0; + for (p= mips_pci_channels; p->pci_ops != NULL; p++) { + bus = pci_scan_bus(busno, p->pci_ops, p); + busno = bus->subordinate+1; + } + + /* fixup irqs (board specific routines) */ + pcibios_fixup_irqs(); + + /* + * should we do a fixup of ioport_resource and iomem_resource + * based on mips_pci_channels? + * Let us wait and see if this is a common need and whether there + * are exceptions. Until then, each board should adjust them + * perhaps in their setup() function. + */ +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + /* pciauto_assign_resources() will enable all devices found */ + return 0; +} + +unsigned long __init +pci_bridge_check_io(struct pci_dev *bridge) +{ + u16 io; + + pci_read_config_word(bridge, PCI_IO_BASE, &io); + if (!io) { + pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); + pci_read_config_word(bridge, PCI_IO_BASE, &io); + pci_write_config_word(bridge, PCI_IO_BASE, 0x0); + } + if (io) + return IORESOURCE_IO; + printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n", + bridge->name); + return 0; +} + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + /* Propogate hose info into the subordinate devices. */ + + struct pci_channel *hose = bus->sysdata; + struct pci_dev *dev = bus->self; + + if (!dev) { + /* Root bus */ + bus->resource[0] = hose->io_resource; + bus->resource[1] = hose->mem_resource; + } else { + /* This is a bridge. Do not care how it's initialized, + just link its resources to the bus ones */ + int i; + + for(i=0; i<3; i++) { + bus->resource[i] = + &dev->resource[PCI_BRIDGE_RESOURCES+i]; + bus->resource[i]->name = bus->name; + } + bus->resource[0]->flags |= pci_bridge_check_io(dev); + bus->resource[1]->flags |= IORESOURCE_MEM; + /* For now, propogate hose limits to the bus; + we'll adjust them later. */ + bus->resource[0]->end = hose->io_resource->end; + bus->resource[1]->end = hose->mem_resource->end; + /* Turn off downstream PF memory address range by default */ + bus->resource[2]->start = 1024*1024; + bus->resource[2]->end = bus->resource[2]->start - 1; + } +} + +char *pcibios_setup(char *str) +{ + return str; +} + +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + /* this should not be called */ + MIPS_ASSERT(1 == 0); +} + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + /* this should not be called */ + MIPS_ASSERT(1 == 0); +} diff --git a/arch/mips/ddb5xxx/common/pci_auto.c b/arch/mips/ddb5xxx/common/pci_auto.c new file mode 100644 index 000000000..b6e751332 --- /dev/null +++ b/arch/mips/ddb5xxx/common/pci_auto.c @@ -0,0 +1,396 @@ +/* + * arch/ppc/kernel/pci_auto.c + * + * PCI autoconfiguration library + * + * Author: Matt Porter <mporter@mvista.com> + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * Modified for MIPS by Jun Sun, jsun@mvista.com + * + * . Simplify the interface between pci_auto and the rest: a single function. + * . Assign resources from low address to upper address. + * . change most int to u32. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/ddb5xxx/pci.h> +#include <asm/ddb5xxx/debug.h> + +#define DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* These are used for config access before all the PCI probing + has been done. */ +int early_read_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 *val); +int early_read_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 *val); +int early_read_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 *val); +int early_write_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 val); +int early_write_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 val); +int early_write_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 val); + +static u32 pciauto_lower_iospc; +static u32 pciauto_upper_iospc; + +static u32 pciauto_lower_memspc; +static u32 pciauto_upper_memspc; + +void __init +pciauto_setup_bars(struct pci_channel *hose, + int current_bus, + int pci_devfn) +{ + u32 bar_response, bar_size, bar_value; + u32 bar, addr_mask, bar_nr = 0; + u32 * upper_limit; + u32 * lower_limit; + int found_mem64 = 0; + + DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", + current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); + + for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar+=4) + { + /* Tickle the BAR and get the response */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0xffffffff); + early_read_config_dword(hose, + current_bus, + pci_devfn, + bar, + &bar_response); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_response) + continue; + + /* Check the BAR type and set our address mask */ + if (bar_response & PCI_BASE_ADDRESS_SPACE) + { + addr_mask = PCI_BASE_ADDRESS_IO_MASK; + upper_limit = &pciauto_upper_iospc; + lower_limit = &pciauto_lower_iospc; + DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr); + } + else + { + if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + found_mem64 = 1; + + addr_mask = PCI_BASE_ADDRESS_MEM_MASK; + upper_limit = &pciauto_upper_memspc; + lower_limit = &pciauto_lower_memspc; + DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr); + } + + /* Calculate requested size */ + bar_size = ~(bar_response & addr_mask) + 1; + + /* Allocate a base address */ + bar_value = ((*lower_limit - 1) & ~(bar_size - 1)) + bar_size; + MIPS_ASSERT(bar_value + bar_size <= *upper_limit); + + /* Write it out and update our limit */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + bar_value); + + *lower_limit = bar_value + bar_size; + + /* + * If we are a 64-bit decoder then increment to the + * upper 32 bits of the bar and force it to locate + * in the lower 4GB of memory. + */ + if (found_mem64) + { + bar += 4; + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0x00000000); + } + + bar_nr++; + + DBG("size=0x%x, address=0x%x\n", + bar_size, bar_value); + } + +} + +void __init +pciauto_prescan_setup_bridge(struct pci_channel *hose, + int current_bus, + int pci_devfn, + int sub_bus) +{ + int cmdstat; + + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_PRIMARY_BUS, + current_bus); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SECONDARY_BUS, + sub_bus + 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + 0xff); + + /* Round memory allocator to 1MB boundary */ + pciauto_upper_memspc &= ~(0x100000 - 1); + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT, + ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT_UPPER16, + ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); + + /* We don't support prefetchable memory for now, so disable */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_BASE, + 0x1000); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_LIMIT, + 0x1000); + + /* Enable memory and I/O accesses, enable bus master */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +void __init +pciauto_postscan_setup_bridge(struct pci_channel *hose, + int current_bus, + int pci_devfn, + int sub_bus) +{ + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + sub_bus); + + /* Round memory allocator to 1MB boundary */ + pciauto_upper_memspc &= ~(0x100000 - 1); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_BASE, + (pciauto_upper_iospc & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_BASE_UPPER16, + pciauto_upper_iospc >> 16); +} + +#define PCIAUTO_IDE_MODE_MASK 0x05 + +int __init +pciauto_bus_scan(struct pci_channel *hose, int current_bus) +{ + int sub_bus; + u32 pci_devfn, pci_class, cmdstat, found_multi=0; + unsigned short vid; + unsigned char header_type; + + sub_bus = current_bus; + + for (pci_devfn=0; pci_devfn<0xff; pci_devfn++) { + + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_HEADER_TYPE, + &header_type); + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + + early_read_config_word(hose, + current_bus, + pci_devfn, + PCI_VENDOR_ID, + &vid); + + if (vid == 0xffff) continue; + + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_CLASS_REVISION, &pci_class); + if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) { + DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn)); + pciauto_prescan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus); + sub_bus = pciauto_bus_scan(hose, sub_bus+1); + pciauto_postscan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus); + + } else if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { + + unsigned char prg_iface; + + early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_CLASS_PROG, + &prg_iface); + if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { + DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n"); + continue; + } + } + + /* + * Found a peripheral, enable some standard + * settings + */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_LATENCY_TIMER, + 0x80); + + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn); + } + return sub_bus; +} + +int __init +pciauto_assign_resources(int busno, struct pci_channel *hose) +{ + /* setup resource limits */ + pciauto_lower_iospc = hose->io_resource->start; + pciauto_upper_iospc = hose->io_resource->end + 1; + pciauto_lower_memspc = hose->mem_resource->start; + pciauto_upper_memspc = hose->mem_resource->end + 1; + + return pciauto_bus_scan(hose, busno); +} + + +/* + * These functions are used early on before PCI scanning is done + * and all of the pci_dev and pci_bus structures have been created. + */ +static struct pci_dev * +fake_pci_dev(struct pci_channel *hose, int busnr, int devfn) +{ + static struct pci_dev dev; + static struct pci_bus bus; + + dev.bus = &bus; + dev.sysdata = hose; + dev.devfn = devfn; + bus.number = busnr; + bus.ops = hose->pci_ops; + return &dev; +} + +#define EARLY_PCI_OP(rw, size, type) \ +int early_##rw##_config_##size(struct pci_channel *hose, int bus, \ + int devfn, int offset, type value) \ +{ \ + return pci_##rw##_config_##size(fake_pci_dev(hose, bus, devfn), \ + offset, value); \ +} + +EARLY_PCI_OP(read, byte, u8 *) +EARLY_PCI_OP(read, word, u16 *) +EARLY_PCI_OP(read, dword, u32 *) +EARLY_PCI_OP(write, byte, u8) +EARLY_PCI_OP(write, word, u16) +EARLY_PCI_OP(write, dword, u32) diff --git a/arch/mips/ddb5xxx/common/prom.c b/arch/mips/ddb5xxx/common/prom.c new file mode 100644 index 000000000..9333b5278 --- /dev/null +++ b/arch/mips/ddb5xxx/common/prom.c @@ -0,0 +1,58 @@ +/*********************************************************************** + * + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/common/prom.c + * prom.c file. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + *********************************************************************** + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/bootmem.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/ddb5xxx/ddb5xxx.h> + +char arcs_cmdline[COMMAND_LINE_SIZE]; + +/* [jsun@junsun.net] PMON passes arguments in C main() style */ +void __init prom_init(int argc, const char **arg) +{ + int i; + + /* arg[0] is "g", the rest is boot parameters */ + arcs_cmdline[0] = '\0'; + for (i = 1; i < argc; i++) { + if (strlen(arcs_cmdline) + strlen(arg[i] + 1) + >= sizeof(arcs_cmdline)) + break; + strcat(arcs_cmdline, arg[i]); + strcat(arcs_cmdline, " "); + } + + mips_machgroup = MACH_GROUP_NEC_DDB; + +#if defined(CONFIG_DDB5074) + mips_machtype = MACH_NEC_DDB5074; +#elif defined(CONFIG_DDB5476) + mips_machtype = MACH_NEC_DDB5476; +#elif defined(CONFIG_DDB5477) + mips_machtype = MACH_NEC_DDB5477; +#endif + + add_memory_region(0, DDB_SDRAM_SIZE, BOOT_MEM_RAM); +} + +void __init prom_free_prom_memory(void) +{ +} diff --git a/arch/mips/ddb5xxx/common/rtc_ds1386.c b/arch/mips/ddb5xxx/common/rtc_ds1386.c new file mode 100644 index 000000000..ff1d81c60 --- /dev/null +++ b/arch/mips/ddb5xxx/common/rtc_ds1386.c @@ -0,0 +1,267 @@ +/*********************************************************************** + * + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/common/rtc_ds1386.c + * low-level RTC hookups for s for Dallas 1396 chip. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + *********************************************************************** + */ + + +/* + * This file exports a function, rtc_ds1386_init(), which expects an + * uncached base address as the argument. It will set the two function + * pointers expected by the MIPS generic timer code. + */ + +#include <linux/types.h> +#include <linux/time.h> +#include <linux/rtc.h> + +#include <asm/time.h> +#include <asm/addrspace.h> + +#include <asm/ddb5xxx/debug.h> + +#define EPOCH 2000 + +#undef BCD_TO_BIN +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) + +#undef BIN_TO_BCD +#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) + +#define READ_RTC(x) *(volatile unsigned char*)(rtc_base+x) +#define WRITE_RTC(x, y) *(volatile unsigned char*)(rtc_base+x) = y + +static unsigned long rtc_base; + +static unsigned long +rtc_ds1386_get_time(void) +{ + u8 byte; + u8 temp; + unsigned int year, month, day, hour, minute, second; + + /* let us freeze external registers */ + byte = READ_RTC(0xB); + byte &= 0x3f; + WRITE_RTC(0xB, byte); + + /* read time data */ + year = BCD_TO_BIN(READ_RTC(0xA)) + EPOCH; + month = BCD_TO_BIN(READ_RTC(0x9) & 0x1f); + day = BCD_TO_BIN(READ_RTC(0x8)); + minute = BCD_TO_BIN(READ_RTC(0x2)); + second = BCD_TO_BIN(READ_RTC(0x1)); + + /* hour is special - deal with it later */ + temp = READ_RTC(0x4); + + /* enable time transfer */ + byte |= 0x80; + WRITE_RTC(0xB, byte); + + /* calc hour */ + if (temp & 0x40) { + /* 12 hour format */ + hour = BCD_TO_BIN(temp & 0x1f); + if (temp & 0x20) hour += 12; /* PM */ + } else { + /* 24 hour format */ + hour = BCD_TO_BIN(temp & 0x3f); + } + + return mktime(year, month, day, hour, minute, second); +} + +void to_tm(unsigned long tim, struct rtc_time * tm); +static int +rtc_ds1386_set_time(unsigned long t) +{ + struct rtc_time tm; + u8 byte; + u8 temp; + u8 year, month, day, hour, minute, second; + + /* let us freeze external registers */ + byte = READ_RTC(0xB); + byte &= 0x3f; + WRITE_RTC(0xB, byte); + + /* convert */ + to_tm(t, &tm); + + /* check each field one by one */ + year = BIN_TO_BCD(tm.tm_year - EPOCH); + if (year != READ_RTC(0xA)) { + WRITE_RTC(0xA, year); + } + + temp = READ_RTC(0x9); + month = BIN_TO_BCD(tm.tm_mon); + if (month != (temp & 0x1f)) { + WRITE_RTC( 0x9, + (month & 0x1f) | (temp & ~0x1f) ); + } + + day = BIN_TO_BCD(tm.tm_mday); + if (day != READ_RTC(0x8)) { + WRITE_RTC(0x8, day); + } + + temp = READ_RTC(0x4); + if (temp & 0x40) { + /* 12 hour format */ + hour = 0x40; + if (tm.tm_hour > 12) { + hour |= 0x20 | (BIN_TO_BCD(hour-12) & 0x1f); + } else { + hour |= BIN_TO_BCD(tm.tm_hour); + } + } else { + /* 24 hour format */ + hour = BIN_TO_BCD(tm.tm_hour) & 0x3f; + } + if (hour != temp) WRITE_RTC(0x4, hour); + + minute = BIN_TO_BCD(tm.tm_min); + if (minute != READ_RTC(0x2)) { + WRITE_RTC(0x2, minute); + } + + second = BIN_TO_BCD(tm.tm_sec); + if (second != READ_RTC(0x1)) { + WRITE_RTC(0x1, second); + } + + return 0; +} + +void +rtc_ds1386_init(unsigned long base) +{ + unsigned char byte; + + /* remember the base */ + rtc_base = base; + MIPS_ASSERT((rtc_base & 0xe0000000) == KSEG1); + + /* turn on RTC if it is not on */ + byte = READ_RTC(0x9); + if (byte & 0x80) { + byte &= 0x7f; + WRITE_RTC(0x9, byte); + } + + /* enable time transfer */ + byte = READ_RTC(0xB); + byte |= 0x80; + WRITE_RTC(0xB, byte); + + /* set the function pointers */ + rtc_get_time = rtc_ds1386_get_time; + rtc_set_time = rtc_ds1386_set_time; +} + + +/* ================================================== */ +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +static void +GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + + +void to_tm(unsigned long tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} |