From: James S. <jsi...@us...> - 2001-10-26 16:13:58
|
Update of /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32 In directory usw-pr-cvs1:/tmp/cvs-serv32140 Modified Files: Makefile ip32-irq.c ip32-pci-dma.c ip32-pci.c ip32-setup.c ip32-timer.c Added Files: ip32-reset.c Log Message: O2 updates. --- NEW FILE: ip32-reset.c --- /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2001 Keith M Wesolowski */ #include <asm/promlib.h> /* linux/reboot.h */ void machine_restart (char *cmd) { ArcReboot (cmd); } void machine_halt (void) { ArcEnterInteractiveMode(); } void machine_power_off (void) { ArcPowerDown(); } Index: Makefile =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/Makefile,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- Makefile 2001/08/24 19:01:26 1.1 +++ Makefile 2001/10/26 16:13:55 1.2 @@ -18,7 +18,7 @@ all: ip32-kern.a ip32-irq-glue.o obj-y += ip32-irq.o ip32-rtc.o ip32-setup.o ip32-irq-glue.o \ - ip32-berr.o ip32-timer.o crime.o + ip32-berr.o ip32-timer.o crime.o ip32-reset.o ifdef CONFIG_PCI obj-y += ip32-pci.o ip32-pci-dma.o Index: ip32-irq.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/ip32-irq.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- ip32-irq.c 2001/08/24 19:01:26 1.1 +++ ip32-irq.c 2001/10/26 16:13:55 1.2 @@ -23,6 +23,12 @@ #include <asm/ip32/mace.h> #include <asm/signal.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/sched.h> + #undef DEBUG_IRQ #ifdef DEBUG_IRQ #define DBG(x...) printk(x) @@ -112,15 +118,50 @@ unsigned long spurious_count = 0; extern void ip32_handle_int (void); -extern void do_IRQ (unsigned int irq, struct pt_regs *regs); +asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs); + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ + /* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ + printk("unexpected interrupt %d\n", irq); +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + + /* For interrupts wired from a single device to the CPU. Only the clock * uses this it seems, which is IRQ 0 and IP7. */ static void enable_cpu_irq (unsigned int irq) { - set_cp0_status (STATUSF_IP7); + set_cp0_status (STATUSF_IP7,~ST0_CU1); } static unsigned int startup_cpu_irq (unsigned int irq) { @@ -460,11 +501,11 @@ while (1) ; } -void __init ip32_irq_init(void) +void __init init_IRQ(void) { unsigned int irq; - extern void init_generic_irq (void); - + int i; + /* Install our interrupt handler, then clear and disable all * CRIME and MACE interrupts. */ @@ -475,7 +516,12 @@ mace_write_32 (MACEISA_INT_MASK, 0); set_except_vector(0, ip32_handle_int); - init_generic_irq (); + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &no_irq_type; + } for (irq = 0; irq <= IP32_IRQ_MAX; irq++) { hw_irq_controller *controller; @@ -554,3 +600,634 @@ { do_IRQ (CLOCK_IRQ, regs); } + +volatile unsigned long irq_err_count, spurious_count; + +/* + * Generic, controller-independent functions: + */ + +int get_irq_list(char *buf) +{ + struct irqaction * action; + char *p = buf; + int i; + + p += sprintf(p, " "); + for (i=0; i < 1 /*smp_num_cpus*/; i++) + p += sprintf(p, "CPU%d ", i); + *p++ = '\n'; + + for (i = 0 ; i < NR_IRQS ; i++) { + action = irq_desc[i].action; + if (!action) + continue; + p += sprintf(p, "%3d: ",i); + p += sprintf(p, "%10u ", kstat_irqs(i)); + p += sprintf(p, " %14s", irq_desc[i].handler->typename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + p += sprintf(p, "ERR: %10lu\n", irq_err_count); + return p - buf; +} + +/* this was setup_x86_irq but it seems pretty generic */ +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; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(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); + + /* register_irq_proc(irq); */ + return 0; +} + + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +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; + +#if 1 + /* + * 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]); + } +#endif + + 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); + return retval; +} + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function may be called from interrupt context. + * + * Bugs: Attempting to free an irq in a handler for the same irq hangs + * the machine. + */ + +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; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ + +static DECLARE_MUTEX(probe_sem); + +/** + * probe_irq_on - begin an interrupt autodetect + * + * Commence probing for an interrupt. The interrupts are scanned + * and a mask of potential interrupt lines is returned. + * + */ + +unsigned long probe_irq_on(void) +{ + unsigned int i; + irq_desc_t *desc; + unsigned long val; + unsigned long delay; + + down(&probe_sem); + /* + * something may have generated an irq long ago and we want to + * flush such a longstanding irq before considering it as spurious. + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ synchronize_irq(); + + /* + * enable any unassigned irqs + * (we must startup again here because if a longstanding irq + * happened in the previous stage, it may have masked itself) + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ synchronize_irq(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ + +/** + * probe_irq_mask - scan a bitmap of interrupt lines + * @val: mask of interrupts to consider + * + * Scan the ISA bus interrupt lines and return a bitmap of + * active interrupts. The interrupt probe logic state is then + * returned to its previous value. + * + * Note: we need to scan all the irq's even though we will + * only return ISA irq numbers - just so that we reset them + * all to a known state. + */ +unsigned int probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (i < 16 && !(status & IRQ_WAITING)) + mask |= 1 << i; + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source). + */ + +/** + * probe_irq_off - end an interrupt autodetect + * @val: mask of potential interrupts (unused) + * + * Scans the unused interrupt lines and returns the line which + * appears to have triggered the interrupt. If no interrupt was + * found then zero is returned. If more than one interrupt is + * found then minus the first candidate is returned to indicate + * their is doubt. + * + * The interrupt probe logic state is returned to its previous + * value. + * + * BUGS: When used in a module (which arguably shouldnt happen) + * nothing prevents two IRQ probe callers from overlapping. The + * results of this are non-optimal. + */ + +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) +{ + int status; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +void inline disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_pending(cpu)) + do_softirq(); + return 1; +} + Index: ip32-pci-dma.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/ip32-pci-dma.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- ip32-pci-dma.c 2001/09/04 16:04:16 1.2 +++ ip32-pci-dma.c 2001/10/26 16:13:55 1.3 @@ -29,7 +29,7 @@ if (ret != NULL) { memset(ret, 0, size); dma_cache_wback_inv((unsigned long) ret, size); - *dma_handle = virt_to_bus(ret); + *dma_handle = KDM_TO_PHYS(ret); } return ret; } @@ -37,5 +37,46 @@ 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)); + free_pages((unsigned long) PHYS_TO_K0(vaddr), get_order(size)); } + + +/* +dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, + int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + +} +void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} +int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} +void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} +void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} +void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} +*/ Index: ip32-pci.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/ip32-pci.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- ip32-pci.c 2001/08/24 19:01:26 1.1 +++ ip32-pci.c 2001/10/26 16:13:55 1.2 @@ -436,3 +436,7 @@ & ~MACEPCI_ERROR_INTERRUPT_TEST); } } +unsigned __init int pcibios_assign_all_busses(void) +{ + return 0; +} Index: ip32-setup.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/ip32-setup.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- ip32-setup.c 2001/08/24 19:01:26 1.1 +++ ip32-setup.c 2001/10/26 16:13:55 1.2 @@ -25,8 +25,11 @@ extern struct rtc_ops ip32_rtc_ops; extern u32 cc_interval; +unsigned long mips_io_port_base = UNCACHEDADDR (MACEPCI_HI_IO);; + + void __init ip32_init (int argc, char **argv, char **envp) { - arc_meminit (); + prom_meminit (); } void __init ip32_setup(void) Index: ip32-timer.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32/ip32-timer.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- ip32-timer.c 2001/08/24 19:01:26 1.1 +++ ip32-timer.c 2001/10/26 16:13:55 1.2 @@ -15,11 +15,39 @@ #include <asm/ip32/crime.h> #include <asm/ip32/ip32_ints.h> -extern u32 cc_interval; +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/mipsregs.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <linux/mc146818rtc.h> +#include <linux/timex.h> + +extern volatile unsigned long wall_jiffies; +extern rwlock_t xtime_lock; + +u32 cc_interval; + +/* Cycle counter value at the previous timer interrupt.. */ +static unsigned int timerhi, timerlo; + /* An arbitrary time; this can be decreased if reliability looks good */ #define WAIT_MS 10 #define PER_MHZ (1000000 / 2 / HZ) +/* + * Change this if you have some constant time drift + */ +#define USECS_PER_JIFFY (1000000/HZ) + void __init ip32_timer_setup (struct irqaction *irq) { u64 crime_time; @@ -44,4 +72,216 @@ printk("%d MHz CPU detected\n", (int) (cc_interval / PER_MHZ)); setup_irq (CLOCK_IRQ, irq); +} + +struct irqaction irq0 = { NULL, SA_INTERRUPT, 0, + "timer", NULL, NULL}; + +void cc_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + u32 count; + + /* + * The cycle counter is only 32 bit which is good for about + * a minute at current count rates of upto 150MHz or so. + */ + count = read_32bit_cp0_register(CP0_COUNT); + timerhi += (count < timerlo); /* Wrap around */ + timerlo = count; + + write_32bit_cp0_register (CP0_COMPARE, + (u32) (count + cc_interval)); + kstat.irqs[0][irq]++; + do_timer (regs); + + if (!jiffies) + { + /* + * If jiffies has overflowed in this timer_interrupt we must + * update the timer[hi]/[lo] to make do_fast_gettimeoffset() + * quotient calc still valid. -arca + */ + timerhi = timerlo = 0; + } +} + +/* + * On MIPS only R4000 and better have a cycle counter. + * + * FIXME: Does playing with the RP bit in c0_status interfere with this code? + */ +static unsigned long do_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + + /* Last jiffy when do_fast_gettimeoffset() was called. */ + static unsigned long last_jiffies; + u32 quotient; + + /* + * Cached "1/(clocks per usec)*2^32" value. + * It has to be recalculated once each jiffy. + */ + static u32 cached_quotient; + + tmp = jiffies; + + quotient = cached_quotient; + + if (tmp && last_jiffies != tmp) { + last_jiffies = tmp; + __asm__(".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "lwu\t%0,%2\n\t" + "dsll32\t$1,%1,0\n\t" + "or\t$1,$1,%0\n\t" + "ddivu\t$0,$1,%3\n\t" + "mflo\t$1\n\t" + "dsll32\t%0,%4,0\n\t" + "nop\n\t" + "ddivu\t$0,%0,$1\n\t" + "mflo\t%0\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=&r" (quotient) + :"r" (timerhi), + "m" (timerlo), + "r" (tmp), + "r" (USECS_PER_JIFFY) + :"$1"); + cached_quotient = quotient; + } + + /* Get last timer tick in absolute kernel time */ + count = read_32bit_cp0_register(CP0_COUNT); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu\t%1,%2\n\t" + "mfhi\t%0" + :"=r" (res) + :"r" (count), + "r" (quotient)); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY-1; + + return res; +} + +void __init time_init(void) +{ + unsigned int epoch = 0, year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + + /* Attempt to guess the epoch. This is the same heuristic as in + * rtc.c so no stupid things will happen to timekeeping. Who knows, + * maybe Ultrix also uses 1952 as epoch ... + */ + if (year > 10 && year < 44) + epoch = 1980; + else if (year < 96) + epoch = 1952; + year += epoch; + + write_lock_irq (&xtime_lock); + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); + xtime.tv_usec = 0; + write_unlock_irq (&xtime_lock); + + write_32bit_cp0_register(CP0_COUNT, 0); + irq0.handler = cc_timer_interrupt; + + ip32_timer_setup (&irq0); + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + /* Set ourselves up for future interrupts */ + write_32bit_cp0_register(CP0_COMPARE, + read_32bit_cp0_register(CP0_COUNT) + + cc_interval); + change_cp0_status(ST0_IM, ALLINTS); + sti (); +} +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + { + unsigned long lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); } |