From: James S. <jsi...@us...> - 2001-11-08 17:31:20
|
Update of /cvsroot/linux-mips/linux/arch/mips64/sibyte/sb1250 In directory usw-pr-cvs1:/tmp/cvs-serv4654/sb1250 Added Files: Makefile irq.c irq_handler.S pci-dma.c pci.c setup.c smp.c time.c Log Message: 64-bit support for SB1250 / SWARM. --- NEW FILE: Makefile --- all: sb1250.a OBJS-y := setup.o irq.o irq_handler.o time.o OBJS-$(CONFIG_SMP) += smp.o OBJS-$(CONFIG_PCI) += pci.o pci-dma.o sb1250.a: $(OBJS-y) $(AR) rcs sb1250.a $^ dep: $(CPP) $(CPPFLAGS) -M *.c > .depend include $(TOPDIR)/Rules.make --- NEW FILE: irq.c --- /* * Copyright (C) 2000, 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/linkage.h> #include <linux/irq.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/irq_cpustat.h> #include <asm/io.h> #include <asm/errno.h> #include <asm/signal.h> #include <asm/system.h> #include <asm/ptrace.h> #include <asm/sibyte/64bit.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/sb1250_scd.h> #include <asm/sibyte/sb1250.h> /* Sanity check. We're an sb1250, with 2 SB1 cores on die; we'd better have configured for an sb1 cpu */ #ifndef CONFIG_CPU_SB1 #error "Sb1250 requires configuration of SB1 cpu" #endif /* * These are the routines that handle all the low level interrupt stuff. * Actions handled here are: initialization of the interrupt map, * requesting of interrupt lines by handlers, dispatching if interrupts * to handlers, probing for interrupt lines */ static spinlock_t irq_mask_lock = SPIN_LOCK_UNLOCKED; #define IMR_MASK(cpu) (*(volatile unsigned long *)(IO_SPACE_BASE | A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MASK))) #define IMR_REG(cpu, ofs) (*(volatile unsigned long *)((IO_SPACE_BASE | A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE)) + (ofs<<3))) #define IMR_MAILBOX(cpu) (*(volatile unsigned long *)(IO_SPACE_BASE | A_IMR_REGISTER(cpu, R_IMR_MAILBOX_CPU))) /* * These functions (sb1250_mask_irq and sb1250_unmask_irq) provide * a safe, locked way to enable and disable interrupts at the * hardware level. Note they only operate on cpu0; all irq's * are routed to cpu0 except the mailbox interrupt needed * for inter-processor-interrupts, so these are usable for * any other kind of irq. */ void sb1250_mask_irq(int cpu, int irq) { unsigned long flags; u64 cur_ints; spin_lock_irqsave(&irq_mask_lock, flags); cur_ints = in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK); cur_ints |= (((u64)1)<<irq); out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK); spin_unlock_irqrestore(&irq_mask_lock, flags); } void sb1250_unmask_irq(int cpu, int irq) { unsigned long flags; u64 cur_ints; spin_lock_irqsave(&irq_mask_lock, flags); cur_ints = in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK); cur_ints &= ~(((u64)1)<<irq); out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK); spin_unlock_irqrestore(&irq_mask_lock, flags); } static unsigned int startup_none(unsigned int irq) { /* Do nothing */ return 0; } static void shutdown_none(unsigned int irq) { /* Do nothing */ } #define enable_none shutdown_none #define disable_none shutdown_none #define ack_none shutdown_none #define end_none shutdown_none static void affinity_none(unsigned int irq, unsigned long mask) { /* Do nothing */ } /* * If depth is 0, then unmask the interrupt. Increment depth */ void enable_irq(unsigned int irq) { unsigned long flags; irq_desc_t *desc = irq_desc + irq; spin_lock_irqsave(&desc->lock, flags); if (!desc->depth) { u64 cur_ints; spin_lock(&irq_mask_lock); cur_ints = in64(KSEG1 + A_IMR_MAPPER(0) + R_IMR_INTERRUPT_MASK); cur_ints &= ~(((u64)1)<<irq); out64(cur_ints, KSEG1 + A_IMR_MAPPER(0) + R_IMR_INTERRUPT_MASK); spin_unlock(&irq_mask_lock); desc->status &= ~IRQ_DISABLED; } desc->depth++; spin_unlock_irqrestore(&desc->lock, flags); } /* * If depth hits 0, disable the interrupt. By grabbing desc->lock, we * implicitly sync because a dispatched interrupt will grab that lock * before calling handlers. IRQ_DISABLED is checked by the dispatcher * under desc->lock, so we set that in case we're racing with a dispatch. */ void disable_irq(unsigned int irq) { unsigned long flags; irq_desc_t *desc = irq_desc + irq; spin_lock_irqsave(&desc->lock, flags); if (!desc->depth) { BUG(); } desc->depth--; if (!desc->depth) { u64 cur_ints; spin_lock(&irq_mask_lock); cur_ints = in64(KSEG1 + A_IMR_MAPPER(0) + R_IMR_INTERRUPT_MASK); cur_ints |= (((u64)1)<<irq); out64(cur_ints, KSEG1 + A_IMR_MAPPER(0) + R_IMR_INTERRUPT_MASK); spin_unlock(&irq_mask_lock); desc->status |= IRQ_DISABLED; } spin_unlock_irqrestore(&desc->lock, flags); } static struct hw_interrupt_type no_irq_type = { "none", startup_none, shutdown_none, enable_none, disable_none, ack_none, end_none, affinity_none }; /* * irq_desc is the structure that keeps track of the state * and handlers for each of the IRQ lines */ static irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; /* Defined in arch/mips/sibyte/sb1250/irq_handler.S */ extern void sb1250_irq_handler(void); /* * spurious_count is used in arch/mips/kernel/entry.S to record the * number of spurious interrupts we see before the handler is installed. * It doesn't provide any particularly relevant information for us, so * we basically ignore it. */ unsigned long spurious_count = 0; /* * The interrupt handler calls this once for every unmasked interrupt * that is pending. vector is the IRQ number that was raised */ void sb1250_dispatch_irq(unsigned int vector, struct pt_regs *regs) { struct irqaction *action; irq_desc_t *desc = irq_desc + vector; /* XXX This race nipping stuff seems to be incorrect. Revisit this later. */ /*spin_lock(&desc->lock);*/ /* Nip a race. If we had a disable_irq() call that happened after we started the interrupt but before we got here, it set the DISABLED flag. Check that flag under the lock before doing anything */ if (!(desc->status & IRQ_DISABLED)) { for (action = desc->action; action != NULL; action = action->next) { if (action->handler != NULL) { (*(action->handler))(vector, action->dev_id, regs); } } } /*spin_unlock(&desc->lock);*/ } /* * Stolen, pretty much intact, from arch/i386/kernel/irq.c */ int setup_irq(unsigned int irq, struct irqaction * new) { int shared = 0; unsigned long flags; struct irqaction *old, **p; irq_desc_t *desc = irq_desc + irq; /* * The following block of code has to be executed atomically */ spin_lock_irqsave(&desc->lock,flags); p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); return -EBUSY; } /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; if (!shared) { desc->depth = 0; desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); desc->handler->startup(irq); } spin_unlock_irqrestore(&desc->lock,flags); return 0; } /* * init_IRQ is called early in the boot sequence from init/main.c. It * is responsible for setting up the interrupt mapper and installing the * handler that will be responsible for dispatching interrupts to the * "right" place. */ /* * For now, map all interrupts to IP[2]. We could save * some cycles by parceling out system interrupts to different * IP lines, but keep it simple for bringup. We'll also direct * all interrupts to a single CPU; we should probably route * PCI and LDT to one cpu and everything else to the other * to balance the load a bit. * * On the second cpu, everything is set to IP5, which is * ignored, EXCEPT the mailbox interrupt. That one is * set to IP2 so it is handled. This is needed so we * can do cross-cpu function calls, as requred by SMP */ void __init init_IRQ(void) { unsigned int i; u64 tmp; /* Default everything to IP2 */ for (i = 0; i < 64; i++) { IMR_REG(0, i) = K_INT_MAP_I0; IMR_REG(1, i) = K_INT_MAP_I0; } /* Map general purpose timer 0 to IP[4] on cpu 0. This is the system timer */ IMR_REG(0, K_INT_TIMER_0) = K_INT_MAP_I2; /* Map the high 16 bits of the mailbox registers to IP[3], for inter-cpu messages */ IMR_REG(0, K_INT_MBOX_0) = K_INT_MAP_I1; IMR_REG(1, K_INT_MBOX_0) = K_INT_MAP_I1; /* Clear the mailboxes. The firmware may leave them dirty */ IMR_MAILBOX(0) = 0; IMR_MAILBOX(1) = 0; /* Mask everything except the mailbox registers for both cpus */ tmp = ~((u64)0) ^ (((u64)1) << K_INT_MBOX_0); IMR_MASK(0) = tmp; IMR_MASK(1) = tmp; /* Enable IP[4:0], disable the rest */ set_cp0_status(0xff00, 0x1f00); set_except_vector(0, sb1250_irq_handler); } /* * request_irq() is called by drivers to request addition to the chain * of handlers called for a given interrupt. * * arch/i386/kernel/irq.c says this is going to become generic code in 2.5 * Makes sense, considering it's already architecture independent. As such, * I've tried to match the i386 style as much as possible to make the * transition simple */ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { int retval; struct irqaction * action; /* * Sanity-check: shared interrupts should REALLY pass in * a real dev-ID, otherwise we'll have trouble later trying * to figure out which interrupt is which (messes up the * interrupt freeing logic etc). */ if (irqflags & SA_SHIRQ) { if (!dev_id) printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); } if (irq >= NR_IRQS) { return -EINVAL; } if (!handler) { return -EINVAL; } action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) { return -ENOMEM; } action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; retval = setup_irq(irq, action); if (retval) { kfree(action); } else { if ((irq >= 56) && (irq <= 59)) { sb1250_unmask_irq(0,irq); } } return retval; } /* * free_irq() releases a handler set up by request_irq() */ void free_irq(unsigned int irq, void *dev_id) { irq_desc_t *desc; struct irqaction **p; unsigned long flags; if (irq >= NR_IRQS) return; desc = irq_desc + irq; spin_lock_irqsave(&desc->lock,flags); p = &desc->action; for (;;) { struct irqaction * action = *p; if (action) { struct irqaction **pp = p; p = &action->next; if (action->dev_id != dev_id) continue; /* Found it - now remove it from the list of entries */ *pp = action->next; if (!desc->action) { desc->status |= IRQ_DISABLED; desc->handler->shutdown(irq); } spin_unlock_irqrestore(&desc->lock,flags); #ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ while (desc->status & IRQ_INPROGRESS) barrier(); #endif kfree(action); return; } spin_unlock_irqrestore(&desc->lock,flags); return; } } /* * get_irq_list() pretty prints a list of who has requested which * irqs. This function is activated by a read of a file in /proc/ * Returns the length of the string generated * */ int get_irq_list(char *buf) { return 0; } /* * probe_irq_on() and probe_irq_off() are a pair of functions used for * determining which interrupt a device is using. I *think* this is * pretty much legacy for [E]ISA applications, thus the stubbing out. */ unsigned long probe_irq_on (void) { panic("probe_irq_on called"); } int probe_irq_off (unsigned long irqs) { panic("probe_irq_off called"); } /* * I don't see a good way to do this without syncing, given the * way desc->lock works, so just sync anyways. */ void disable_irq_nosync(unsigned int irq) { disable_irq(irq); } --- NEW FILE: irq_handler.S --- /* * Copyright (C) 2000, 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * sb1250_handle_int() is the routine that is actually called when an interrupt * occurs. It is installed as the exception vector handler in init_IRQ() * in arch/mips/sibyte/sb1250/irq.c * * In the handle we figure out which interrupts need handling, and use that to call * the dispatcher, which will take care of actually calling registered handlers * * Note that we take care of all raised interrupts in one go at the handler. This * is more BSDish than the Indy code, and also, IMHO, more sane. */ #include <asm/addrspace.h> #include <asm/processor.h> #include <asm/asm.h> #include <asm/mipsregs.h> #include <asm/regdef.h> #include <asm/stackframe.h> #include <asm/sibyte/sb1250_regs.h> .text .set push .set noreorder .set noat .align 5 NESTED(sb1250_irq_handler, PT_SIZE, sp) SAVE_ALL /* Is the CLI really needed? If it is, this looks suspiciously like a place that would need a spinlock instead of a CLI. */ CLI mfc0 s0, $13 /* Timer interrupt is routed to IP[4] */ andi t1, s0, 0x1000 beqz t1, 1f nop jal sb1250_timer_interrupt move a0, sp /* Pass the registers along */ 1: #ifdef CONFIG_SMP /* Mailbox interrupt is routed to IP[3] */ andi t1, s0, 0x800 beqz t1, 2f nop jal sb1250_mailbox_interrupt move a0, sp 2: #endif and t1, s0, 0x400 beqz t1, 4f nop /* Default...we've hit an IP[2] interrupt, which means we've got to check the 1250 interrupt registers to figure out what to do */ la v0, KSEG1 + A_IMR_CPU0_BASE ld s1, R_IMR_INTERRUPT_MASK(v0) ld s0, R_IMR_INTERRUPT_SOURCE_STATUS(v0) ld t0, R_IMR_LDT_INTERRUPT(v0) nor s1, s1, zero /* Negate mask to turn it into an and mask */ or s0, s0, t0 /* Merge pending system and LDT IRQS */ and s0, s0, s1 /* Now s0 has a bitfield of unmasked pending IRQs */ and t0, t0, s1 /* LDT interrupts we will service */ beqz s0, 4f /* No interrupts. Return */ daddiu a1, sp, 0 /* registers get passed along as the second argument */ sd t0, R_IMR_LDT_INTERRUPT_CLR(v0) /* Clear what we'll service */ 3: .word 0x72118824 /* find next interrupt, actually a dclz s1,s0 */ dsubu a0, zero, s1 jal sb1250_dispatch_irq /* Handle the interrupt */ daddiu a0, a0, 63 daddiu s1, s1, 1 /* Get shift amount for clearing of top interrupt */ dsllv s0, s0, s1 /* Clear the top interrupt */ bnez s0, 3b /* More interrupts to service? */ dsrlv s0, s0, s1 /* realign pending interrupts */ 4: j ret_from_irq /* defined in arch/mips/kernel/entry.S */ nop .set pop END(sb1250_irq_handler) --- NEW FILE: pci-dma.c --- /* * Copyright (C) 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* from: ip27-pci-dma.c * * Dynamic DMA mapping support. * * On the Origin there is dynamic DMA address translation for all PCI DMA. * However we don't use this facility yet but rely on the 2gb direct * mapped DMA window for PCI64. So consistent alloc/free are merely page * allocation/freeing. The rest of the dynamic DMA mapping interface is * implemented in <asm/pci.h>. So this code will fail with more than * 2gb of memory. */ #include <linux/types.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/pci.h> #include <asm/io.h> /* Pure 2^n version of get_order */ extern __inline__ int __get_order(unsigned long size) { int order; size = (size-1) >> (PAGE_SHIFT-1); order = -1; do { size >>= 1; order++; } while (size); return order; } void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) { void *ret; int gfp = GFP_ATOMIC; int order = __get_order(size); if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) gfp |= GFP_DMA; ret = (void *)__get_free_pages(gfp, order); if (ret != NULL) { memset(ret, 0, size); *dma_handle = (bus_to_baddr[hwdev->bus->number] | __pa(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)); } --- NEW FILE: pci.c --- /* * Copyright (C) 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * SB1250-specific PCI support * * This module provides the glue between Linux's PCI subsystem * and the hardware. We basically provide glue for accessing * configuration space, and set up the translation for I/O * space accesses. * * To access configuration space, we call some assembly-level * stubs that flip the KX bit on and off in the status * register, and do XKSEG addressed memory accesses there. * It's slow (7 SSNOPs to guarantee that KX is set!) but * fortunately, config space accesses are rare. * * We could use the ioremap functionality for the confguration * space as well as I/O space, but I'm not sure of the * implications of setting aside 16MB of KSEG2 for something * that is used so rarely (how much space in the page tables?) * */ #include <linux/config.h> #ifdef CONFIG_PCI #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/mm.h> #include <asm/sibyte/sb1250_defs.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_pci.h> #define PCI_BUS_ENABLED 1 #define LDT_BUS_ENABLED 2 static int sb1250_bus_status = 0; #define MATCH_BITS 0x20000000 /* really belongs in an include file */ #define LDT_BRIDGE_START ((A_PCI_TYPE01_HEADER|MATCH_BITS)+0x00) #define LDT_BRIDGE_END ((A_PCI_TYPE01_HEADER|MATCH_BITS)+0x20) /* * This macro calculates the offset into config space where * a given bus, device/function, and offset live on the sb1250 */ #define CFGOFFSET(bus,devfn,where) (((bus)<<16)+((devfn)<<8)+(where)) /* * Using the above offset, this macro calcuates the actual * address. Note that the physical address is not accessible * without remapping or setting KX. We use 'match bits' * as our endian policy to guarantee that 32-bit accesses * look the same from either endianness. */ #define CFGADDR(dev,where) (A_PHYS_LDTPCI_CFG_MATCH_BITS + \ CFGOFFSET(dev->bus->number,dev->devfn,where)) /* * Read/write 32-bit values in config space. */ #define READCFG32(addr) *((volatile uint32_t *) (K1BASE+((addr)&~3))) #define WRITECFG32(addr,data) *((volatile uint32_t *) (K1BASE+((addr)&~3))) = (data) /* * This variable is the mapping * of the ISA/PCI I/O space area. We map 64K here and * the offsets from this address get treated with "match bytes" * policy to make everything look little-endian. So, * you need to also set CONFIG_SWAP_IO_SPACE, but this is the * combination that works correctly with most of Linux's drivers. */ unsigned long mips_io_port_base; /* * Read/write access functions for various sizes of values * in config space. */ static int sb1250_pci_read_config_byte (struct pci_dev *dev, int where, u8 *val) { u32 data = 0; u32 cfgaddr = CFGADDR(dev,where); data = READCFG32(cfgaddr); /* * If the LDT was not configured, make it look like the bridge * header is not there. */ if (!(sb1250_bus_status & LDT_BUS_ENABLED) && (cfgaddr >= LDT_BRIDGE_START) && (cfgaddr < LDT_BRIDGE_END)) { data = 0xFFFFFFFF; } *val = (data >> ((where & 3) << 3)) & 0xff; return PCIBIOS_SUCCESSFUL; } static int sb1250_pci_read_config_word (struct pci_dev *dev, int where, u16 *val) { u32 data = 0; u32 cfgaddr = CFGADDR(dev,where); if (where & 1) return PCIBIOS_BAD_REGISTER_NUMBER; data = READCFG32(cfgaddr); /* * If the LDT was not configured, make it look like the bridge * header is not there. */ if (!(sb1250_bus_status & LDT_BUS_ENABLED) && (cfgaddr >= LDT_BRIDGE_START) && (cfgaddr < LDT_BRIDGE_END)) { data = 0xFFFFFFFF; } *val = (data >> ((where & 3) << 3)) & 0xffff; return PCIBIOS_SUCCESSFUL; } static int sb1250_pci_read_config_dword (struct pci_dev *dev, int where, u32 *val) { u32 data = 0; u32 cfgaddr = CFGADDR(dev,where); if (where & 3) return PCIBIOS_BAD_REGISTER_NUMBER; data = READCFG32(cfgaddr); /* * If the LDT was not configured, make it look like the bridge * header is not there. */ if (!(sb1250_bus_status & LDT_BUS_ENABLED) && (cfgaddr >= LDT_BRIDGE_START) && (cfgaddr < LDT_BRIDGE_END)) { data = 0xFFFFFFFF; } *val = data; return PCIBIOS_SUCCESSFUL; } static int sb1250_pci_write_config_byte (struct pci_dev *dev, int where, u8 val) { u32 data = 0; u32 cfgaddr = CFGADDR(dev,where); data = READCFG32(cfgaddr); data = (data & ~(0xff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); WRITECFG32(cfgaddr,data); return PCIBIOS_SUCCESSFUL; } static int sb1250_pci_write_config_word (struct pci_dev *dev, int where, u16 val) { u32 data = 0; u32 cfgaddr = CFGADDR(dev,where); if (where & 1) return PCIBIOS_BAD_REGISTER_NUMBER; data = READCFG32(cfgaddr); data = (data & ~(0xffff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); WRITECFG32(cfgaddr,data); return PCIBIOS_SUCCESSFUL; } static int sb1250_pci_write_config_dword(struct pci_dev *dev, int where, u32 val) { u32 cfgaddr = CFGADDR(dev,where); if (where & 3) return PCIBIOS_BAD_REGISTER_NUMBER; WRITECFG32(cfgaddr,val); return PCIBIOS_SUCCESSFUL; } struct pci_ops sb1250_pci_ops = { sb1250_pci_read_config_byte, sb1250_pci_read_config_word, sb1250_pci_read_config_dword, sb1250_pci_write_config_byte, sb1250_pci_write_config_word, sb1250_pci_write_config_dword }; void __init pcibios_init(void) { uint32_t cmdreg; /* * See if the PCI bus has been configured by the firmware. */ cmdreg = READCFG32((A_PCI_TYPE00_HEADER|MATCH_BITS) + R_PCI_TYPE0_CMDSTATUS); if (!(cmdreg & M_PCI_CMD_MASTER_EN)) { printk("PCI: Skipping PCI probe. Bus is not initialized.\n"); return; } sb1250_bus_status |= PCI_BUS_ENABLED; /* * Also check the LDT bridge's enable, just in case we didn't * initialize that one. */ cmdreg = READCFG32((A_PCI_TYPE01_HEADER|MATCH_BITS) + R_PCI_TYPE0_CMDSTATUS); if (cmdreg & M_PCI_CMD_MASTER_EN) { sb1250_bus_status |= LDT_BUS_ENABLED; } /* * Establish a mapping from kernel uncached space to ISA-style I/O space. * Use "match bytes", even though this exposes endianness. * big-endian Linuxes will have CONFIG_SWAP_IO_SPACE set. */ #if defined(CONFIG_SWAP_IO_SPACE) mips_io_port_base = K1BASE | (A_PHYS_LDTPCI_IO_MATCH_BYTES); #else mips_io_port_base = K1BASE | (A_PHYS_LDTPCI_IO_MATCH_BITS); #endif /* Probe for PCI hardware */ printk("PCI: Probing PCI hardware on host bus 0.\n"); pci_scan_bus(0, &sb1250_pci_ops, NULL); } int __init pcibios_enable_device(struct pci_dev *dev) { /* Not needed, since we enable all devices at startup. */ return 0; } void __init pcibios_align_resource(void *data, struct resource *res, unsigned long size) { } char * __init pcibios_setup(char *str) { /* Nothing to do for now. */ return str; } struct pci_fixup pcibios_fixups[] = { { 0 } }; void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *res, int resource) { unsigned long where, size; u32 reg; 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); } /* * Called after each bus is probed, but before its children * are examined. */ void __init pcibios_fixup_bus(struct pci_bus *b) { pci_read_bridge_bases(b); } #endif /* CONFIG_PCI */ --- NEW FILE: setup.c --- /* * Copyright (C) 2000, 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Setup code likely to be common to all SB1250 platforms */ #include <linux/spinlock.h> #include <linux/mc146818rtc.h> #include <linux/mm.h> #include <linux/bootmem.h> #include <linux/blk.h> #include <asm/irq.h> #include <asm/bootinfo.h> #include <asm/addrspace.h> unsigned long bus_to_baddr[256]; void sb1250_setup(void) { printk("sb1250_setup was called.\n"); } --- NEW FILE: smp.c --- /* * Copyright (C) 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <asm/sibyte/64bit.h> #include <asm/sibyte/sb1250.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/addrspace.h> #include <asm/smp.h> #include <asm/processor.h> #include <asm/delay.h> #include <linux/sched.h> extern struct cpuinfo_mips cpu_data[NR_CPUS]; /* * These are routines for dealing with the sb1250 smp capabilities * independent of board/firmware */ static u64 mailbox_set_regs[] = { KSEG1 + A_IMR_CPU0_BASE + R_IMR_MAILBOX_SET_CPU, KSEG1 + A_IMR_CPU1_BASE + R_IMR_MAILBOX_SET_CPU }; static u64 mailbox_clear_regs[] = { KSEG1 + A_IMR_CPU0_BASE + R_IMR_MAILBOX_CLR_CPU, KSEG1 + A_IMR_CPU1_BASE + R_IMR_MAILBOX_CLR_CPU }; static u64 mailbox_regs[] = { KSEG1 + A_IMR_CPU0_BASE + R_IMR_MAILBOX_CPU, KSEG1 + A_IMR_CPU1_BASE + R_IMR_MAILBOX_CPU }; /* Simple enough; everything is set up, so just poke the appropriate mailbox register, and we should be set */ void sys_send_intercpu_int(int cpu, unsigned int action) { #ifdef PARANOID if (action & ~0xff) { BUG(); } #endif out64((((u64)action)<< 48), mailbox_set_regs[cpu]); } void sb1250_smp_finish(void) { void sb1_sanitize_tlb(void); cpu_data[0].udelay_val = loops_per_jiffy; cpu_data[1].udelay_val = loops_per_jiffy; sb1250_time_init(); sb1_sanitize_tlb(); } void sb1250_mailbox_interrupt(struct pt_regs *regs) { /* Function pointer to latch the value before we say we're started */ unsigned int action; /* Load the mailbox register to figure out what we're supposed to do */ action = (in64(mailbox_regs[smp_processor_id()]) >> 48) & 0xffff; /* Clear the mailbox to clear the interrupt */ out64(((u64)0xffff)<<48, mailbox_clear_regs[smp_processor_id()]); if (action & SMP_INT_RESCHEDULE) { current->need_resched = 1; } if (action & SMP_INT_CALL_FUNC) { smp_call_function_interrupt(); } } --- NEW FILE: time.c --- /* * Copyright (C) 2000, 2001 Broadcom Corporation * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * These are routines to set up and handle interrupts from the * sb1250 general purpose timer 0. We're using the timer as a * system clock, so we set it up to run at 100 Hz. On every * interrupt, we update our idea of what the time of day is, * then call do_timer() in the architecture-independent kernel * code to do general bookkeeping (e.g. update jiffies, run * bottom halves, etc.) */ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <asm/irq.h> #include <asm/ptrace.h> #include <asm/addrspace.h> #include <asm/io.h> #include <asm/sibyte/sb1250.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_scd.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/64bit.h> #include <asm/pgtable.h> void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); void sb1250_time_init(void) { int cpu; cpu = smp_processor_id(); /* Only have 4 general purpose timers */ if (cpu > 3) { BUG(); } sb1250_mask_irq(cpu, 2 + cpu); /* Map the timer interrupt to ip[4] of this cpu */ out64(K_INT_MAP_I2, KSEG1 + A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) + ((K_INT_TIMER_0 + cpu)<<3)); /* the general purpose timer ticks at 1 Mhz independent if the rest of the system */ /* Disable the timer and set up the count */ out64(0, KSEG1 + A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); out64(1000000/HZ, KSEG1 + A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); /* Set the timer running */ out64(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS, KSEG1 + A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); sb1250_unmask_irq(cpu, 2 + cpu); /* This interrupt is "special" in that it doesn't use the request_irq way to hook the irq line. The timer interrupt is initialized early enough to make this a major pain, and it's also firing enough to warrant a bit of special case code. sb1250_timer_interrupt is called directly from irq_handler.S when IP[4] is set during an interrupt */ } void sb1250_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); /* Reset the timer */ out64(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS, KSEG1 + A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); /* Need to do some stuff here with xtime, too, but that looks like it should be architecture independent...does it really belong here? */ if (!cpu) { do_timer(regs); } #ifdef CONFIG_SMP { int user = user_mode(regs); /* * update_process_times() expects us to have done irq_enter(). * Besides, if we don't timer interrupts ignore the global * interrupt lock, which is the WrongThing (tm) to do. * Picked from i386 code. */ irq_enter(cpu, 0); update_process_times(user); irq_exit(cpu, 0); } #endif /* CONFIG_SMP */ } |