From: James S. <jsi...@us...> - 2001-11-08 17:42:11
|
Update of /cvsroot/linux-mips/linux/arch/mips/sibyte/sb1250 In directory usw-pr-cvs1:/tmp/cvs-serv7520/sibyte/sb1250 Added Files: Makefile bcm1250_tbprof.c bcm1250_tbprof.h irq.c irq_handler.S lib_hssubr.S lib_hssubr.h pci.c setup.c smp.c time.c Log Message: Cleanup Makefile crap and add support for Sibyte SB1250 / SWARM. --- NEW FILE: Makefile --- #all: sb1250.a O_TARGET := sb1250.o obj-y := setup.o irq.o irq_handler.o time.o pci.o lib_hssubr.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_BCM1250_TBPROF) += bcm1250_tbprof.o include $(TOPDIR)/Rules.make --- NEW FILE: bcm1250_tbprof.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. */ #define SBPROF_TB_DEBUG 0 #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/reboot.h> #include <linux/devfs_fs_kernel.h> #include <asm/uaccess.h> #include <asm/smplock.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_scd.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/sbmips.h> #include <asm/sibyte/64bit.h> #include "bcm1250_tbprof.h" #define DEVNAME "sb1250_tbprof" static struct sbprof_tb *sbp; /************************************************************************ * Support for ZBbus sampling using the trace buffer * * We use the SCD performance counter interrupt, caused by a Zclk counter * overflow, to trigger the start of tracing. * * We set the trace buffer to sample everything and freeze on * overflow. * * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. * ************************************************************************/ /* Once per second on a 500 Mhz 1250 */ #define TB_PERIOD 250000000ULL static void arm_tb(void) { unsigned long long next = (1ULL << 40) - TB_PERIOD; /* Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to trigger start of trace. XXX vary sampling period */ out64(0, KSEG1 + A_SCD_PERF_CNT_1); out64(in64(KSEG1 + A_SCD_PERF_CNT_CFG) | // keep counters 0,2,3 as is M_SPC_CFG_ENABLE | // enable counting V_SPC_CFG_SRC1(1), // counter 1 counts cycles KSEG1 + A_SCD_PERF_CNT_CFG); out64(next, KSEG1 + A_SCD_PERF_CNT_1); /* Reset the trace buffer */ out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG); out64(M_SCD_TRACE_CFG_FREEZE_FULL, KSEG1 + A_SCD_TRACE_CFG); sbp->tb_armed = 1; } static void sbprof_tb_intr(int irq, void *dev_id, struct pt_regs *regs) { int i; DBG(printk(DEVNAME ": tb_intr\n")); if (sbp->next_tb_sample < MAX_TB_SAMPLES) { /* XXX should use XKPHYS to make writes bypass L2 */ unsigned long long *p = sbp->sbprof_tbbuf[sbp->next_tb_sample++]; /* Read out trace */ out64(M_SCD_TRACE_CFG_START_READ, KSEG1 + A_SCD_TRACE_CFG); __asm__ __volatile__ ("sync" : : : "memory"); /* Loop runs backwards because bundles are read out in reverse order */ for (i = 256 * 6; i > 0; i -= 6) { // Subscripts decrease to put bundle in the order // t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi p[i-1] = in64(KSEG1 + A_SCD_TRACE_READ); // read t2 hi p[i-2] = in64(KSEG1 + A_SCD_TRACE_READ); // read t2 lo p[i-3] = in64(KSEG1 + A_SCD_TRACE_READ); // read t1 hi p[i-4] = in64(KSEG1 + A_SCD_TRACE_READ); // read t1 lo p[i-5] = in64(KSEG1 + A_SCD_TRACE_READ); // read t0 hi p[i-6] = in64(KSEG1 + A_SCD_TRACE_READ); // read t0 lo } if (!sbp->tb_enable) { DBG(printk(DEVNAME ": tb_intr shutdown\n")); out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG); sbp->tb_armed = 0; wake_up(&sbp->tb_sync); } else { arm_tb(); // knock down current interrupt and get another one later } } else { /* No more trace buffer samples */ DBG(printk(DEVNAME ": tb_intr full\n")); out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG); sbp->tb_armed = 0; if (!sbp->tb_enable) { wake_up(&sbp->tb_sync); } } } static void sbprof_pc_intr(int irq, void *dev_id, struct pt_regs *regs) { panic(DEVNAME ": pc_intr"); } static int sbprof_zbprof_start(struct file *filp) { if (sbp->tb_enable) return -EBUSY; DBG(printk(DEVNAME ": starting\n")); sbp->tb_enable = 1; sbp->next_tb_sample = 0; filp->f_pos = 0; if (request_irq (K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, "sbprof_tb trace freeze", sbp)) { return -EBUSY; } if (request_irq (K_INT_PERF_CNT, sbprof_pc_intr, 0, "sbprof_tb scd perfcnt", sbp)) { free_irq(K_INT_TRACE_FREEZE, sbp); return -EBUSY; } /* I need the core to mask these, but the interrupt mapper to pass them through. I am exploiting my knowledge that cp0_status masks out IP[5]. krw */ out64(K_INT_MAP_I3, KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + (K_INT_PERF_CNT<<3)); /* Initialize address traps */ out64(0, KSEG1 + A_ADDR_TRAP_UP_0); out64(0, KSEG1 + A_ADDR_TRAP_UP_1); out64(0, KSEG1 + A_ADDR_TRAP_UP_2); out64(0, KSEG1 + A_ADDR_TRAP_UP_3); out64(0, KSEG1 + A_ADDR_TRAP_DOWN_0); out64(0, KSEG1 + A_ADDR_TRAP_DOWN_1); out64(0, KSEG1 + A_ADDR_TRAP_DOWN_2); out64(0, KSEG1 + A_ADDR_TRAP_DOWN_3); out64(0, KSEG1 + A_ADDR_TRAP_CFG_0); out64(0, KSEG1 + A_ADDR_TRAP_CFG_1); out64(0, KSEG1 + A_ADDR_TRAP_CFG_2); out64(0, KSEG1 + A_ADDR_TRAP_CFG_3); /* Initialize Trace Event 0-7 */ // when interrupt out64(M_SCD_TREVT_INTERRUPT, KSEG1 + A_SCD_TRACE_EVENT_0); out64(0, KSEG1 + A_SCD_TRACE_EVENT_1); out64(0, KSEG1 + A_SCD_TRACE_EVENT_2); out64(0, KSEG1 + A_SCD_TRACE_EVENT_3); out64(0, KSEG1 + A_SCD_TRACE_EVENT_4); out64(0, KSEG1 + A_SCD_TRACE_EVENT_5); out64(0, KSEG1 + A_SCD_TRACE_EVENT_6); out64(0, KSEG1 + A_SCD_TRACE_EVENT_7); /* Initialize Trace Sequence 0-7 */ // Start on event 0 (interrupt) out64(V_SCD_TRSEQ_FUNC_START|0x0fff, KSEG1 + A_SCD_TRACE_SEQUENCE_0); // dsamp when d used | asamp when a used out64(M_SCD_TRSEQ_ASAMPLE|M_SCD_TRSEQ_DSAMPLE|K_SCD_TRSEQ_TRIGGER_ALL, KSEG1 + A_SCD_TRACE_SEQUENCE_1); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_2); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_3); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_4); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_5); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_6); out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_7); /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */ out64((1ULL << K_INT_PERF_CNT), KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)); arm_tb(); DBG(printk(DEVNAME ": done starting\n")); return 0; } static int sbprof_zbprof_stop(void) { DBG(printk(DEVNAME ": stopping\n")); if (sbp->tb_enable) { sbp->tb_enable = 0; /* XXXKW there is a window here where the intr handler may run, see the disable, and do the wake_up before this sleep happens. */ if (sbp->tb_armed) { DBG(printk(DEVNAME ": wait for disarm\n")); interruptible_sleep_on(&sbp->tb_sync); DBG(printk(DEVNAME ": disarm complete\n")); } free_irq(K_INT_TRACE_FREEZE, sbp); free_irq(K_INT_PERF_CNT, sbp); } DBG(printk(DEVNAME ": done stopping\n")); return 0; } static int sbprof_tb_open(struct inode *inode, struct file *filp) { int minor; minor = MINOR(inode->i_rdev); if (minor != 0) { return -ENODEV; } if (sbp != NULL) { return -EBUSY; } /* XXXKW spinlock? */ sbp = kmalloc(sizeof(struct sbprof_tb), GFP_KERNEL); if (!sbp) { return -ENOMEM; } memset(sbp, 0, sizeof(struct sbprof_tb)); sbp->sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES); if (!sbp->sbprof_tbbuf) { kfree(sbp); sbp = NULL; return -ENOMEM; } memset(sbp->sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES); init_waitqueue_head(&sbp->tb_sync); return 0; } static int sbprof_tb_release(struct inode *inode, struct file *filp) { int minor; minor = MINOR(inode->i_rdev); if (minor != 0 || sbp == NULL) { return -ENODEV; } if (sbp->tb_armed || sbp->tb_enable) { sbprof_zbprof_stop(); } vfree(sbp->sbprof_tbbuf); kfree(sbp); sbp = NULL; return 0; } static ssize_t sbprof_tb_read(struct file *filp, char *buf, size_t size, loff_t *offp) { int cur_sample, sample_off, cur_count, sample_left; char *src; int count = 0; char *dest = buf; long cur_off = *offp; count = 0; cur_sample = cur_off / TB_SAMPLE_SIZE; sample_off = cur_off % TB_SAMPLE_SIZE; sample_left = TB_SAMPLE_SIZE - sample_off; while (size && (cur_sample < sbp->next_tb_sample)) { cur_count = size < sample_left ? size : sample_left; src = (char *)(((long)sbp->sbprof_tbbuf[cur_sample])+sample_off); copy_to_user(dest, src, cur_count); DBG(printk(DEVNAME ": read from sample %d, %d bytes\n", cur_sample, cur_count)); size -= cur_count; sample_left -= cur_count; if (!sample_left) { cur_sample++; sample_off = 0; sample_left = TB_SAMPLE_SIZE; } else { sample_off += cur_count; } cur_off += cur_count; dest += cur_count; count += cur_count; } *offp = cur_off; return count; } #define SBPROF_ZBSTART _IOW('s', 0, int) #define SBPROF_ZBSTOP _IOW('s', 1, int) #define SBPROF_ZBFULL _IOW('s', 2, int) static int sbprof_tb_ioctl(struct inode *inode, struct file *filp, unsigned int command, unsigned long arg) { int error = 0; int full; switch (command) { case SBPROF_ZBSTART: error = sbprof_zbprof_start(filp); break; case SBPROF_ZBSTOP: error = sbprof_zbprof_stop(); break; case SBPROF_ZBFULL: full = (sbp->next_tb_sample == MAX_TB_SAMPLES); return put_user(full, (int *) arg); default: error = -EINVAL; break; } return error; } static struct file_operations sbprof_tb_fops = { owner: THIS_MODULE, open: sbprof_tb_open, release: sbprof_tb_release, read: sbprof_tb_read, ioctl: sbprof_tb_ioctl, mmap: NULL, }; static devfs_handle_t devfs_handle; static int __init sbprof_tb_init(void) { if (devfs_register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) { printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n", SBPROF_TB_MAJOR); return -EIO; } devfs_handle = devfs_register(NULL, DEVNAME, DEVFS_FL_DEFAULT, SBPROF_TB_MAJOR, 0, S_IFCHR | S_IRUGO | S_IWUGO, &sbprof_tb_fops, NULL); sbp = NULL; printk(KERN_INFO DEVNAME ": initialized\n"); return 0; } static void __exit sbprof_tb_cleanup(void) { devfs_unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); devfs_unregister(devfs_handle); } module_init(sbprof_tb_init); module_exit(sbprof_tb_cleanup); --- NEW FILE: bcm1250_tbprof.h --- /* * 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. */ #ifndef BCM1250_TBPROF_H #if SBPROF_TB_DEBUG #define DBG(a) a #else #define DBG(a) #endif #define SBPROF_TB_MAJOR 240 typedef u_int64_t tb_sample_t[6*256]; struct sbprof_tb { tb_sample_t *sbprof_tbbuf; int next_tb_sample; volatile int tb_enable; volatile int tb_armed; wait_queue_head_t tb_sync; }; #define MAX_SAMPLE_BYTES (24*1024*1024) #define MAX_TBSAMPLE_BYTES (12*1024*1024) #define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t)) #define TB_SAMPLE_SIZE (sizeof(tb_sample_t)) #define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE) /*************************************************************************** * Routines for gathering ZBbus profiles using trace buffer ***************************************************************************/ /* Requires: Already called zclk_timer_init with a value that won't saturate 40 bits. No subsequent use of SCD performance counters or trace buffer. Effect: Starts gathering random ZBbus profiles using trace buffer. */ static int sbprof_zbprof_start(struct file *filp); /* Effect: Stops collection of ZBbus profiles */ static int sbprof_zbprof_stop(void); /*************************************************************************** * Routines for using 40-bit SCD cycle counter * * Client responsible for either handling interrupts or making sure * the cycles counter never saturates, e.g., by doing * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. ***************************************************************************/ /* Configures SCD counter 0 to count ZCLKs starting from val; Configures SCD counters1,2,3 to count nothing. Must not be called while gathering ZBbus profiles. unsigned long long val; */ #define zclk_timer_init(val) \ __asm__ __volatile__ (".set push;" \ ".set mips64;" \ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ "sd %0, 0x10($8);" /* write val to counter0 */ \ "sd %1, 0($8);" /* config counter0 for zclks*/ \ ".set pop" \ : /* no outputs */ \ /* enable, counter0 */ \ : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \ : /* modifies */ "$8" ) /* Reads SCD counter 0 and puts result in value unsigned long long val; */ #define zclk_get(val) \ __asm__ __volatile__ (".set push;" \ ".set mips64;" \ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ "ld %0, 0x10($8);" /* write val to counter0 */ \ ".set pop" \ : /* outputs */ "=r"(val) \ : /* inputs */ \ : /* modifies */ "$8" ) #endif --- 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/errno.h> #include <asm/signal.h> #include <asm/system.h> #include <asm/ptrace.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/sb1250_scd.h> #include <asm/sibyte/sb1250.h> #include <asm/sibyte/64bit.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 */ #define shutdown_sb1250_irq disable_sb1250_irq static void end_sb1250_irq(unsigned int irq); static void enable_sb1250_irq(unsigned int irq); static void disable_sb1250_irq(unsigned int irq); static unsigned int startup_sb1250_irq(unsigned int irq); static void ack_sb1250_irq(unsigned int irq); #ifdef CONFIG_REMOTE_DEBUG extern void breakpoint(void); #endif #define NR_IRQS 64 static struct hw_interrupt_type sb1250_irq_type = { "SB1250-IMR", startup_sb1250_irq, shutdown_sb1250_irq, enable_sb1250_irq, disable_sb1250_irq, ack_sb1250_irq, end_sb1250_irq, NULL }; spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; spinlock_t sb1250_imr_lock = SPIN_LOCK_UNLOCKED; void sb1250_mask_irq(int cpu, int irq) { unsigned long flags; u64 cur_ints; spin_lock_irqsave(&sb1250_imr_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(&sb1250_imr_lock, flags); } void sb1250_unmask_irq(int cpu, int irq) { unsigned long flags; u64 cur_ints; spin_lock_irqsave(&sb1250_imr_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(&sb1250_imr_lock, flags); } /* Defined in arch/mips/sibyte/sb1250/irq_handler.S */ extern void sb1250_irq_handler(void); /* ********************************************************************************************* */ static unsigned int startup_sb1250_irq(unsigned int irq) { sb1250_unmask_irq(0, irq); return 0; /* never anything pending */ } static void disable_sb1250_irq(unsigned int irq) { sb1250_mask_irq(0, irq); } static void enable_sb1250_irq(unsigned int irq) { sb1250_unmask_irq(0, irq); } static void ack_sb1250_irq(unsigned int irq) { sb1250_mask_irq(0, irq); } static void end_sb1250_irq(unsigned int irq) { if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { sb1250_unmask_irq(0, irq); } } void __init init_sb1250_irqs(void) { int i; for (i = 0; i < NR_IRQS; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = 0; irq_desc[i].depth = 1; irq_desc[i].handler = &sb1250_irq_type; } } /* * 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 IP[2] so it is handled. This is needed so we * can do cross-cpu function calls, as requred by SMP */ #define IMR_IP2_VAL K_INT_MAP_I0 #define IMR_IP3_VAL K_INT_MAP_I1 #define IMR_IP4_VAL K_INT_MAP_I2 void __init init_IRQ(void) { unsigned int i; u64 tmp; /* Default everything to IP2 */ for (i = 0; i < NR_IRQS; i++) { /* was I0 */ out64(IMR_IP2_VAL, KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + (i << 3)); out64(IMR_IP2_VAL, KSEG1 + A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) + (i << 3)); } init_sb1250_irqs(); /* Map the high 16 bits of the mailbox registers to IP[3], for inter-cpu messages */ /* Was I1 */ out64(IMR_IP3_VAL, KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + (K_INT_MBOX_0 << 3)); out64(IMR_IP3_VAL, KSEG1 + A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) + (K_INT_MBOX_0 << 3)); /* Clear the mailboxes. The firmware may leave them dirty */ out64(0xffffffffffffffff, KSEG1 + A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)); out64(0xffffffffffffffff, KSEG1 + A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU)); /* Mask everything except the mailbox registers for both cpus */ tmp = ~((u64) 0) ^ (((u64) 1) << K_INT_MBOX_0); out64(tmp, KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK)); out64(tmp, KSEG1 + A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK)); /* Note that the timer interrupts are also mapped, but this is done in sb1250_time_init() */ #ifdef CONFIG_SIBYTE_SB1250_PROF /* Enable IP[7,4:0], disable the rest */ clear_cp0_status(0x6000); set_cp0_status(0x9f00); #else /* Enable IP[4:0], disable the rest */ clear_cp0_status(0xe000); set_cp0_status(0x1f00); #endif set_except_vector(0, sb1250_irq_handler); #ifdef CONFIG_REMOTE_DEBUG /* If local serial I/O used for debug port, enter kgdb at once */ // puts("Waiting for kgdb to connect..."); set_debug_traps(); breakpoint(); #endif } --- 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_defs.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> #include "asm/sibyte/sbmips.h" /* * What a pain. We have to be really careful saving * the upper 32 bits of any register across function * calls if we don't want them trashed--since were * running in -o32, the calling routing never saves * the full 64 bits of a register across a function * call. Being the interrupt handler, we're guaranteed * that interrupts are disabled during this code so we * don't have to worry about random interrupts blasting * the high 32 bits. */ .text .set push .set noreorder .set noat .set mips64 .align 5 NESTED(sb1250_irq_handler, PT_SIZE, sp) SAVE_ALL /* Indicate kernel mode (set CU0, clear KSU,ERL,EXL, leave IE off) */ CLI li s1, 0xb00a0020 lw t1, 0(s1) #ifdef CONFIG_SIBYTE_SB1250_PROF /* Set compare to count to silence count/compare timer interrupts */ mfc0 t1, C0_COUNT mtc0 t1, C0_COMPARE /* pause to clear IP[7] bit of cause ? */ #endif /* Read cause */ mfc0 s0, C0_CAUSE #ifdef CONFIG_SIBYTE_SB1250_PROF /* Cpu performance counter interrupt is routed to IP[7] */ andi t1, s0, CAUSEF_IP7 beqz t1, 0f srl t1, s0, (CAUSEB_BD-2) /* Shift BD bit to bit 2 */ and t1, t1, 0x4 /* mask to get just BD bit */ mfc0 a0, C0_EPC jal sbprof_cpu_intr addu a0, a0, t1 /* a0 = EPC + (BD ? 4 : 0) */ j ret_from_irq nop # delay slot 0: #endif /* Timer interrupt is routed to IP[4] */ andi t1, s0, CAUSEF_IP4 beqz t1, 1f nop jal sb1250_timer_interrupt move a0, sp /* Pass the registers along */ lw t1, 4(s1) j ret_from_irq nop # delay slot 1: lw t1, 8(s1) #ifdef CONFIG_SMP /* Mailbox interrupt is routed to IP[3] */ andi t1, s0, CAUSEF_IP3 beqz t1, 2f nop jal sb1250_mailbox_interrupt move a0, sp lw t1, 12(s1) j ret_from_irq nop # delay slot 2: lw t1, 16(s1) #endif and t1, s0, CAUSEF_IP2 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 */ /* * Don't let timer interrupts or mailbox interrupts get dispatched * through this mechanism - they get handled specially above */ #ifdef CONFIG_SMP li t0, (_SB_MAKEMASK1(K_INT_TIMER_0) | _SB_MAKEMASK1(K_INT_TIMER_1) \ | _SB_MAKEMASK1(K_INT_MBOX_0)) /* remove timer bits from mask */ #else li t0, _SB_MAKEMASK1(K_INT_TIMER_0) /* remove timer bits from mask */ #endif not t0 and s0, s0, t0 /* timer interrupts don't count here */ /* Now the timer and mailbox interrupts have been removed from the pending mask */ 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: dclz s1, s0 /* Find the next interrupt */ dsubu a0, zero, s1 daddiu a0, a0, 63 jal do_IRQ nop 4: j ret_from_irq /* defined in arch/mips/kernel/entry.S */ nop .set pop END(sb1250_irq_handler) --- NEW FILE: lib_hssubr.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. */ #include "asm/sibyte/sb1250_defs.h" #include "asm/sibyte/sbmips.h" .set mips64 #define HAZARD ssnop ; ssnop ; ssnop ; ssnop ; ssnop ; ssnop ; ssnop /* ********************************************************************* * hs_read8 - read 8-bit bytes ********************************************************************* */ LEAF(hs_read8) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 lb v0,(a0) and v0,0xFF mtc0 t2,C0_SR HAZARD j ra END(hs_read8) /* ********************************************************************* * hs_read16 - read 16-bit shorts ********************************************************************* */ LEAF(hs_read16) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 lh v0,(a0) and v0,0xFFFF mtc0 t2,C0_SR HAZARD j ra END(hs_read16) /* ********************************************************************* * hs_read32 - read 32-bit ints ********************************************************************* */ LEAF(hs_read32) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 lw v0,(a0) and v0,0xFFFFFFFF mtc0 t2,C0_SR HAZARD j ra END(hs_read32) /* ********************************************************************* * hs_read64 - read 64-bit longs ********************************************************************* */ LEAF(hs_read64) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 ld v0,(a0) mtc0 t2,C0_SR HAZARD j ra END(hs_read64) /* ********************************************************************* * hs_write8 - write 8-bit bytes ********************************************************************* */ LEAF(hs_write8) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 sb a1,(a0) mtc0 t2,C0_SR HAZARD j ra END(hs_write8) /* ********************************************************************* * hs_write16 - write 16-bit shorts ********************************************************************* */ LEAF(hs_write16) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 sh a1,(a0) mtc0 t2,C0_SR HAZARD j ra END(hs_write16) /* ********************************************************************* * hs_write32 - write 32-bit longs ********************************************************************* */ LEAF(hs_write32) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 sw a1,(a0) mtc0 t2,C0_SR HAZARD j ra END(hs_write32) /* ********************************************************************* * hs_write64 - write 64-bit longs ********************************************************************* */ LEAF(hs_write64) mfc0 t2,C0_SR or t1,t2,M_SR_KX mtc0 t1,C0_SR HAZARD dli v0,PHYS_TO_XKSEG_UNCACHED(0) dsll a0,a0,32 dsrl a0,a0,32 or a0,a0,v0 sd a1,(a0) mtc0 t2,C0_SR HAZARD j ra END(hs_write64) /* ********************************************************************* * End ********************************************************************* */ --- NEW FILE: lib_hssubr.h --- /* * 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. */ #ifndef _LIB_HSSUBR_H #define _LIB_HSSUBR_H /* * If __long64 we can do this via macros. Otherwise, call * the magic functions. */ #if __long64 typedef long hsaddr_t; #define hs_write8(a,b) *((volatile uint8_t *) (a)) = (b) #define hs_write16(a,b) *((volatile uint16_t *) (a)) = (b) #define hs_write32(a,b) *((volatile uint32_t *) (a)) = (b) #define hs_write64(a,b) *((volatile uint32_t *) (a)) = (b) #define hs_read8(a) *((volatile uint8_t *) (a)) #define hs_read16(a) *((volatile uint16_t *) (a)) #define hs_read32(a) *((volatile uint32_t *) (a)) #define hs_read64(a) *((volatile uint64_t *) (a)) #else /* not __long64 */ typedef long hsaddr_t; extern void hs_write8(hsaddr_t a,uint8_t b); extern void hs_write16(hsaddr_t a,uint16_t b); extern void hs_write32(hsaddr_t a,uint32_t b); extern void hs_write64(hsaddr_t a,uint64_t b); extern uint8_t hs_read8(hsaddr_t a); extern uint16_t hs_read16(hsaddr_t a); extern uint32_t hs_read32(hsaddr_t a); extern uint64_t hs_read64(hsaddr_t a); #endif #endif --- 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> #include "lib_hssubr.h" /* * 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) hs_read32((addr)&~3) #define WRITECFG32(addr,data) hs_write32(((addr)&~3),(data)) /* * This variable is the KSEG2 (kernel virtual) 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. */ extern unsigned long mips_io_port_base; #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) /* * 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; /* * Establish a mapping from KSEG2 (kernel virtual) to PCI I/O space * Use "match bytes", even though this exposes endianness. * big-endian Linuxes will have CONFIG_SWAP_IO_SPACE set. */ mips_io_port_base = (unsigned long) ioremap(A_PHYS_LDTPCI_IO_MATCH_BYTES, 65536); /* * 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; } /* 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); } unsigned __init int pcibios_assign_all_busses(void) { return 1; } #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/config.h> #include <linux/errno.h> #include <linux/hdreg.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> #include <linux/string.h> #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> #include <linux/utsname.h> #include <linux/a.out.h> #include <linux/tty.h> #include <linux/bootmem.h> #ifdef CONFIG_BLK_DEV_RAM #include <linux/blk.h> #endif #include <linux/ide.h> #ifdef CONFIG_RTC #include <linux/timex.h> #endif #include <asm/asm.h> #include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/io.h> #include <asm/stackframe.h> #include <asm/system.h> #include <asm/cpu.h> #include <asm/mmu_context.h> #include <asm/sibyte/swarm.h> #include <asm/sibyte/sb1250_defs.h> #include <asm/sibyte/sb1250_regs.h> /* * */ void sb1250_setup(void) { } --- 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 <linux/sched.h> /* * 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 core_send_ipi(int cpu, unsigned int action) { out64((((u64)action)<< 48), mailbox_set_regs[cpu]); } void sb1250_smp_finish(void) { extern void sb1_sanitize_tlb(void); sb1_sanitize_tlb(); sb1250_time_init(); } static void sb1250_smp_reschedule(void) { current->need_resched = 1; } static void sb1250_smp_call_function(void) { void (*func) (void *info) = smp_fn_call.fn; void *data = smp_fn_call.data; int wait = smp_fn_call.wait; /* Notify initiating CPU that I've grabbed the data * and am about to execute the function */ atomic_inc(&smp_fn_call.started); (*func)(data); if (wait) atomic_inc(&smp_fn_call.finished); } void sb1250_mailbox_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); unsigned int action; /* Load the mailbox register to figure out what we're supposed to do */ action = (in64(mailbox_regs[cpu]) >> 48) & 0xffff; /* Clear the mailbox to clear the interrupt */ out64(((u64)action)<<48, mailbox_clear_regs[cpu]); if (action & SMP_RESCHEDULE_YOURSELF) { sb1250_smp_reschedule(); } if (action & SMP_CALL_FUNCTION) { sb1250_smp_call_function(); } } --- 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/sibyte/sb1250.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/sb1250_scd.h> #include <asm/sibyte/64bit.h> void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); #define IMR_IP2_VAL K_INT_MAP_I0 #define IMR_IP3_VAL K_INT_MAP_I1 #define IMR_IP4_VAL K_INT_MAP_I2 void sb1250_time_init(void) { int cpu = smp_processor_id(); /* Only have 4 general purpose timers */ if (cpu > 3) { BUG(); } sb1250_mask_irq(cpu, K_INT_TIMER_0 + cpu); /* Map the timer interrupt to ip[4] of this cpu */ out64(IMR_IP4_VAL, 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( #ifndef CONFIG_SIMULATION 1000000/HZ #else 50000/HZ #endif , 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, K_INT_TIMER_0 + 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 */ } extern int set_rtc_mmss(unsigned long nowtime); extern rwlock_t xtime_lock; static long last_rtc_update = 0; 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); read_lock(&xtime_lock); if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec >= 500000 - (tick >> 1) && xtime.tv_usec <= 500000 + (tick >> 1)) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else /* do it again in 60 s */ last_rtc_update = xtime.tv_sec - 600; read_unlock(&xtime_lock); } #ifdef CONFIG_SMP { int user = user_mode(regs); /* We need to make like a normal interrupt -- otherwise timer interrupts ignore the global interrupt lock, which would be a Bad Thing. */ irq_enter(cpu, 0); update_process_times(user); irq_exit(cpu, 0); if (softirq_pending(cpu)) do_softirq(); } #endif /* CONFIG_SMP */ } |