diff options
Diffstat (limited to 'arch/ppc/kernel')
38 files changed, 2312 insertions, 541 deletions
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 7aaacfadb..ea7c7c6e7 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -38,7 +38,7 @@ O_OBJS += hashtable.o endif ifdef CONFIG_PCI -O_OBJS += pci.o +O_OBJS += pci.o pci-dma.o endif ifdef CONFIG_KGDB @@ -59,8 +59,16 @@ endif ifeq ($(CONFIG_4xx),y) O_OBJS += ppc4xx_pic.o - ifeq ($(CONFIG_OAK),y) - O_OBJS += oak_setup.o +endif + +ifeq ($(CONFIG_OAK),y) + O_OBJS += oak_setup.o +endif + +ifeq ($(CONFIG_WALNUT),y) + O_OBJS += walnut_setup.o + ifeq ($(CONFIG_PCI),y) + O_OBJS += galaxy_pci.o endif endif @@ -83,6 +91,9 @@ endif ifeq ($(CONFIG_6xx),y) O_OBJS += open_pic.o indirect_pci.o endif +ifeq ($(CONFIG_PPC64),y) + O_OBJS += open_pic.o indirect_pci.o +endif ifeq ($(CONFIG_APUS),y) O_OBJS += apus_setup.o endif diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index a7b057fa1..5f0c4b06e 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -19,7 +19,6 @@ #include <linux/sched.h> #include <linux/kd.h> #include <linux/init.h> -#include <linux/serial.h> #include <linux/hdreg.h> #include <linux/blk.h> #include <linux/pci.h> @@ -28,8 +27,41 @@ #include <asm/logging.h> #endif -/* Get the IDE stuff from the 68k file */ #include <linux/ide.h> +#define T_CHAR (0x0000) /* char: don't touch */ +#define T_SHORT (0x4000) /* short: 12 -> 21 */ +#define T_INT (0x8000) /* int: 1234 -> 4321 */ +#define T_TEXT (0xc000) /* text: 12 -> 21 */ + +#define T_MASK_TYPE (0xc000) +#define T_MASK_COUNT (0x3fff) + +#define D_CHAR(cnt) (T_CHAR | (cnt)) +#define D_SHORT(cnt) (T_SHORT | (cnt)) +#define D_INT(cnt) (T_INT | (cnt)) +#define D_TEXT(cnt) (T_TEXT | (cnt)) + +static u_short driveid_types[] = { + D_SHORT(10), /* config - vendor2 */ + D_TEXT(20), /* serial_no */ + D_SHORT(3), /* buf_type, buf_size - ecc_bytes */ + D_TEXT(48), /* fw_rev - model */ + D_CHAR(2), /* max_multsect - vendor3 */ + D_SHORT(1), /* dword_io */ + D_CHAR(2), /* vendor4 - capability */ + D_SHORT(1), /* reserved50 */ + D_CHAR(4), /* vendor5 - tDMA */ + D_SHORT(4), /* field_valid - cur_sectors */ + D_INT(1), /* cur_capacity */ + D_CHAR(2), /* multsect - multsect_valid */ + D_INT(1), /* lba_capacity */ + D_SHORT(194) /* dma_1word - reservedyy */ +}; + +#define num_driveid_types (sizeof(driveid_types)/sizeof(*driveid_types)) + +#if 0 /* Get rid of this crud */ +/* Get the IDE stuff from the 68k file */ #define ide_init_hwif_ports m68k_ide_init_hwif_ports #define ide_default_irq m68k_ide_default_irq #undef ide_request_irq @@ -57,6 +89,7 @@ #undef ide_release_region #undef ide_fix_driveid /*-------------------------------------------*/ +#endif #include <asm/bootinfo.h> #include <asm/setup.h> @@ -411,33 +444,6 @@ void kbd_reset_setup(char *str, int *ints) { } -#if defined(CONFIG_WHIPPET_SERIAL)||defined(CONFIG_MULTIFACE_III_TTY)||defined(CONFIG_GVPIOEXT)||defined(CONFIG_AMIGA_BUILTIN_SERIAL) - -long m68k_rs_init(void); -int m68k_register_serial(struct serial_struct *); -void m68k_unregister_serial(int); -long m68k_serial_console_init(long, long ); - -int rs_init(void) -{ - return m68k_rs_init(); -} -int register_serial(struct serial_struct *p) -{ - return m68k_register_serial(p); -} -void unregister_serial(int i) -{ - m68k_unregister_serial(i); -} -#ifdef CONFIG_SERIAL_CONSOLE -long serial_console_init(long kmem_start, long kmem_end) -{ - return m68k_serial_console_init(kmem_start, kmem_end); -} -#endif -#endif - /*********************************************************** FLOPPY */ #if defined(CONFIG_AMIGA_FLOPPY) __init @@ -673,7 +679,7 @@ apus_ide_outsw(ide_ioreg_t port, void *buf, int ns) int apus_ide_default_irq(ide_ioreg_t base) { - return m68k_ide_default_irq(base); + return 0; } ide_ioreg_t @@ -685,7 +691,7 @@ apus_ide_default_io_base(int index) int apus_ide_check_region(ide_ioreg_t from, unsigned int extent) { - return m68k_ide_check_region(from, extent); + return 0; } void @@ -693,27 +699,66 @@ apus_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name) { - m68k_ide_request_region(from, extent, name); } void apus_ide_release_region(ide_ioreg_t from, unsigned int extent) { - m68k_ide_release_region(from, extent); } void apus_ide_fix_driveid(struct hd_driveid *id) { - m68k_ide_fix_driveid(id); + u_char *p = (u_char *)id; + int i, j, cnt; + u_char t; + + if (!MACH_IS_AMIGA && !MACH_IS_MAC) + return; + for (i = 0; i < num_driveid_types; i++) { + cnt = driveid_types[i] & T_MASK_COUNT; + switch (driveid_types[i] & T_MASK_TYPE) { + case T_CHAR: + p += cnt; + break; + case T_SHORT: + for (j = 0; j < cnt; j++) { + t = p[0]; + p[0] = p[1]; + p[1] = t; + p += 2; + } + break; + case T_INT: + for (j = 0; j < cnt; j++) { + t = p[0]; + p[0] = p[3]; + p[3] = t; + t = p[1]; + p[1] = p[2]; + p[2] = t; + p += 4; + } + break; + case T_TEXT: + for (j = 0; j < cnt; j += 2) { + t = p[0]; + p[0] = p[1]; + p[1] = t; + p += 2; + } + break; + } + } } __init void apus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { - m68k_ide_init_hwif_ports(hw, data_port, ctrl_port, irq); + if (data_port || ctrl_port) + printk("apus_ide_init_hwif_ports: must not be called\n"); } #endif /****************************************************** IRQ stuff */ @@ -732,7 +777,7 @@ int apus_get_irq_list(char *buf) /* IPL must be between 0 and 7 */ __apus -static inline void apus_set_IPL(int ipl) +static inline void apus_set_IPL(unsigned long ipl) { APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); @@ -743,42 +788,22 @@ static inline void apus_set_IPL(int ipl) __apus static inline unsigned long apus_get_IPL(void) { - unsigned short __f; + /* This returns the present IPL emulation level. */ + unsigned long __f; APUS_READ(APUS_IPL_EMU, __f); return ((~__f) & IPLEMU_IPLMASK); } __apus -static inline unsigned long apus_get_prev_IPL(void) -{ - unsigned short __f; - APUS_READ(APUS_IPL_EMU, __f); - return ((~__f >> 3) & IPLEMU_IPLMASK); -} - - -__apus -static void apus_save_flags(unsigned long* flags) +static inline unsigned long apus_get_prev_IPL(struct pt_regs* regs) { - *flags = apus_get_IPL(); -} - -__apus -static void apus_restore_flags(unsigned long flags) -{ - apus_set_IPL(flags); -} - -__apus -static void apus_sti(void) -{ - apus_set_IPL(0); -} - -__apus -static void apus_cli(void) -{ - apus_set_IPL(7); + /* The value saved in mq is the IPL_EMU value at the time of + interrupt. The lower bits are the current interrupt level, + the upper bits the requested level. Thus, to restore the + IPL level to the post-interrupt state, we will need to use + the lower bits. */ + unsigned long __f = regs->mq; + return ((~__f) & IPLEMU_IPLMASK); } @@ -802,6 +827,22 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return amiga_request_irq (irq, handler, irqflags, devname, dev_id); } + +/* In Linux/m68k the sys_request_irq deals with vectors 0-7. That's what + callers expect - but on Linux/APUS we actually use the IRQ_AMIGA_AUTO + vectors (24-31), so we put this dummy function in between to adjust + the vector argument (rather have cruft here than in the generic irq.c). */ +int sys_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + extern int request_sysirq(unsigned int irq, + void (*handler)(int, void *, + struct pt_regs *), + unsigned long irqflags, + const char * devname, void *dev_id); + return request_sysirq(irq+IRQ_AMIGA_AUTO, handler, irqflags, + devname, dev_id); +} #endif __apus @@ -809,14 +850,17 @@ int apus_get_irq(struct pt_regs* regs) { #ifdef CONFIG_APUS int level = apus_get_IPL(); - unsigned short ints = custom.intreqr & custom.intenar; + +#ifdef __INTERRUPT_DEBUG + printk("<%d:%d>", level, apus_get_prev_IPL(regs)); +#endif if (0 == level) - return -1; + return -8; if (7 == level) - return -2; + return -9; - return level; + return level + IRQ_AMIGA_AUTO; #else return 0; #endif @@ -824,10 +868,13 @@ int apus_get_irq(struct pt_regs* regs) __apus -void apus_post_irq(int level) +void apus_post_irq(struct pt_regs* regs, int level) { +#ifdef __INTERRUPT_DEBUG + printk("{%d}", apus_get_prev_IPL(regs)); +#endif /* Restore IPL to the previous value */ - apus_set_IPL(apus_get_IPL()); + apus_set_IPL(apus_get_prev_IPL(regs)); } @@ -903,11 +950,28 @@ irq_node_t *new_irq_node(void) return NULL; } +extern void amiga_enable_irq(unsigned int irq); +extern void amiga_disable_irq(unsigned int irq); + +struct hw_interrupt_type amiga_irqctrl = { + " Amiga ", + NULL, + NULL, + amiga_enable_irq, + amiga_disable_irq, + 0, + 0 +}; + + __init void apus_init_IRQ(void) { int i; + for ( i = 0 ; i < NR_IRQS ; i++ ) + irq_desc[i].ctl = &amiga_irqctrl; + for (i = 0; i < NUM_IRQ_NODES; i++) nodes[i].handler = NULL; @@ -919,10 +983,10 @@ void apus_init_IRQ(void) amiga_init_IRQ(); - int_control.int_sti = apus_sti; - int_control.int_cli = apus_cli; - int_control.int_save_flags = apus_save_flags; - int_control.int_restore_flags = apus_restore_flags; + int_control.int_sti = __no_use_sti; + int_control.int_cli = __no_use_cli; + int_control.int_save_flags = __no_use_save_flags; + int_control.int_restore_flags = __no_use_restore_flags; } __init diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index b93bc45f7..a2fbe5f14 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -273,14 +273,13 @@ void __init chrp_pcibios_fixup(void) { struct pci_dev *dev; - - /* some of IBM chrps have > 1 bus */ - if ( !strncmp("IBM", get_property(find_path_device("/"), - "name", NULL),3) ) - { - - } - + int i; + extern struct pci_ops generic_pci_ops; + + /* Some IBM's with the python have >1 bus, this finds them */ + for ( i = 0; i < python_busnr ; i++ ) + pci_scan_bus(i+1, &generic_pci_ops, NULL); + /* PCI interrupts are controlled by the OpenPIC */ pci_for_each_dev(dev) { if ( dev->irq ) diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index e76aa8dd9..21abfc84f 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -249,7 +249,6 @@ chrp_setup_arch(void) else #endif ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ -sprintf(cmd_line, "console=ttyS0,9600 console=tty0"); printk("Boot arguments: %s\n", cmd_line); request_region(0x20,0x20,"pic1"); @@ -384,7 +383,7 @@ int chrp_get_irq( struct pt_regs *regs ) return irq; } -void chrp_post_irq(int irq) +void chrp_post_irq(struct pt_regs* regs, int irq) { /* * If it's an i8259 irq then we've already done the @@ -394,7 +393,7 @@ void chrp_post_irq(int irq) * We do it this way since our irq_desc[irq].handler can change * with RTL and no longer be open_pic -- Cort */ - if ( irq >= open_pic.irq_offset) + if ( irq >= open_pic_irq_offset) openpic_eoi( smp_processor_id() ); } @@ -411,10 +410,11 @@ void __init chrp_init_IRQ(void) (*(unsigned long *)get_property(np, "8259-interrupt-acknowledge", NULL)); } - open_pic.irq_offset = 16; + open_pic_irq_offset = 16; for ( i = 16 ; i < NR_IRQS ; i++ ) irq_desc[i].handler = &open_pic; openpic_init(1); + enable_irq(IRQ_8259_CASCADE); for ( i = 0 ; i < 16 ; i++ ) irq_desc[i].handler = &i8259_pic; i8259_init(); diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 50c7417fb..d55fa24c0 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -171,9 +171,10 @@ void __init chrp_calibrate_decr(void) if (fp != 0) freq = *fp; } - freq *= 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor); + freq *= 30; + divisor = 30; + printk("time_init: decrementer frequency = %lu/%d (%d MHz)\n", freq, + divisor, (freq/divisor)>>20); decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index 2c4ff5554..2d1238a6b 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -256,6 +256,15 @@ _GLOBAL(_switch) REST_8GPRS(23, r1) REST_GPR(31, r1) lwz r2,_NIP(r1) /* Restore environment */ + /* + * We need to hard disable here even if RTL is active since + * being interrupted after here trashes SRR{0,1} + * -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + mtmsr r0 /* Update machine state */ + lwz r0,_MSR(r1) mtspr SRR0,r2 mtspr SRR1,r0 @@ -271,7 +280,7 @@ ret_from_smpfork: bl schedule_tail b ret_from_except #endif - + .globl ret_from_intercept ret_from_intercept: /* @@ -291,7 +300,7 @@ ret_from_intercept: .globl ret_from_except ret_from_except: -0: /* disable interrupts */ +0: /* disable interrupts */ lis r30,int_control@h ori r30,r30,int_control@l lwz r30,0(r30) @@ -342,16 +351,26 @@ do_bottom_half_ret: .globl do_signal_ret do_signal_ret: b 0b -8: addi r4,r1,INT_FRAME_SIZE /* size of frame */ +8: /* + * We need to hard disable here even if RTL is active since + * being interrupted after here trashes the SPRG2 + * -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + mtmsr r0 /* Update machine state */ + + addi r4,r1,INT_FRAME_SIZE /* size of frame */ stw r4,THREAD+KSP(r2) /* save kernel stack pointer */ tophys(r3,r1) mtspr SPRG2,r3 /* phys exception stack pointer */ + b 11f 10: /* make sure we hard disable here, even if rtl is active -- Cort */ mfmsr r0 /* Get current interrupt state */ rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ sync /* Some chip revs have problems here... */ mtmsr r0 /* Update machine state */ - +11: lwz r2,_CTR(r1) lwz r0,_LINK(r1) mtctr r2 diff --git a/arch/ppc/kernel/feature.c b/arch/ppc/kernel/feature.c index a9a30396a..156eb187e 100644 --- a/arch/ppc/kernel/feature.c +++ b/arch/ppc/kernel/feature.c @@ -8,85 +8,144 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * BenH: Changed implementation to work on multiple registers + * polarity is also taken into account. Removed delay (now + * responsibility of the caller). Added spinlocks. + * */ +#include <linux/config.h> #include <linux/types.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <asm/errno.h> #include <asm/ohare.h> +#include <asm/heathrow.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/feature.h> -#define MAX_FEATURE_REGS 2 #undef DEBUG_FEATURE -static u32 feature_bits_pbook[] = { - 0, /* FEATURE_null */ - OH_SCC_RESET, /* FEATURE_Serial_reset */ - OH_SCC_ENABLE, /* FEATURE_Serial_enable */ - OH_SCCA_IO, /* FEATURE_Serial_IO_A */ - OH_SCCB_IO, /* FEATURE_Serial_IO_B */ - OH_FLOPPY_ENABLE, /* FEATURE_SWIM3_enable */ - OH_MESH_ENABLE, /* FEATURE_MESH_enable */ - OH_IDE_ENABLE, /* FEATURE_IDE_enable */ - OH_VIA_ENABLE, /* FEATURE_VIA_enable */ - OH_IDECD_POWER, /* FEATURE_CD_power */ - OH_BAY_RESET, /* FEATURE_Mediabay_reset */ - OH_BAY_ENABLE, /* FEATURE_Mediabay_enable */ - OH_BAY_PCI_ENABLE, /* FEATURE_Mediabay_PCI_enable */ - OH_BAY_IDE_ENABLE, /* FEATURE_Mediabay_IDE_enable */ - OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ - 0, /* FEATURE_BMac_reset */ - 0, /* FEATURE_BMac_IO_enable */ - 0, /* FEATURE_Modem_Reset -> guess... */ - OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ - OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ +#define MAX_FEATURE_CONTROLLERS 2 +#define MAX_FEATURE_OFFSET 0x50 +#define FREG(c,r) (&(((c)->reg)[(r)>>2])) + +typedef struct feature_bit { + int reg; /* reg. offset from mac-io base */ + unsigned int polarity; /* 0 = normal, 1 = inverse */ + unsigned int mask; /* bit mask */ +} fbit; + +/* I don't have an OHare machine to test with, so I left those as they + * were. Someone with such a machine chould check out what OF says and + * try too see if they match the heathrow ones and should be changed too + */ +static fbit feature_bits_ohare_pbook[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,OH_SCC_RESET}, /* FEATURE_Serial_reset */ + {0x38,0,OH_SCC_ENABLE}, /* FEATURE_Serial_enable */ + {0x38,0,OH_SCCA_IO}, /* FEATURE_Serial_IO_A */ + {0x38,0,OH_SCCB_IO}, /* FEATURE_Serial_IO_B */ + {0x38,0,OH_FLOPPY_ENABLE}, /* FEATURE_SWIM3_enable */ + {0x38,0,OH_MESH_ENABLE}, /* FEATURE_MESH_enable */ + {0x38,0,OH_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ + {0x38,1,OH_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ + {0x38,0,OH_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ + {0x38,1,OH_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ + {0x38,1,OH_BAY_POWER_N}, /* FEATURE_Mediabay_power */ + {0x38,0,OH_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,OH_BAY_IDE_ENABLE}, /* FEATURE_Mediabay_IDE_enable */ + {0x38,1,OH_IDE1_RESET_N}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,OH_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,0}, /* FEATURE_BMac_reset */ + {0x38,0,0}, /* FEATURE_BMac_IO_enable */ + {0x38,0,0}, /* FEATURE_Modem_power */ + {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,0,0}, /* FEATURE_Sound_Power */ + {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x38,0,0}, /* FEATURE_IDE2_reset */ }; -/* assume these are the same as the ohare until proven otherwise */ -static u32 feature_bits_heathrow[] = { - 0, /* FEATURE_null */ - OH_SCC_RESET, /* FEATURE_Serial_reset */ - OH_SCC_ENABLE, /* FEATURE_Serial_enable */ - OH_SCCA_IO, /* FEATURE_Serial_IO_A */ - OH_SCCB_IO, /* FEATURE_Serial_IO_B */ - OH_FLOPPY_ENABLE, /* FEATURE_SWIM3_enable */ - OH_MESH_ENABLE, /* FEATURE_MESH_enable */ - OH_IDE_ENABLE, /* FEATURE_IDE_enable */ - OH_VIA_ENABLE, /* FEATURE_VIA_enable */ - OH_IDECD_POWER, /* FEATURE_CD_power */ - OH_BAY_RESET, /* FEATURE_Mediabay_reset */ - OH_BAY_ENABLE, /* FEATURE_Mediabay_enable */ - OH_BAY_PCI_ENABLE, /* FEATURE_Mediabay_PCI_enable */ - OH_BAY_IDE_ENABLE, /* FEATURE_Mediabay_IDE_enable */ - OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ - 0x80000000, /* FEATURE_BMac_reset */ - 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000, /* FEATURE_Modem_Reset -> guess...*/ - OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ - OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ +/* Those bits are from a PowerBook. It's possible that desktop machines + * based on heathrow need a different definition or some bits removed + */ +static fbit feature_bits_heathrow[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,HRW_RESET_SCC}, /* FEATURE_Serial_reset */ + {0x38,0,HRW_SCC_ENABLE}, /* FEATURE_Serial_enable */ + {0x38,0,HRW_SCCA_IO}, /* FEATURE_Serial_IO_A */ + {0x38,0,HRW_SCCB_IO}, /* FEATURE_Serial_IO_B */ + {0x38,0,HRW_SWIM_ENABLE}, /* FEATURE_SWIM3_enable */ + {0x38,0,HRW_MESH_ENABLE}, /* FEATURE_MESH_enable */ + {0x38,0,HRW_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ + {0x38,1,HRW_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ + {0x38,0,HRW_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ + {0x38,1,HRW_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ + {0x38,1,HRW_BAY_POWER_N}, /* FEATURE_Mediabay_power */ + {0x38,0,HRW_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,HRW_BAY_IDE_ENABLE}, /* FEATURE_Mediabay_IDE_enable */ + {0x38,1,HRW_IDE1_RESET_N}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,HRW_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,HRW_BMAC_RESET}, /* FEATURE_BMac_reset */ + {0x38,0,HRW_BMAC_IO_ENABLE}, /* FEATURE_BMac_IO_enable */ + {0x38,1,HRW_MODEM_POWER_N}, /* FEATURE_Modem_power */ + {0x38,0,HRW_SLOW_SCC_PCLK}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,1,HRW_SOUND_POWER_N}, /* FEATURE_Sound_Power */ + {0x38,0,HRW_SOUND_CLK_ENABLE}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x38,0,0}, /* FEATURE_IDE2_reset */ +}; + +/* Those bits are from an iBook. + */ +static fbit feature_bits_keylargo[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,0}, /* FEATURE_Serial_reset */ + {0x38,0,0x00000054}, /* FEATURE_Serial_enable */ + {0x38,0,0}, /* FEATURE_Serial_IO_A */ + {0x38,0,0}, /* FEATURE_Serial_IO_B */ + {0x38,0,0}, /* FEATURE_SWIM3_enable */ + {0x38,0,0}, /* FEATURE_MESH_enable */ + {0x38,0,0}, /* FEATURE_IDE0_enable */ + {0x3c,1,0x01000000}, /* FEATURE_IDE0_reset */ + {0x38,0,0}, /* FEATURE_IOBUS_enable */ + {0x38,0,0}, /* FEATURE_Mediabay_reset */ + {0x38,0,0}, /* FEATURE_Mediabay_power */ + {0x38,0,0}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,0}, /* FEATURE_Mediabay_IDE_enable */ + {0x3c,1,0x08000000}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,0}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,0}, /* FEATURE_BMac_reset */ + {0x38,0,0}, /* FEATURE_BMac_IO_enable */ + {0x40,1,0x02000000}, /* FEATURE_Modem_power */ + {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,0,0}, /* FEATURE_Sound_Power */ + {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x3c,1,0x40000000}, /* FEATURE_IDE2_reset */ }; /* definition of a feature controller object */ -struct feature_controller -{ - u32* bits; +struct feature_controller { + fbit* bits; volatile u32* reg; struct device_node* device; + spinlock_t lock; }; /* static functions */ static void -feature_add_controller(struct device_node *controller_device, u32* bits); +feature_add_controller(struct device_node *controller_device, fbit* bits); -static int +static struct feature_controller* feature_lookup_controller(struct device_node *device); /* static varialbles */ -static struct feature_controller controllers[MAX_FEATURE_REGS]; +static struct feature_controller controllers[MAX_FEATURE_CONTROLLERS]; static int controller_count = 0; @@ -96,18 +155,23 @@ feature_init(void) struct device_node *np; np = find_devices("mac-io"); - while (np != NULL) - { - feature_add_controller(np, feature_bits_heathrow); + while (np != NULL) { + /* KeyLargo contains several (5 ?) FCR registers in mac-io, + * plus some gpio's which could eventually be handled here. + */ + if (device_is_compatible(np, "Keylargo")) { + feature_add_controller(np, feature_bits_keylargo); + } else { + feature_add_controller(np, feature_bits_heathrow); + } np = np->next; } if (controller_count == 0) { np = find_devices("ohare"); - if (np) - { + if (np) { if (find_devices("via-pmu") != NULL) - feature_add_controller(np, feature_bits_pbook); + feature_add_controller(np, feature_bits_ohare_pbook); else /* else not sure; maybe this is a Starmax? */ feature_add_controller(np, NULL); @@ -116,17 +180,26 @@ feature_init(void) if (controller_count) printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count); + +#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_DMASOUND_MODULE + /* On PowerBooks, we disable the sound chip when dmasound is a module */ + if (controller_count && find_devices("via-pmu") != NULL) { + feature_clear(controllers[0].device, FEATURE_Sound_power); + feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable); + } +#endif +#endif } static void -feature_add_controller(struct device_node *controller_device, u32* bits) +feature_add_controller(struct device_node *controller_device, fbit* bits) { struct feature_controller* controller; - if (controller_count >= MAX_FEATURE_REGS) - { + if (controller_count >= MAX_FEATURE_CONTROLLERS) { printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n", - controller_device->full_name, MAX_FEATURE_REGS); + controller_device->full_name, MAX_FEATURE_CONTROLLERS); return; } controller = &controllers[controller_count]; @@ -140,30 +213,32 @@ feature_add_controller(struct device_node *controller_device, u32* bits) } controller->reg = (volatile u32 *)ioremap( - controller_device->addrs[0].address + OHARE_FEATURE_REG, 4); + controller_device->addrs[0].address, MAX_FEATURE_OFFSET); if (bits == NULL) { printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(controller->reg, STARMAX_FEATURES); + out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES); return; } + spin_lock_init(&controller->lock); + controller_count++; } -static int +static struct feature_controller* feature_lookup_controller(struct device_node *device) { int i; if (device == NULL) - return -EINVAL; + return NULL; while(device) { for (i=0; i<controller_count; i++) if (device == controllers[i].device) - return i; + return &controllers[i]; device = device->parent; } @@ -172,35 +247,36 @@ feature_lookup_controller(struct device_node *device) device->name); #endif - return -ENODEV; + return NULL; } int feature_set(struct device_node* device, enum system_feature f) { - int controller; - unsigned long flags; + struct feature_controller* controller; + unsigned long flags; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; #ifdef DEBUG_FEATURE printk("feature: <%s> setting feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controllers[controller].reg); + device->name, (int)f, (unsigned int)controller->reg); #endif - save_flags(flags); - cli(); - out_le32( controllers[controller].reg, - in_le32(controllers[controller].reg) | - controllers[controller].bits[f]); - (void)in_le32(controllers[controller].reg); - restore_flags(flags); - udelay(10); + spin_lock_irqsave(&controller->lock, flags); + value = in_le32(FREG(controller, bit->reg)); + value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask); + out_le32(FREG(controller, bit->reg), value); + (void)in_le32(FREG(controller, bit->reg)); + spin_unlock_irqrestore(&controller->lock, flags); return 0; } @@ -208,29 +284,30 @@ feature_set(struct device_node* device, enum system_feature f) int feature_clear(struct device_node* device, enum system_feature f) { - int controller; - unsigned long flags; + struct feature_controller* controller; + unsigned long flags; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; #ifdef DEBUG_FEATURE printk("feature: <%s> clearing feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controllers[controller].reg); + device->name, (int)f, (unsigned int)controller->reg); #endif - save_flags(flags); - cli(); - out_le32( controllers[controller].reg, - in_le32(controllers[controller].reg) & - ~(controllers[controller].bits[f])); - (void)in_le32(controllers[controller].reg); - restore_flags(flags); - udelay(10); + spin_lock_irqsave(&controller->lock, flags); + value = in_le32(FREG(controller, bit->reg)); + value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask); + out_le32(FREG(controller, bit->reg), value); + (void)in_le32(FREG(controller, bit->reg)); + spin_unlock_irqrestore(&controller->lock, flags); return 0; } @@ -238,16 +315,27 @@ feature_clear(struct device_node* device, enum system_feature f) int feature_test(struct device_node* device, enum system_feature f) { - int controller; + struct feature_controller* controller; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; - return (in_le32(controllers[controller].reg) & - controllers[controller].bits[f]) != 0; +#ifdef DEBUG_FEATURE + printk("feature: <%s> clearing feature %d in controller @0x%x\n", + device->name, (int)f, (unsigned int)controller->reg); +#endif + /* If one feature contains several bits, all of them must be set + * for value to be true, or all of them must be 0 if polarity is + * inverse + */ + value = (in_le32(FREG(controller, bit->reg)) & bit->mask); + return bit->polarity ? (value == 0) : (value == bit->mask); } diff --git a/arch/ppc/kernel/galaxy_pci.c b/arch/ppc/kernel/galaxy_pci.c new file mode 100644 index 000000000..aeddd9a0e --- /dev/null +++ b/arch/ppc/kernel/galaxy_pci.c @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2000 Grant Erickson <grant@borg.umn.edu> + * All rights reserved. + * + * Module name: galaxy_pci.c + * + * Description: + * PCI interface code for the IBM PowerPC 405GP on-chip PCI bus + * interface. + * + * Why is this file called "galaxy_pci"? Because on the original + * IBM "Walnut" evaluation board schematic I have, the 405GP is + * is labeled "GALAXY". + * + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/machdep.h> + +#include "pci.h" + + +/* Preprocessor Defines */ + +#define PCICFGADDR (volatile unsigned int *)(0xEEC00000) +#define PCICFGDATA (volatile unsigned int *)(0xEEC00004) + + +/* Function Prototypes */ + +decl_config_access_method(galaxy); + + +void __init +galaxy_pcibios_fixup(void) +{ + +} + +void __init +galaxy_setup_pci_ptrs(void) +{ + set_config_access_method(galaxy); + + ppc_md.pcibios_fixup = galaxy_pcibios_fixup; +} + +int +galaxy_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + + return (PCIBIOS_SUCCESSFUL); +} diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c index d7ca91780..fcf3a701c 100644 --- a/arch/ppc/kernel/gemini_setup.c +++ b/arch/ppc/kernel/gemini_setup.c @@ -53,7 +53,7 @@ static unsigned int cpu_6xx[16] = { }; int chrp_get_irq(struct pt_regs *); -void chrp_post_irq(int); +void chrp_post_irq(struct pt_regs* regs, int); static inline unsigned long _get_HID1(void) { @@ -132,6 +132,19 @@ extern unsigned long loops_per_sec; extern int root_mountflags; extern char cmd_line[]; +void +gemini_heartbeat(void) +{ + static unsigned long led = GEMINI_LEDBASE+(4*8); + static char direction = 8; + *(char *)led = 0; + if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || + (led + direction) < (GEMINI_LEDBASE+(4*8)) ) + direction *= -1; + led += direction; + *(char *)led = 0xff; + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} void __init gemini_setup_arch(void) { @@ -175,6 +188,10 @@ void __init gemini_setup_arch(void) printk("CPU manufacturer: %s [rev=%04x]\n", (cpu & (1<<15)) ? "IBM" : "Motorola", (cpu & 0xffff)); + ppc_md.heartbeat = gemini_heartbeat; + ppc_md.heartbeat_reset = HZ/8; + ppc_md.heartbeat_count = 1; + /* take special pains to map the MPIC, since it isn't mapped yet */ gemini_openpic_init(); /* start the L2 */ @@ -505,7 +522,7 @@ int gemini_get_irq( struct pt_regs *regs ) return irq; } -void gemini_post_irq(int irq) +void gemini_post_irq(struct pt_regs* regs, int irq) { /* * If it's an i8259 irq then we've already done the diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index a70ba8bfd..8b56c635c 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -156,6 +156,16 @@ __start: bl fix_mem_constants #endif /* CONFIG_APUS */ +#ifndef CONFIG_GEMINI +/* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains + * the physical address we are running at, returned by prom_init() + */ +__after_prom_start: + bl mmu_off + bl clear_bats + bl flush_tlbs +#endif + /* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely @@ -211,6 +221,11 @@ __start: mtspr DBAT0U,r11 /* bit in upper BAT register */ mtspr IBAT0L,r8 mtspr IBAT0U,r11 +#if 0 /* Useful debug code, please leave in for now so I don't have to + * look at docs when I need to setup a BAT ; + */ + bl setup_screen_bat +#endif 5: isync #ifndef CONFIG_APUS @@ -627,12 +642,8 @@ DataStoreTLBMiss: mtcrf 0x80,r3 rfi -/* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) - -/* System management exception (603?) */ - STD_EXCEPTION(0x1400, Trap_14, UnknownException) - + STD_EXCEPTION(0x1400, SMI, SMIException) STD_EXCEPTION(0x1500, Trap_15, UnknownException) STD_EXCEPTION(0x1600, Trap_16, UnknownException) STD_EXCEPTION(0x1700, Trap_17, TAUException) @@ -644,10 +655,7 @@ DataStoreTLBMiss: STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) - - /* Run mode exception */ STD_EXCEPTION(0x2000, RunMode, RunModeException) - STD_EXCEPTION(0x2100, Trap_21, UnknownException) STD_EXCEPTION(0x2200, Trap_22, UnknownException) STD_EXCEPTION(0x2300, Trap_23, UnknownException) @@ -911,12 +919,16 @@ giveup_fpu: * the kernel image to physical address 0. */ relocate_kernel: +#if 0 /* Is this still needed ? I don't think so. It breaks new + * boot-with-mmu-off stuff + */ lis r9,0x426f /* if booted from BootX, don't */ addi r9,r9,0x6f58 /* translate source addr */ cmpw r31,r9 /* (we have to on chrp) */ beq 7f rlwinm r4,r4,0,8,31 /* translate source address */ add r4,r4,r3 /* to region mapped with BATs */ +#endif 7: addis r9,r26,klimit@ha /* fetch klimit */ lwz r25,klimit@l(r9) addis r25,r25,-KERNELBASE@h @@ -1194,14 +1206,26 @@ enable_caches: cmpi 0,r9,4 /* check for 604 */ cmpi 1,r9,9 /* or 604e */ cmpi 2,r9,10 /* or mach5 */ + cmpi 3,r9,8 /* check for 750 (G3) */ + cmpi 4,r9,12 /* or 7400 (G4) */ cror 2,2,6 cror 2,2,10 bne 4f ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ bne 2,5f ori r11,r11,HID0_BTCD + b 5f +4: + cror 14,14,18 + bne 3,6f + /* We should add ABE here if we want to use Store Gathering + * and other nifty bridge features + */ + ori r11,r11,HID0_SGE|HID0_BHTE|HID0_BTIC /* for g3/g4, enable */ + li r3,0 + mtspr ICTC,r3 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ -4: blr +6: blr /* * Load stuff into the MMU. Intended to be called with @@ -1388,6 +1412,45 @@ clear_bats: #endif /* !defined(CONFIG_GEMINI) */ blr +#ifndef CONFIG_GEMINI +flush_tlbs: + lis r20, 0x1000 +1: addic. r20, r20, -0x1000 + tlbie r20 + blt 1b + sync + blr + +mmu_off: + addi r4, r3, __after_prom_start - _start + mfmsr r3 + andi. r0,r3,MSR_DR|MSR_IR /* MMU enabled? */ + beq 1f + ori r3,r3,MSR_DR|MSR_IR + xori r3,r3,MSR_DR|MSR_IR + mtspr SRR0,r4 + mtspr SRR1,r3 + sync + rfi +1: blr +#endif + +#if 0 /* That's useful debug stuff */ +setup_screen_bat: + lis r3, 0x9100 +#ifdef __SMP__ + ori r3,r3,0x12 +#else + ori r3,r3,0x2 +#endif + mtspr DBAT1L, r3 + mtspr IBAT1L, r3 + ori r3,r3,(BL_8M<<2)|0x2 /* set up BAT registers for 604 */ + mtspr DBAT1U, r3 + mtspr IBAT1U, r3 + blr +#endif + /* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the data segment, diff --git a/arch/ppc/kernel/head_4xx.S b/arch/ppc/kernel/head_4xx.S index ba3284ad8..bec805b92 100644 --- a/arch/ppc/kernel/head_4xx.S +++ b/arch/ppc/kernel/head_4xx.S @@ -78,13 +78,17 @@ _GLOBAL(_start) li r24,0 + ## Invalidate all TLB entries + + tlbia + ## We should still be executing code at physical address 0x0000xxxx ## at this point. However, start_here is at virtual address ## 0xC000xxxx. So, set up a TLB mapping to cover this once ## translation is enabled. lis r3,KERNELBASE@h # Load the kernel virtual address - addis r3,r3,KERNELBASE@l + ori r3,r3,KERNELBASE@l tophys(r4,r3) # Load the kernel physical address ## Save the existing PID and load the kernel PID. @@ -96,11 +100,7 @@ _GLOBAL(_start) ## Configure and load entry into TLB slot 0. clrrwi r4,r4,10 # Mask off the real page number - - ## XXX - Temporarily set the TLB_I bit because of cache issues that - ## seem to foul-up the exception handling code. - - ori r4,r4,(TLB_WR | TLB_EX | TLB_I) # Set the write and execute bits + ori r4,r4,(TLB_WR | TLB_EX) # Set the write and execute bits clrrwi r3,r3,10 # Mask off the effective page number ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) @@ -333,22 +333,12 @@ _GLOBAL(timer_interrupt_intercept) #endif ### 0x1100 - Data TLB Miss Exception - - START_EXCEPTION(0x1100, DTLBMiss) - STND_EXCEPTION_PROLOG - addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC - li r20,MSR_KERNEL - FINISH_EXCEPTION(UnknownException) + + STND_EXCEPTION(0x1100, DTLBMiss, PPC4xx_dtlb_miss) ### 0x1200 - Instruction TLB Miss Exception - - START_EXCEPTION(0x1200, ITLBMiss) - STND_EXCEPTION_PROLOG - addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC - li r20,MSR_KERNEL - FINISH_EXCEPTION(UnknownException) + + STND_EXCEPTION(0x1200, ITLBMiss, PPC4xx_itlb_miss) STND_EXCEPTION(0x1300, Trap_13, UnknownException) STND_EXCEPTION(0x1400, Trap_14, UnknownException) @@ -560,8 +550,6 @@ start_here: _GLOBAL(set_context) mtspr SPRN_PID,r3 - tlbia - SYNC blr ### diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 8dda6ad6b..7cf97b873 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -154,11 +154,12 @@ unsigned long get_zero_page_fast(void) if ( zero_quicklist ) { /* atomically remove this page from the list */ - asm ( "101:lwarx %1,0,%2\n" /* reserve zero_cache */ + register unsigned long tmp; + asm ( "101:lwarx %1,0,%3\n" /* reserve zero_cache */ " lwz %0,0(%1)\n" /* get next -- new zero_cache */ - " stwcx. %0,0,%2\n" /* update zero_cache */ + " stwcx. %0,0,%3\n" /* update zero_cache */ " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_quicklist), "=&r" (page) + : "=&r" (tmp), "=&r" (page), "+m" (zero_cache) : "r" (&zero_quicklist) : "cc" ); #ifdef __SMP__ @@ -193,6 +194,7 @@ void zero_paged(void) { unsigned long pageptr = 0; /* current page being zero'd */ unsigned long bytecount = 0; + register unsigned long tmp; pte_t *pte; if ( atomic_read(&zero_cache_sz) >= zero_cache_water[0] ) @@ -249,15 +251,14 @@ void zero_paged(void) pte_cache(*pte); flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); /* atomically add this page to the list */ - asm ( "101:lwarx %0,0,%1\n" /* reserve zero_cache */ - " stw %0,0(%2)\n" /* update *pageptr */ + asm ( "101:lwarx %0,0,%2\n" /* reserve zero_cache */ + " stw %0,0(%3)\n" /* update *pageptr */ #ifdef __SMP__ " sync\n" /* let store settle */ #endif - " mr %0,%2\n" /* update zero_cache in reg */ - " stwcx. %2,0,%1\n" /* update zero_cache in mem */ + " stwcx. %3,0,%2\n" /* update zero_cache in mem */ " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_quicklist) + : "=&r" (tmp), "+m" (zero_quicklist) : "r" (&zero_quicklist), "r" (pageptr) : "cc" ); /* diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index c2f2d1c11..8b5f590fb 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -126,7 +126,7 @@ void irq_kfree(void *ptr) */ int request_8xxirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #elif defined(CONFIG_APUS) -int sys_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), +int request_sysirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #else int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #endif @@ -315,7 +315,7 @@ asmlinkage int do_IRQ(struct pt_regs *regs, int isfake) } ppc_irq_dispatch_handler( regs, irq ); if ( ppc_md.post_irq ) - ppc_md.post_irq( irq ); + ppc_md.post_irq( regs, irq ); out: hardirq_exit( cpu ); diff --git a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h index 602192013..840b14d6f 100644 --- a/arch/ppc/kernel/local_irq.h +++ b/arch/ppc/kernel/local_irq.h @@ -4,6 +4,8 @@ #include <linux/kernel_stat.h> #include <linux/interrupt.h> +#include <linux/cache.h> +#include <linux/spinlock.h> #include <linux/irq.h> void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index aa9f5ac83..fde7112c7 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -20,13 +20,13 @@ #include <asm/page.h> #include "ppc_asm.h" -#ifndef CONFIG_8xx -CACHE_LINE_SIZE = 32 -LG_CACHE_LINE_SIZE = 5 -#else +#if defined(CONFIG_4xx) || defined(CONFIG_8xx) CACHE_LINE_SIZE = 16 LG_CACHE_LINE_SIZE = 4 -#endif /* CONFIG_8xx */ +#else +CACHE_LINE_SIZE = 32 +LG_CACHE_LINE_SIZE = 5 +#endif /* CONFIG_4xx || CONFIG_8xx */ .text @@ -590,6 +590,20 @@ _GLOBAL(_set_THRM3) _GLOBAL(_get_PVR) mfspr r3,PVR blr + +_GLOBAL(_get_HID0) + mfspr r3,HID0 + blr + +_GLOBAL(_get_ICTC) + mfspr r3,ICTC + blr + +_GLOBAL(_set_ICTC) + mtspr ICTC,r3 + blr + + /* L2CR functions Copyright © 1997-1998 by PowerLogix R & D, Inc. @@ -656,6 +670,8 @@ _GLOBAL(_set_L2CR) rlwinm r4,r4,16,16,31 cmplwi r4,0x0008 beq thisIs750 + cmplwi r4,0x000c + beq thisIs750 li r3,-1 blr @@ -750,9 +766,11 @@ _GLOBAL(_get_L2CR) mfspr r3,PVR rlwinm r3,r3,16,16,31 cmplwi r3,0x0008 + beq 1f + cmplwi r3,0x000c li r3,0 bnelr - +1: /* Return the L2CR contents */ mfspr r3,L2CR blr diff --git a/arch/ppc/kernel/oak_setup.c b/arch/ppc/kernel/oak_setup.c index ad2c224bb..f3c142e2b 100644 --- a/arch/ppc/kernel/oak_setup.c +++ b/arch/ppc/kernel/oak_setup.c @@ -1,6 +1,6 @@ /* * - * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> * * Module name: oak_setup.c * @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/param.h> #include <linux/string.h> +#include <linux/blk.h> #include <asm/processor.h> #include <asm/board.h> @@ -30,6 +31,12 @@ #include "time.h" #include "oak_setup.h" + + + + + + /* Function Prototypes */ extern void abort(void); @@ -95,32 +102,32 @@ oak_init(unsigned long r3, unsigned long r4, unsigned long r5, /* Initialize machine-dependency vectors */ - ppc_md.setup_arch = oak_setup_arch; - ppc_md.setup_residual = oak_setup_residual; - ppc_md.get_cpuinfo = NULL; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = oak_init_IRQ; - ppc_md.get_irq = oak_get_irq; - ppc_md.init = NULL; - - ppc_md.restart = oak_restart; - ppc_md.power_off = oak_power_off; - ppc_md.halt = oak_halt; - - ppc_md.time_init = oak_time_init; - ppc_md.set_rtc_time = oak_set_rtc_time; - ppc_md.get_rtc_time = oak_get_rtc_time; - ppc_md.calibrate_decr = oak_calibrate_decr; - - ppc_md.kbd_setkeycode = NULL; - ppc_md.kbd_getkeycode = NULL; - ppc_md.kbd_translate = NULL; - ppc_md.kbd_unexpected_up = NULL; - ppc_md.kbd_leds = NULL; - ppc_md.kbd_init_hw = NULL; + ppc_md.setup_arch = oak_setup_arch; + ppc_md.setup_residual = oak_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = oak_init_IRQ; + ppc_md.get_irq = oak_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = oak_restart; + ppc_md.power_off = oak_power_off; + ppc_md.halt = oak_halt; + + ppc_md.time_init = oak_time_init; + ppc_md.set_rtc_time = oak_set_rtc_time; + ppc_md.get_rtc_time = oak_get_rtc_time; + ppc_md.calibrate_decr = oak_calibrate_decr; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; #if defined(CONFIG_MAGIC_SYSRQ) - ppc_md.kbd_sysrq_xlate = NULL; + ppc_md.ppc_kbd_sysrq_xlate = NULL; #endif return; diff --git a/arch/ppc/kernel/oak_setup.h b/arch/ppc/kernel/oak_setup.h index 62cfac906..8648bd084 100644 --- a/arch/ppc/kernel/oak_setup.h +++ b/arch/ppc/kernel/oak_setup.h @@ -1,14 +1,14 @@ /* * - * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> * - * Module name: oak_setup.c + * Module name: oak_setup.h * * Description: * Architecture- / platform-specific boot-time initialization code for * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original * code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek - * <dmalek@jlc.net>. + * <dan@netx4.com>. * */ diff --git a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c index 6857aa36f..d4dbe05e5 100644 --- a/arch/ppc/kernel/open_pic.c +++ b/arch/ppc/kernel/open_pic.c @@ -8,6 +8,7 @@ * for more details. */ +#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -17,17 +18,30 @@ #include <asm/signal.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/prom.h> #include "local_irq.h" volatile struct OpenPIC *OpenPIC = NULL; u_int OpenPIC_NumInitSenses __initdata = 0; u_char *OpenPIC_InitSenses __initdata = NULL; +int open_pic_irq_offset; +extern int use_of_interrupt_tree; void chrp_mask_irq(unsigned int); void chrp_unmask_irq(unsigned int); +void find_ISUs(void); static u_int NumProcessors; static u_int NumSources; +OpenPIC_Source *ISU; +/* + * We should use this if we have > 1 ISU. + * We can just point each entry to the + * appropriate source regs but it wastes a lot of space + * so until we have >1 ISU I'll leave it unimplemented. + * -- Cort +OpenPIC_Source ISU[128]; +*/ struct hw_interrupt_type open_pic = { " OpenPIC ", @@ -38,7 +52,6 @@ struct hw_interrupt_type open_pic = { 0, 0 }; -int open_pic_irq_offset; /* * Accesses to the current processor's registers @@ -96,7 +109,7 @@ void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) #endif /* __SMP__ */ #ifdef __i386__ -static inline u_int ld_le32(volatile u_int *addr) +static inline u_int in_le32(volatile u_int *addr) { return *addr; } @@ -111,7 +124,7 @@ u_int openpic_read(volatile u_int *addr) { u_int val; - val = ld_le32(addr); + val = in_le32(addr); return val; } @@ -148,6 +161,9 @@ static void openpic_safe_writefield(volatile u_int *addr, u_int mask, { openpic_setfield(addr, OPENPIC_MASK); /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ while (openpic_read(addr) & OPENPIC_ACTIVITY); openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); } @@ -182,16 +198,18 @@ void __init openpic_init(int main_pic) OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; - - printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, - NumProcessors, NumSources, OpenPIC); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); - printk("OpenPIC timer frequency is "); - if (timerfreq) - printk("%d Hz\n", timerfreq); - else - printk("not set\n"); - + if ( _machine != _MACH_Pmac ) + { + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, + NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (timerfreq) + printk("%d MHz\n", timerfreq>>20); + else + printk("not set\n"); + } + if ( main_pic ) { /* Initialize timer interrupts */ @@ -209,24 +227,59 @@ void __init openpic_init(int main_pic) /* Disabled, Priority 8 */ openpic_initipi(i, 8, OPENPIC_VEC_IPI+i); } - - /* Initialize external interrupts */ - if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); - /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, open_pic_irq_offset, 1, 1); - openpic_mapirq(0, 1<<0); - for (i = 1; i < NumSources; i++) { - /* Enabled, Priority 8 */ - openpic_initirq(i, 8, open_pic_irq_offset+i, 0, - i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); - /* Processor 0 */ - openpic_mapirq(i, 1<<0); + find_ISUs(); + if ( _machine != _MACH_Pmac ) + { + /* Initialize external interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, open_pic_irq_offset, 1, 1); + openpic_mapirq(0, 1<<0); + for (i = 1; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, open_pic_irq_offset+i, 0, + i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } } - + else + { + /* Prevent any interrupt from occuring during initialisation. + * Hum... I believe this is not necessary, Apple does that in + * Darwin's PowerExpress code. + */ + openpic_set_priority(0, 0xf); + + /* First disable all interrupts and map them to CPU 0 */ + for (i = 0; i < NumSources; i++) { + openpic_disable_irq(i); + openpic_mapirq(i, 1<<0); + } + + /* If we use the device tree, then lookup all interrupts and + * initialize them according to sense infos found in the tree + */ + if (use_of_interrupt_tree) { + struct device_node* np = find_all_nodes(); + while(np) { + int j, pri; + pri = strcmp(np->name, "programmer-switch") ? 2 : 7; + for (j=0;j<np->n_intrs;j++) + openpic_initirq( np->intrs[j].line, + pri, + np->intrs[j].line, + np->intrs[j].sense, + np->intrs[j].sense); + np = np->next; + } + } + } + /* Initialize the spurious interrupt */ if ( ppc_md.progress ) ppc_md.progress("openpic spurious",0x3bd); openpic_set_spurious(OPENPIC_VEC_SPURIOUS); - if ( _machine != _MACH_gemini ) + if ( !(_machine && (_MACH_gemini|_MACH_Pmac)) ) { if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, "82c59 cascade", NULL)) @@ -238,6 +291,20 @@ void __init openpic_init(int main_pic) if ( ppc_md.progress ) ppc_md.progress("openpic exit",0x222); } +void find_ISUs(void) +{ +#ifdef CONFIG_PPC64 + /* hardcode this for now since the IBM 260 is the only thing with + * a distributed openpic right now. -- Cort + */ + ISU = (OpenPIC_Source *)0xfeff7c00; + NumSources = 0x10; +#else + /* for non-distributed OpenPIC implementations it's in the IDU -- Cort */ + ISU = OpenPIC->Source; +#endif +} + void openpic_reset(void) { openpic_setfield(&OpenPIC->Global.Global_Configuration0, @@ -279,6 +346,8 @@ void openpic_eoi(u_int cpu) { check_arg_cpu(cpu); openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); } @@ -379,7 +448,7 @@ void do_openpic_setup_cpu(void) #if 0 /* let the openpic know we want intrs */ for ( i = 0; i < NumSources ; i++ ) - openpic_mapirq(i, openpic_read(&OpenPIC->Source[i].Destination) + openpic_mapirq(i, openpic_read(ISU[i].Destination) | (1<<smp_processor_id()) ); #endif openpic_set_priority(smp_processor_id(), 0); @@ -417,13 +486,23 @@ void openpic_maptimer(u_int timer, u_int cpumask) void openpic_enable_irq(u_int irq) { check_arg_irq(irq); - openpic_clearfield(&OpenPIC->Source[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + openpic_clearfield(&ISU[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while(openpic_readfield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_MASK)); } void openpic_disable_irq(u_int irq) { check_arg_irq(irq); - openpic_setfield(&OpenPIC->Source[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + openpic_setfield(&ISU[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while(!openpic_readfield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_MASK)); } /* @@ -440,12 +519,13 @@ void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) check_arg_irq(irq); check_arg_pri(pri); check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + openpic_safe_writefield(&ISU[irq].Vector_Priority, OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | - OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec | - (pol ? OPENPIC_SENSE_POLARITY : 0) | - (sense ? OPENPIC_SENSE_LEVEL : 0)); + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); } /* @@ -454,7 +534,7 @@ void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) void openpic_mapirq(u_int irq, u_int cpumask) { check_arg_irq(irq); - openpic_write(&OpenPIC->Source[irq].Destination, cpumask); + openpic_write(&ISU[irq].Destination, cpumask); } /* @@ -465,7 +545,7 @@ void openpic_mapirq(u_int irq, u_int cpumask) void openpic_set_sense(u_int irq, int sense) { check_arg_irq(irq); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + openpic_safe_writefield(&ISU[irq].Vector_Priority, OPENPIC_SENSE_LEVEL, (sense ? OPENPIC_SENSE_LEVEL : 0)); } diff --git a/arch/ppc/kernel/pci-dma.c b/arch/ppc/kernel/pci-dma.c new file mode 100644 index 000000000..089566908 --- /dev/null +++ b/arch/ppc/kernel/pci-dma.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> + * + * + * Dynamic DMA mapping support. + * + * swiped from i386 + * + */ + +#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; + + if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, __get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, __get_order(size)); +} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 6c98bbf2c..8326bc369 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -71,9 +71,9 @@ void __init pcibios_init(void) { printk("PCI: Probing PCI hardware\n"); pci_scan_bus(0, &generic_pci_ops, NULL); - pcibios_claim_resources(&pci_root_buses); if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); + pcibios_claim_resources(&pci_root_buses); } void __init diff --git a/arch/ppc/kernel/pmac_nvram.c b/arch/ppc/kernel/pmac_nvram.c index ea3338aef..a3a9bae70 100644 --- a/arch/ppc/kernel/pmac_nvram.c +++ b/arch/ppc/kernel/pmac_nvram.c @@ -6,6 +6,7 @@ #include <linux/stddef.h> #include <linux/nvram.h> #include <linux/init.h> +#include <linux/slab.h> #include <asm/init.h> #include <asm/io.h> #include <asm/system.h> @@ -20,23 +21,37 @@ static int nvram_naddrs; static volatile unsigned char *nvram_addr; static volatile unsigned char *nvram_data; -static int nvram_mult; - -#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ - +static int nvram_mult, is_core_99; +static char* nvram_image; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + __init void pmac_nvram_init(void) { struct device_node *dp; + nvram_naddrs = 0; + dp = find_devices("nvram"); if (dp == NULL) { printk(KERN_ERR "Can't find NVRAM device\n"); - nvram_naddrs = 0; return; } nvram_naddrs = dp->n_addrs; - if (_machine == _MACH_chrp && nvram_naddrs == 1) { + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) + { + int i; + if (nvram_naddrs < 1) + return; + nvram_image = kmalloc(dp->addrs[0].size, GFP_KERNEL); + if (!nvram_image) + return; + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + for (i=0; i<dp->addrs[0].size; i++) + nvram_image[i] = in_8(nvram_data + i); + } else if (_machine == _MACH_chrp && nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = 1; } else if (nvram_naddrs == 1) { @@ -69,6 +84,8 @@ unsigned char nvram_read_byte(int addr) return req.reply[1]; #endif case 1: + if (is_core_99) + return nvram_image[addr]; return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; case 2: *nvram_addr = addr >> 5; @@ -94,6 +111,10 @@ void nvram_write_byte(unsigned char val, int addr) break; #endif case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; break; case 2: diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 75f8097fd..b57d5aa28 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -30,6 +30,16 @@ struct bridge_data **bridges, *bridge_list; static int max_bus; +struct uninorth_data { + struct device_node* node; + volatile unsigned int* cfg_addr; + volatile unsigned int* cfg_data; +}; + +static struct uninorth_data uninorth_bridges[3]; +static int uninorth_count; +static int uninorth_default = -1; + static void add_bridges(struct device_node *dev); /* @@ -73,6 +83,159 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, return 0; } +/* This function only works for bus 0, uni-N uses a different mecanism for + * other busses (see below) + */ +#define UNI_N_CFA0(devfn, off) \ + ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ + | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned long)(off)) & 0xFCUL)) + +/* This one is for type 1 config accesses */ +#define UNI_N_CFA1(bus, devfn, off) \ + ((((unsigned long)(bus)) << 16) \ + |(((unsigned long)(devfn)) << 8) \ + |(((unsigned long)(off)) & 0xFCUL) \ + |1UL) + +/* We should really use RTAS here, unfortunately, it's not available with BootX. + * (one more reason for writing a beautiful OF booter). I'll do the RTAS stuff + * later, once I have something that works enough with BootX. + */ +__pmac static +unsigned int +uni_north_access_data(unsigned char bus, unsigned char dev_fn, + unsigned char offset) +{ + struct device_node *node, *bridge_node; + int bridge = uninorth_default; + unsigned int caddr; + + if (bus == 0) { + if (PCI_SLOT(dev_fn) < 11) { + return 0; + } + /* We look for the OF device corresponding to this bus/devfn pair. If we + * don't find it, we default to the external PCI */ + bridge_node = NULL; + node = find_pci_device_OFnode(bus, dev_fn & 0xf8); + if (node) { + /* note: we don't stop on the first occurence since we need to go + * up to the root bridge */ + do { + if (!strcmp(node->type, "pci")) + bridge_node = node; + node=node->parent; + } while (node); + } + if (bridge_node) { + int i; + for (i=0;i<uninorth_count;i++) + if (uninorth_bridges[i].node == bridge_node) { + bridge = i; + break; + } + } + caddr = UNI_N_CFA0(dev_fn, offset); + } else + caddr = UNI_N_CFA1(bus, dev_fn, offset); + + if (bridge == -1) { + printk(KERN_WARNING "pmac_pci: no default bridge !\n"); + return 0; + } + + /* Uninorth will return garbage if we don't read back the value ! */ + out_le32(uninorth_bridges[bridge].cfg_addr, caddr); + (void)in_le32(uninorth_bridges[bridge].cfg_addr); + /* Yes, offset is & 7, not & 3 ! */ + return (unsigned int)(uninorth_bridges[bridge].cfg_data) + (offset & 0x07); +} + +__pmac +int uni_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + unsigned int addr; + + *val = 0xff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_8((volatile unsigned char*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + unsigned int addr; + + *val = 0xffff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_le16((volatile unsigned short*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + unsigned int addr; + + *val = 0xffff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_le32((volatile unsigned int*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_8((volatile unsigned char *)addr, val); + (void)in_8((volatile unsigned char *)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le16((volatile unsigned short *)addr, val); + (void)in_le16((volatile unsigned short *)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32((volatile unsigned int *)addr, val); + (void)in_le32((volatile unsigned int *)addr); + return PCIBIOS_SUCCESSFUL; +} + __pmac int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) @@ -362,6 +525,21 @@ static void __init init_bandit(struct bridge_data *bp) bp->io_base); } +#define GRACKLE_STG_ENABLE 0x00000040 + +/* N.B. this is called before bridges is initialized, so we can't + use grackle_pcibios_{read,write}_config_dword. */ +static inline void grackle_set_stg(struct bridge_data *bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32((volatile unsigned int *)bp->cfg_data); + val = enable? (val | GRACKLE_STG_ENABLE): (val & ~GRACKLE_STG_ENABLE); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32((volatile unsigned int *)bp->cfg_data, val); +} + void __init pmac_find_bridges(void) { int bus; @@ -411,20 +589,47 @@ static void __init add_bridges(struct device_node *dev) printk(KERN_INFO "PCI buses %d..%d", bus_range[0], bus_range[1]); printk(" controlled by %s at %x\n", dev->name, addr->address); + if (device_is_compatible(dev, "uni-north")) { + int i = uninorth_count++; + uninorth_bridges[i].cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + uninorth_bridges[i].cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + uninorth_bridges[i].node = dev; + /* XXX This is the bridge with the PCI expansion bus. This is also the + * address of the bus that will receive type 1 config accesses and io + * accesses. Appears to be correct for iMac DV and G4 Sawtooth too. + * That means that we cannot do io cycles on the AGP bus nor the internal + * ethernet/fw bus. Fortunately, they appear not to be needed on iMac DV + * and G4 neither. + */ + if (addr->address == 0xf2000000) + uninorth_default = i; + else + continue; + } + bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp)); - if (strcmp(dev->name, "pci") != 0) { - bp->cfg_addr = (volatile unsigned int *) - ioremap(addr->address + 0x800000, 0x1000); - bp->cfg_data = (volatile unsigned char *) - ioremap(addr->address + 0xc00000, 0x1000); - bp->io_base = (void *) ioremap(addr->address, 0x10000); - } else { - /* XXX */ + if (device_is_compatible(dev, "uni-north")) { + bp->cfg_addr = 0; + bp->cfg_data = 0; + /* is 0x10000 enough for io space ? */ + bp->io_base = (void *)ioremap(addr->address, 0x10000); + } else if (strcmp(dev->name, "pci") == 0) { + /* XXX assume this is a mpc106 (grackle) */ bp->cfg_addr = (volatile unsigned int *) ioremap(0xfec00000, 0x1000); bp->cfg_data = (volatile unsigned char *) ioremap(0xfee00000, 0x1000); bp->io_base = (void *) ioremap(0xfe000000, 0x20000); +#if 0 /* Disabled for now, HW problems */ + grackle_set_stg(bp, 1); +#endif + } else { + /* a `bandit' or `chaos' bridge */ + bp->cfg_addr = (volatile unsigned int *) + ioremap(addr->address + 0x800000, 0x1000); + bp->cfg_data = (volatile unsigned char *) + ioremap(addr->address + 0xc00000, 0x1000); + bp->io_base = (void *) ioremap(addr->address, 0x10000); } if (isa_io_base == 0) isa_io_base = (unsigned long) bp->io_base; @@ -453,7 +658,7 @@ fix_intr(struct device_node *node, struct pci_dev *dev) for (; node != 0;node = node->sibling) { class_code = (unsigned int *) get_property(node, "class-code", 0); - if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) + if(class_code && (*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) fix_intr(node->child, dev); reg = (unsigned int *) get_property(node, "reg", 0); if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) @@ -490,20 +695,38 @@ pmac_pcibios_fixup(void) if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || !pin) continue; /* No interrupt generated -> no fixup */ - fix_intr(bp->node->child, dev); + /* We iterate all instances of uninorth for now */ + if (uninorth_count && dev->bus->number == 0) { + int i; + for (i=0;i<uninorth_count;i++) + fix_intr(uninorth_bridges[i].node->child, dev); + } else + fix_intr(bp->node->child, dev); } } void __init pmac_setup_pci_ptrs(void) { - if (find_devices("pci") != 0) { - /* looks like a G3 powermac */ - set_config_access_method(grackle); - } else { + struct device_node* np; + + np = find_devices("pci"); + if (np != 0) + { + if (device_is_compatible(np, "uni-north")) + { + /* looks like an Core99 powermac */ + set_config_access_method(uni); + } else + { + /* looks like a G3 powermac */ + set_config_access_method(grackle); + } + } else + { set_config_access_method(pmac); } - + ppc_md.pcibios_fixup = pmac_pcibios_fixup; } diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index 385e23327..d2d5e6b25 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -3,6 +3,8 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/signal.h> +#include <linux/pci.h> +#include <linux/openpic.h> #include <asm/init.h> #include <asm/io.h> @@ -27,12 +29,38 @@ static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { static int max_irqs; static int max_real_irqs; +static int has_openpic = 0; #define MAXCOUNT 10000000 #define GATWICK_IRQ_POOL_SIZE 10 static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; +extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); + +static void pmac_openpic_mask_irq(unsigned int irq_nr) +{ + openpic_disable_irq(irq_nr); +} + +static void pmac_openpic_unmask_irq(unsigned int irq_nr) +{ + openpic_enable_irq(irq_nr); +} + +struct hw_interrupt_type pmac_open_pic = { + " OpenPIC ", + NULL, + NULL, + pmac_openpic_unmask_irq, + pmac_openpic_mask_irq, + pmac_openpic_mask_irq, + 0 +}; + static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) { unsigned long bit = 1UL << (irq_nr & 0x1f); @@ -141,74 +169,6 @@ static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) ppc_irq_dispatch_handler( regs, irq ); } -#if 0 -void -pmac_do_IRQ(struct pt_regs *regs, - int cpu, - int isfake) -{ - int irq; - unsigned long bits = 0; - -#ifdef __SMP__ - /* IPI's are a hack on the powersurge -- Cort */ - if ( cpu != 0 ) - { -#ifdef CONFIG_XMON - static int xmon_2nd; - if (xmon_2nd) - xmon(regs); -#endif - pmac_smp_message_recv(); - return -1; - } - - { - unsigned int loops = MAXCOUNT; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* __SMP__ */ - - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - - if (irq < 0) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - ppc_spurious_interrupts++; - } - else - { - ppc_irq_dispatch_handler( regs, irq ); - } -#ifdef CONFIG_SMP -out: -#endif /* CONFIG_SMP */ -} -#endif - int pmac_get_irq(struct pt_regs *regs) { @@ -248,15 +208,30 @@ pmac_get_irq(struct pt_regs *regs) } #endif /* __SMP__ */ - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } + /* Yeah, I know, this could be a separate do_IRQ function */ + if (has_openpic) + { + irq = openpic_irq(smp_processor_id()); + if (irq == OPENPIC_VEC_SPURIOUS) + /* We get those when doing polled ADB requests, + * using -2 is a temp hack to disable the printk + */ + irq = -2; /*-1; */ + else + openpic_eoi(smp_processor_id()); + } + else + { + for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + } return irq; } @@ -339,6 +314,51 @@ pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) } } +/* + * The PowerBook 3400/2400/3500 can have a combo ethernet/modem + * card which includes an ohare chip that acts as a second interrupt + * controller. If we find this second ohare, set it up and fix the + * interrupt value in the device tree for the ethernet chip. + */ +static void __init enable_second_ohare(void) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + int second_irq; + struct device_node *irqctrler = find_devices("pci106b,7"); + struct device_node *ether; + + if (irqctrler == NULL || irqctrler->n_addrs <= 0) + return; + addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); + pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); + max_irqs = 64; + if (pci_device_loc(irqctrler, &bus, &devfn) == 0) { + pmac_pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + pmac_pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + + second_irq = irqctrler->intrs[0].line; + printk(KERN_INFO "irq: secondary controller on irq %d\n", second_irq); + request_irq(second_irq, gatwick_action, SA_INTERRUPT, + "interrupt cascade", 0 ); + + /* Fix interrupt for the modem/ethernet combo controller. The number + in the device tree (27) is bogus (correct for the ethernet-only + board but not the combo ethernet/modem board). + The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = find_devices("pci1011,14"); + if (ether && ether->n_intrs > 0) { + ether->intrs[0].line = 60; + printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", + ether->intrs[0].line); + } +} + void __init pmac_pic_init(void) { @@ -347,9 +367,44 @@ pmac_pic_init(void) unsigned long addr; int second_irq = -999; + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + irqctrler = find_type_devices("open-pic"); + if (irqctrler != NULL) + { + printk("PowerMac using OpenPIC irq controller\n"); + if (irqctrler->n_addrs > 0) + { +#ifdef CONFIG_XMON + struct device_node* pswitch; +#endif /* CONFIG_XMON */ + OpenPIC = (volatile struct OpenPIC *) + ioremap(irqctrler->addrs[0].address, + irqctrler->addrs[0].size); + for ( i = 0 ; i < NR_IRQS ; i++ ) + irq_desc[i].handler = &pmac_open_pic; + openpic_init(1); + has_openpic = 1; +#ifdef CONFIG_XMON + pswitch = find_devices("programmer-switch"); + if (pswitch && pswitch->n_intrs) + request_irq(pswitch->intrs[0].line, xmon_irq, 0, + "NMI - XMON", 0); +#endif /* CONFIG_XMON */ + return; + } + irqctrler = NULL; + } - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, - others have 32 */ + /* + * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, + * 1998 G3 Series PowerBooks have 128, + * other powermacs have 32. + * The combo ethernet/modem card for the Powerstar powerbooks + * (2400/3400/3500, ohare based) has a second ohare chip + * effectively making a total of 64. + */ max_irqs = max_real_irqs = 32; irqctrler = find_devices("mac-io"); if (irqctrler) @@ -389,6 +444,12 @@ pmac_pic_init(void) pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); } + /* PowerBooks 3400 and 3500 can have a second controller in a second + ohare chip, on the combo ethernet/modem card */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + enable_second_ohare(); + /* disable all interrupts in all controllers */ for (i = 0; i * 32 < max_irqs; ++i) out_le32(&pmac_irq_hw[i]->enable, 0); @@ -435,7 +496,12 @@ sleep_save_intrs(int viaint) out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); if (max_real_irqs > 32) out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); - mb(); + (void)in_le32(&pmac_irq_hw[0]->flag); + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while(in_le32(&pmac_irq_hw[0]->enable) != ppc_cached_irq_mask[0]); } void diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 72d23a934..e1c1815ac 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -104,6 +104,10 @@ extern char saved_command_line[]; extern void zs_kgdb_hook(int tty_num); static void ohare_init(void); static void init_p2pbridge(void); +static void init_uninorth(void); +#ifdef CONFIG_BOOTX_TEXT +void pmac_progress(char *s, unsigned short hex); +#endif __pmac int @@ -232,8 +236,10 @@ pmac_setup_arch(void) if (fp != 0) { switch (_get_PVR() >> 16) { case 4: /* 604 */ + case 8: /* G3 */ case 9: /* 604e */ case 10: /* mach V (604ev5) */ + case 12: /* G4 */ case 20: /* 620 */ loops_per_sec = *fp; break; @@ -252,9 +258,10 @@ pmac_setup_arch(void) pmac_find_bridges(); init_p2pbridge(); - + init_uninorth(); + /* Checks "l2cr-value" property in the registry */ - if ( (_get_PVR() >> 16) == 8) { + if ( (_get_PVR() >> 16) == 8 || (_get_PVR() >> 16) == 12 ) { struct device_node *np = find_devices("cpus"); if (np == 0) np = find_type_devices("cpu"); @@ -346,6 +353,33 @@ static void __init ohare_init(void) } } +static void __init +init_uninorth(void) +{ + /* + * Turns on the gmac clock so that it responds to PCI cycles + * later, the driver may want to turn it off again to save + * power when interface is down + */ + struct device_node* uni_n = find_devices("uni-n"); + struct device_node* gmac = find_devices("ethernet"); + unsigned long* addr; + + if (!uni_n || uni_n->n_addrs < 1) + return; + addr = ioremap(uni_n->addrs[0].address, 0x300); + + while(gmac) { + if (device_is_compatible(gmac, "gmac")) + break; + gmac = gmac->next; + } + if (gmac) { + *(addr + 8) |= 2; + eieio(); + } +} + extern char *bootpath; extern char *bootdevice; void *boot_host; @@ -401,14 +435,11 @@ note_scsi_host(struct device_node *node, void *host) #endif #if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) -extern int pmac_ide_count; -extern struct device_node *pmac_ide_node[]; -static int ide_majors[] = { 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 }; kdev_t __init find_ide_boot(void) { char *p; - int i, n; + int n; if (bootdevice == NULL) return 0; @@ -417,18 +448,7 @@ kdev_t __init find_ide_boot(void) return 0; n = p - bootdevice; - /* - * Look through the list of IDE interfaces for this one. - */ - for (i = 0; i < pmac_ide_count; ++i) { - char *name = pmac_ide_node[i]->full_name; - if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) { - /* XXX should cope with the 2nd drive as well... */ - return MKDEV(ide_majors[i], 0); - } - } - - return 0; + return pmac_find_ide_boot(bootdevice, n); } #endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ @@ -464,10 +484,10 @@ void note_bootable_part(kdev_t dev, int part) find_boot_device(); found_boot = 1; } - if (dev == boot_dev) { + if (boot_dev == 0 || dev == boot_dev) { ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); boot_dev = NODEV; - printk(" (root)"); + printk(" (root on %d)", part); } } @@ -550,11 +570,15 @@ pmac_ide_default_irq(ide_ioreg_t base) return 0; } +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) +extern ide_ioreg_t pmac_ide_get_base(int index); +#endif + ide_ioreg_t pmac_ide_default_io_base(int index) { #if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) - return pmac_ide_regbase[index]; + return pmac_ide_get_base(index); #else return 0; #endif @@ -660,5 +684,22 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; ppc_ide_md.io_base = _IO_BASE; /* actually too early for this :-( */ -#endif +#endif +#ifdef CONFIG_BOOTX_TEXT + ppc_md.progress = pmac_progress; +#endif + if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); + } + +#ifdef CONFIG_BOOTX_TEXT +extern void drawchar(char c); +extern void drawstring(const char *c); +void +pmac_progress(char *s, unsigned short hex) +{ + drawstring(s); + drawchar('\n'); +} +#endif CONFIG_BOOTX_TEXT + diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c index 3b7dd283f..1c935a625 100644 --- a/arch/ppc/kernel/pmac_time.c +++ b/arch/ppc/kernel/pmac_time.c @@ -71,8 +71,8 @@ unsigned long pmac_get_rtc_time(void) if (req.reply_len != 7) printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", req.reply_len); - return (req.reply[3] << 24) + (req.reply[4] << 16) - + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + return (unsigned long)(req.reply[1] << 24) + (req.reply[2] << 16) + + (req.reply[3] << 8) + (unsigned long)req.reply[4] - RTC_OFFSET; #endif /* CONFIG_ADB_CUDA */ #ifdef CONFIG_ADB_PMU case SYS_CTRLER_PMU: diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c index b6397daac..42ca7eadc 100644 --- a/arch/ppc/kernel/ppc-stub.c +++ b/arch/ppc/kernel/ppc-stub.c @@ -351,7 +351,7 @@ static inline int get_msr() static inline void set_msr(int msr) { - asm volatile("mfmsr %0" : : "r" (msr)); + asm volatile("mtmsr %0" : : "r" (msr)); } /* Set up exception handlers for tracing and breakpoints diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index b90fa7a2c..264a24d48 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -45,17 +45,9 @@ extern unsigned long pte_misses; extern unsigned long pte_errors; static struct file_operations ppc_htab_operations = { - ppc_htab_lseek, /* lseek */ - ppc_htab_read, /* read */ - ppc_htab_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ + llseek: ppc_htab_lseek, + read: ppc_htab_read, + write: ppc_htab_write, }; /* @@ -531,7 +523,8 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, "0.5", "1.0", "(reserved2)", "(reserved3)" }; - if ( (_get_PVR() >> 16) != 8) return -EFAULT; + if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) + return -EFAULT; if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { *lenp = 0; diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 65e925034..9a5444a51 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -229,6 +229,8 @@ EXPORT_SYMBOL(find_path_device); EXPORT_SYMBOL(find_phandle); EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_pci_device_OFnode); +EXPORT_SYMBOL(find_all_nodes); EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 633757875..fd14fc483 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -40,7 +40,7 @@ unsigned char *Motherboard_routes; static unsigned long *ProcInfo; extern int chrp_get_irq(struct pt_regs *); -extern void chrp_post_irq(int); +extern void chrp_post_irq(struct pt_regs* regs, int); /* Tables for known hardware */ diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 241b7c33c..ed98ba6f0 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -57,6 +57,8 @@ union task_union __attribute((aligned(16))) init_task_union = { }; /* only used to get secondary processor up */ struct task_struct *current_set[NR_CPUS] = {&init_task, }; +char *sysmap = NULL; +unsigned long sysmap_size = 0; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index 049cde10f..a52bdd804 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -30,6 +30,10 @@ #include <asm/system.h> #include <asm/gemini.h> +#ifdef CONFIG_FB +#include <asm/linux_logo.h> +#endif + /* * Properties whose value is longer than this get excluded from our * copy of the device tree. This way we don't waste space storing @@ -91,7 +95,7 @@ unsigned int prom_num_displays = 0; char *of_stdout_device = 0; prom_entry prom = 0; -ihandle prom_chosen = 0, prom_stdout = 0; +ihandle prom_chosen = 0, prom_stdout = 0, prom_disp_node = 0; extern char *klimit; char *bootpath = 0; @@ -102,33 +106,35 @@ unsigned int rtas_entry = 0; /* physical pointer */ unsigned int rtas_size = 0; unsigned int old_rtas = 0; +int use_of_interrupt_tree = 0; static struct device_node *allnodes = 0; +#ifdef CONFIG_BOOTX_TEXT + static void clearscreen(void); static void flushscreen(void); -#ifdef CONFIG_BOOTX_TEXT - void drawchar(char c); void drawstring(const char *c); static void drawhex(unsigned long v); static void scrollscreen(void); static void draw_byte(unsigned char c, long locX, long locY); -static void draw_byte_32(unsigned char *bits, unsigned long *base); -static void draw_byte_16(unsigned char *bits, unsigned long *base); -static void draw_byte_8(unsigned char *bits, unsigned long *base); +static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb); -static long g_loc_X; -static long g_loc_Y; -static long g_max_loc_X; -static long g_max_loc_Y; +/* We want those in data, not BSS */ +static long g_loc_X = 0; +static long g_loc_Y = 0; +static long g_max_loc_X = 0; +static long g_max_loc_Y = 0; #define cmapsz (16*256) static unsigned char vga_font[cmapsz]; -#endif +#endif /* CONFIG_BOOTX_TEXT */ static void *call_prom(const char *service, int nargs, int nret, ...); @@ -138,15 +144,25 @@ static unsigned long inspect_node(phandle, struct device_node *, unsigned long, unsigned long, struct device_node ***); static unsigned long finish_node(struct device_node *, unsigned long, interpret_func *); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); static void *early_get_property(unsigned long, unsigned long, char *); +#ifdef CONFIG_BOOTX_TEXT +static void setup_disp_fake_bi(ihandle dp); +static void prom_welcome(boot_infos_t* bi, unsigned long phys); +#endif + extern void enter_rtas(void *); extern unsigned long reloc_offset(void); extern char cmd_line[512]; /* XXX */ boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */ +#ifdef CONFIG_BOOTX_TEXT +boot_infos_t *disp_bi = 0; +boot_infos_t fake_bi = {0,}; +#endif unsigned long dev_tree_size; /* @@ -240,7 +256,7 @@ prom_print(const char *msg) if (RELOC(prom_stdout) == 0) { #ifdef CONFIG_BOOTX_TEXT - if (RELOC(boot_infos) != 0) + if (RELOC(disp_bi) != 0) drawstring(msg); #endif return; @@ -261,7 +277,6 @@ prom_print(const char *msg) } } -unsigned long smp_ibm_chrp_hack __initdata = 0; unsigned long smp_chrp_cpu_nr __initdata = 1; /* @@ -269,23 +284,29 @@ unsigned long smp_chrp_cpu_nr __initdata = 1; * handling exceptions and the MMU hash table for us. */ __init -void +unsigned long prom_init(int r3, int r4, prom_entry pp) { #ifdef CONFIG_SMP int i; phandle node; char type[16], *path; -#endif +#endif + int chrp = 0; unsigned long mem; - ihandle prom_rtas; + ihandle prom_rtas, prom_mmu, prom_op; unsigned long offset = reloc_offset(); int l; char *p, *d; + int prom_version = 0; + unsigned long phys; + + /* Default */ + phys = offset + KERNELBASE; /* check if we're apus, return if we are */ if ( r3 == 0x61707573 ) - return; + return phys; /* If we came here from BootX, clear the screen, * set up some pointers and return. */ @@ -294,22 +315,20 @@ prom_init(int r3, int r4, prom_entry pp) unsigned long space; unsigned long ptr, x; char *model; -#ifdef CONFIG_BOOTX_TEXT - unsigned long flags; -#endif RELOC(boot_infos) = PTRUNRELOC(bi); if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) bi->logicalDisplayBase = 0; - clearscreen(); - #ifdef CONFIG_BOOTX_TEXT RELOC(g_loc_X) = 0; RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; + RELOC(disp_bi) = PTRUNRELOC(bi); + clearscreen(); + /* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since there is nothing much we can do with an incompatible version, except display a message and eventually hang the processor... @@ -320,23 +339,9 @@ prom_init(int r3, int r4, prom_entry pp) if (!BOOT_INFO_IS_COMPATIBLE(bi)) prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); - prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); - prom_print(RELOC("\nstarted at : 0x")); - drawhex(reloc_offset() + KERNELBASE); - prom_print(RELOC("\nlinked at : 0x")); - drawhex(KERNELBASE); - prom_print(RELOC("\nframe buffer at : 0x")); - drawhex((unsigned long)bi->dispDeviceBase); - prom_print(RELOC(" (phys), 0x")); - drawhex((unsigned long)bi->logicalDisplayBase); - prom_print(RELOC(" (log)")); - prom_print(RELOC("\nMSR : 0x")); - __asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); - drawhex(flags); - prom_print(RELOC("\n\n")); -#endif - /* Out of the #if/#endif since it flushes the clearscreen too */ + prom_welcome(bi, phys); flushscreen(); +#endif /* CONFIG_BOOTX_TEXT */ /* New BootX enters kernel with MMU off, i/os are not allowed here. This hack will have been done by the boostrap anyway. @@ -381,12 +386,12 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC("booting...\n")); flushscreen(); #endif - return; + return phys; } /* check if we're prep, return if we are */ if ( *(unsigned long *)(0) == 0xdeadc0de ) - return; + return phys; /* First get a handle for the stdout device */ RELOC(prom) = pp; @@ -407,6 +412,30 @@ prom_init(int r3, int r4, prom_entry pp) RELOC(of_stdout_device) = PTRUNRELOC(p); mem += strlen(p) + 1; + /* Find the OF version */ + prom_op = call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom")); + prom_version = 0; + if (prom_op != (void*)-1) { + char model[64]; + int sz; + sz = (int)call_prom(RELOC("getprop"), 4, 1, prom_op, RELOC("model"), model, 64); + if (sz > 0) { + char *c; + /* hack to skip the ibm chrp firmware # */ + if ( strncmp(model,RELOC("IBM"),3) ) { + for (c = model; *c; c++) + if (*c >= '0' && *c <= '9') { + prom_version = *c - '0'; + break; + } + } + else + chrp = 1; + } + } + if (prom_version >= 3) + prom_print(RELOC("OF Version 3 detected.\n")); + /* Get the boot device and translate it to a full OF pathname. */ p = (char *) mem; l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), @@ -478,6 +507,42 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC(" done\n")); } + /* If we are already running at 0xc0000000, we assume we were loaded by + * an OF bootloader which did set a BAT for us. This breaks OF translate + * so we force phys to be 0 + */ + if (offset == 0) + phys = 0; + else { + if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), + RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + } else { + int nargs; + struct prom_args prom_args; + nargs = 4; + prom_args.service = RELOC("call-method"); + prom_args.nargs = nargs; + prom_args.nret = 4; + prom_args.args[0] = RELOC("translate"); + prom_args.args[1] = prom_mmu; + prom_args.args[2] = (void *)(offset + KERNELBASE); + prom_args.args[3] = (void *)1; + RELOC(prom)(&prom_args); + + /* We assume the phys. address size is 3 cells */ + if (prom_args.args[nargs] != 0) + prom_print(RELOC(" (translate failed) ")); + else + phys = (unsigned long)prom_args.args[nargs+3]; + } + } + +#ifdef CONFIG_BOOTX_TEXT + if (!chrp && RELOC(prom_disp_node) != 0) + setup_disp_fake_bi(RELOC(prom_disp_node)); +#endif + #ifdef CONFIG_SMP /* * With CHRP SMP we need to use the OF to start the other @@ -512,7 +577,7 @@ prom_init(int r3, int r4, prom_entry pp) node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); if ( (int)call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),type, sizeof(type)) <= 0) - return; + return phys; /* copy the holding pattern code to someplace safe (8M) */ memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x100 ); @@ -554,6 +619,79 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC("...failed\n")); } #endif + /* If OpenFirmware version >= 3, then use quiesce call */ + if (prom_version >= 3) { + prom_print(RELOC("Calling quiesce ...\n")); + call_prom(RELOC("quiesce"), 0, 0); + offset = reloc_offset(); + phys = offset + KERNELBASE; + } + +#ifdef CONFIG_BOOTX_TEXT + if (!chrp && RELOC(disp_bi)) { + RELOC(prom_stdout) = 0; + clearscreen(); + prom_welcome(PTRRELOC(RELOC(disp_bi)), phys); + prom_print(RELOC("booting...\n")); + } +#endif + + return phys; +} + +#ifdef CONFIG_BOOTX_TEXT +__init static void +prom_welcome(boot_infos_t* bi, unsigned long phys) +{ + unsigned long offset = reloc_offset(); + unsigned long flags; + unsigned long pvr; + + prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); + prom_print(RELOC("\nstarted at : 0x")); + drawhex(phys); + prom_print(RELOC("\nlinked at : 0x")); + drawhex(KERNELBASE); + prom_print(RELOC("\nframe buffer at : 0x")); + drawhex((unsigned long)bi->dispDeviceBase); + prom_print(RELOC(" (phys), 0x")); + drawhex((unsigned long)bi->logicalDisplayBase); + prom_print(RELOC(" (log)")); + prom_print(RELOC("\nMSR : 0x")); + __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); + drawhex(flags); + __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); + pvr >>= 16; + if (pvr > 1) { + prom_print(RELOC("\nHID0 : 0x")); + __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); + drawhex(flags); + } + if (pvr == 8 || pvr == 12) { + prom_print(RELOC("\nICTC : 0x")); + __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); + drawhex(flags); + } + prom_print(RELOC("\n\n")); +} +#endif + +static int prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + struct prom_args prom_args; + unsigned long offset = reloc_offset(); + + prom_args.service = RELOC("call-method"); + prom_args.nargs = 6; + prom_args.nret = 1; + prom_args.args[0] = RELOC("color!"); + prom_args.args[1] = ih; + prom_args.args[2] = (void *) i; + prom_args.args[3] = (void *) b; + prom_args.args[4] = (void *) g; + prom_args.args[5] = (void *) r; + RELOC(prom)(&prom_args); + return (int) prom_args.args[6]; } /* @@ -573,6 +711,26 @@ check_display(unsigned long mem) int i; unsigned long offset = reloc_offset(); char type[16], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + RELOC(prom_disp_node) = 0; for (node = 0; prom_next_node(&node); ) { type[0] = 0; @@ -595,6 +753,26 @@ check_display(unsigned long mem) } prom_print(RELOC("... ok\n")); + if (RELOC(prom_disp_node) == 0) + RELOC(prom_disp_node) = node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, RELOC(default_colors)[i*3], + RELOC(default_colors)[i*3+1], + RELOC(default_colors)[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + RELOC(linux_logo_red)[i], + RELOC(linux_logo_green)[i], + RELOC(linux_logo_blue)[i]) != 0) + break; +#endif /* CONFIG_FB */ + /* * If this display is the device that OF is using for stdout, * move it to the front of the list. @@ -614,6 +792,79 @@ check_display(unsigned long mem) return ALIGN(mem); } +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +#ifdef CONFIG_BOOTX_TEXT +__init +static void +setup_disp_fake_bi(ihandle dp) +{ + unsigned int len; + int width = 640, height = 480, depth = 8, pitch; + unsigned address; + boot_infos_t* bi; + unsigned long offset = reloc_offset(); + + prom_print(RELOC("Initing fake screen\n")); + + len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("depth"), &len, sizeof(len)); + if (len == 0) + prom_print(RELOC("Warning: assuming display depth = 8\n")); + else + depth = len; + width = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("width"), &len, sizeof(len)); + width = len; + if (width == 0) { + prom_print(RELOC("Failed to get width\n")); + return; + } + height = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("height"), &len, sizeof(len)); + height = len; + if (height == 0) { + prom_print(RELOC("Failed to get height\n")); + return; + } + pitch = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("linebytes"), &len, sizeof(len)); + pitch = len; + if (pitch == 0) { + prom_print(RELOC("Failed to get pitch\n")); + return; + } + address = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("address"), &len, sizeof(len)); + address = len; + if (address == 0) { + prom_print(RELOC("Failed to get address\n")); + return; + } +#if 0 + /* kludge for valkyrie */ + if (strcmp(dp->name, "valkyrie") == 0) + address += 0x1000; + } +#endif + + RELOC(disp_bi) = &fake_bi; + bi = PTRRELOC((&fake_bi)); + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y) = 0; + RELOC(g_max_loc_X) = width / 8; + RELOC(g_max_loc_Y) = height / 16; + bi->logicalDisplayBase = (unsigned char *)address; + bi->dispDeviceBase = (unsigned char *)address; + bi->dispDeviceRowBytes = pitch; + bi->dispDeviceDepth = depth; + bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; +} +#endif + __init static int prom_next_node(phandle *nodep) @@ -748,6 +999,16 @@ void finish_device_tree(void) { unsigned long mem = (unsigned long) klimit; + char* model; + + /* Here, we decide if we'll use the interrupt-tree (new Core99 code) or not. + * This code was only tested with Core99 machines so far, but should be easily + * adapted to older newworld machines (iMac, B&W G3, Lombard). + */ + model = get_property(allnodes, "model", 0); + if ((boot_infos == 0) && model && (strcmp(model, "PowerBook2,1") == 0 + || strcmp(model, "PowerMac2,1") == 0 || strcmp(model, "PowerMac3,1") == 0)) + use_of_interrupt_tree = 1; mem = finish_node(allnodes, mem, NULL); dev_tree_size = mem - (unsigned long) allnodes; @@ -788,6 +1049,9 @@ finish_node(struct device_node *np, unsigned long mem_start, if (ifunc != NULL) { mem_start = ifunc(np, mem_start); } + if (use_of_interrupt_tree) { + mem_start = finish_node_interrupts(np, mem_start); + } /* the f50 sets the name to 'display' and 'compatible' to what we * expect for the name -- Cort @@ -834,6 +1098,133 @@ finish_node(struct device_node *np, unsigned long mem_start, return mem_start; } +/* This routine walks the interrupt tree for a given device node and gather + * all necessary informations according to the draft interrupt mapping + * for CHRP. The current version was only tested on Apple "Core99" machines + * and may not handle cascaded controllers correctly. + */ +__init +static unsigned long +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + /* Finish this node */ + unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; + phandle *parent; + struct device_node *node, *parent_node; + int l, isize, ipsize, asize, map_size, regpsize; + + /* Currently, we don't look at all nodes with no "interrupts" property */ + interrupts = (unsigned int *)get_property(np, "interrupts", &l); + if (interrupts == NULL) + return mem_start; + ipsize = l>>2; + + reg = (unsigned int *)get_property(np, "reg", &l); + regpsize = l>>2; + + /* We assume default interrupt cell size is 1 (bugus ?) */ + isize = 1; + node = np; + + do { + /* We adjust the cell size if the current parent contains an #interrupt-cells + * property */ + isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); + if (isizep) + isize = *isizep; + + /* We don't do interrupt cascade (ISA) for now, we stop on the first + * controller found + */ + if (get_property(node, "interrupt-controller", &l)) { + int i,j; + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = ipsize / isize; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *interrupts++; + np->intrs[i].sense = 0; + if (isize > 1) + np->intrs[i].sense = *interrupts++; + for (j=2; j<isize; j++) + interrupts++; + } + return mem_start; + } + /* We lookup for an interrupt-map. This code can only handle one interrupt + * per device in the map. We also don't handle #address-cells in the parent + * I skip the pci node itself here, may not be necessary but I don't like it's + * reg property. + */ + if (np != node) + map = (unsigned int *)get_property(node, "interrupt-map", &l); + else + map = NULL; + if (map && l) { + int i, found, temp_isize; + map_size = l>>2; + map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); + asizep = (unsigned int *)get_property(node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + asize = *asizep; + else + asize = 0; + found = 0; + while(map_size>0 && !found) { + found = 1; + for (i=0; i<asize; i++) { + unsigned int mask = map_mask ? map_mask[i] : 0xffffffff; + if (!reg || (i>=regpsize) || ((mask & *map) != (mask & reg[i]))) + found = 0; + map++; + map_size--; + } + for (i=0; i<isize; i++) { + unsigned int mask = map_mask ? map_mask[i+asize] : 0xffffffff; + if ((mask & *map) != (mask & interrupts[i])) + found = 0; + map++; + map_size--; + } + parent = *((phandle *)(map)); + map+=1; map_size-=1; + parent_node = find_phandle(parent); + temp_isize = isize; + if (parent_node) { + isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l); + if (isizep) + temp_isize = *isizep; + } + if (!found) { + map += temp_isize; + map_size-=temp_isize; + } + } + if (found) { + node = parent_node; + reg = NULL; + regpsize = 0; + interrupts = (unsigned int *)map; + ipsize = temp_isize*1; + continue; + } + } + /* We look for an explicit interrupt-parent. + */ + parent = (phandle *)get_property(node, "interrupt-parent", &l); + if (parent && (l == sizeof(phandle)) && + (parent_node = find_phandle(*parent))) { + node = parent_node; + continue; + } + /* Default, get real parent */ + node = node->parent; + } while(node); + + return mem_start; +} + + /* * When BootX makes a copy of the device tree from the MacOS * Name Registry, it is in the format we use but all of the pointers @@ -892,6 +1283,9 @@ interpret_pci_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + /* * If the pci host bridge has an interrupt-map property, * look for our node in it. @@ -901,14 +1295,28 @@ interpret_pci_props(struct device_node *np, unsigned long mem_start) get_property(np->parent, "interrupt-map", &ml)) != 0 && (ip = (int *) get_property(np, "interrupts", &l)) != 0) { unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00; + unsigned int cell_size; + struct device_node* np2; + /* This is hackish, but is only used for BootX booting */ + cell_size = sizeof(struct pci_intr_map); + np2 = np->parent; + while(np2) { + if (device_is_compatible(np2, "uni-north")) { + cell_size += 4; + break; + } + np2 = np2->parent; + } np->n_intrs = 0; np->intrs = (struct interrupt_info *) mem_start; - for (i = 0; (ml -= sizeof(struct pci_intr_map)) >= 0; ++i) { - if (imp[i].addr.a_hi == devfn) { - np->intrs[np->n_intrs].line = imp[i].intr; - np->intrs[np->n_intrs].sense = 0; + for (i = 0; (ml -= cell_size) >= 0; ++i) { + if (imp->addr.a_hi == devfn) { + np->intrs[np->n_intrs].line = imp->intr; + np->intrs[np->n_intrs].sense = 0; /* FIXME */ ++np->n_intrs; } + imp = (struct pci_intr_map *)(((unsigned int)imp) + + cell_size); } if (np->n_intrs == 0) np->intrs = 0; @@ -965,6 +1373,9 @@ interpret_dbdma_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "AAPL,interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "interrupts", &l); @@ -988,13 +1399,14 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) struct reg_property *rp; struct address_range *adr; unsigned long base_address; - int i, l, *ip; + int i, l, keylargo, *ip; struct device_node *db; base_address = 0; for (db = np->parent; db != NULL; db = db->parent) { if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { base_address = db->addrs[0].address; + keylargo = device_is_compatible(db, "Keylargo"); break; } } @@ -1014,6 +1426,9 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "AAPL,interrupts", &l); @@ -1022,9 +1437,15 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) if (_machine == _MACH_Pmac) { /* for the iMac */ np->n_intrs = l / sizeof(int); + /* Hack for BootX on Core99 */ + if (keylargo) + np->n_intrs = np->n_intrs/2; for (i = 0; i < np->n_intrs; ++i) { np->intrs[i].line = *ip++; - np->intrs[i].sense = 0; + if (keylargo) + np->intrs[i].sense = *ip++; + else + np->intrs[i].sense = 0; } } else { /* CHRP machines */ @@ -1064,6 +1485,9 @@ interpret_isa_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "interrupts", &l); if (ip != 0) { np->intrs = (struct interrupt_info *) mem_start; @@ -1101,6 +1525,9 @@ interpret_root_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "AAPL,interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "interrupts", &l); @@ -1157,6 +1584,49 @@ find_type_devices(const char *type) return head; } +/* Finds a device node given its PCI bus number, device number + * and function number + */ +__openfirmware +struct device_node * +find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn) +{ + struct device_node* np; + unsigned int *reg; + int l; + + for (np = allnodes; np != 0; np = np->allnext) { + char *pname = np->parent ? + (char *)get_property(np->parent, "name", &l) : 0; + if (pname && strcmp(pname, "mac-io") == 0) + continue; + reg = (unsigned int *) get_property(np, "reg", &l); + if (reg == 0 || l < sizeof(struct reg_property)) + continue; + if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus) + break; + } + return np; +} + +/* + * Returns all nodes linked together + */ +__openfirmware +struct device_node * +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = 0; + return head; +} + /* Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ @@ -1377,18 +1847,28 @@ abort() prom_exit(); } -#ifdef CONFIG_XMON +#ifdef CONFIG_BOOTX_TEXT + +/* Here's a small text engine to use during early boot or for debugging purposes + * + * todo: + * + * - build some kind of vgacon with it to enable early printk + * - move to a separate file + * - add a few video driver hooks to keep in sync with display + * changes. + */ + __init void map_bootx_text(void) { - if (boot_infos == 0) + if (disp_bi == 0) return; - boot_infos->logicalDisplayBase = - ioremap((unsigned long) boot_infos->dispDeviceBase, - boot_infos->dispDeviceRowBytes * boot_infos->dispDeviceRect[3]); + disp_bi->logicalDisplayBase = + ioremap((unsigned long) disp_bi->dispDeviceBase, + disp_bi->dispDeviceRowBytes * disp_bi->dispDeviceRect[3]); } -#endif /* CONFIG_XMON */ /* Calc the base address of a given point (x,y) */ __pmac @@ -1410,7 +1890,7 @@ static void clearscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -1435,7 +1915,7 @@ static void flushscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -1452,14 +1932,12 @@ flushscreen(void) } } -#ifdef CONFIG_BOOTX_TEXT - __pmac static void scrollscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *src = (unsigned long *)calc_base(bi,0,16); unsigned long *dst = (unsigned long *)calc_base(bi,0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * @@ -1563,19 +2041,20 @@ static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned char *base = calc_base(bi, locX << 3, locY << 4); unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + int rb = bi->dispDeviceRowBytes; switch(bi->dispDeviceDepth) { case 32: - draw_byte_32(font, (unsigned long *)base); + draw_byte_32(font, (unsigned long *)base, rb); break; case 16: - draw_byte_16(font, (unsigned long *)base); + draw_byte_16(font, (unsigned long *)base, rb); break; case 8: - draw_byte_8(font, (unsigned long *)base); + draw_byte_8(font, (unsigned long *)base, rb); break; default: break; @@ -1613,15 +2092,12 @@ static unsigned long expand_bits_16[4] = { __pmac static void -draw_byte_32(unsigned char *font, unsigned long *base) +draw_byte_32(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; - for (l = 0; l < 16; ++l) { bits = *font++; @@ -1633,19 +2109,18 @@ draw_byte_32(unsigned char *font, unsigned long *base) base[5] = (-((bits >> 2) & 1) & fg) ^ bg; base[6] = (-((bits >> 1) & 1) & fg) ^ bg; base[7] = (-(bits & 1) & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } __pmac static void -draw_byte_16(unsigned char *font, unsigned long *base) +draw_byte_16(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); unsigned long *eb = RELOC(expand_bits_16); for (l = 0; l < 16; ++l) @@ -1655,19 +2130,18 @@ draw_byte_16(unsigned char *font, unsigned long *base) base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; base[3] = (eb[bits & 3] & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } __pmac static void -draw_byte_8(unsigned char *font, unsigned long *base) +draw_byte_8(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0x0F0F0F0FUL; int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); unsigned long *eb = RELOC(expand_bits_8); for (l = 0; l < 16; ++l) @@ -1675,7 +2149,7 @@ draw_byte_8(unsigned char *font, unsigned long *base) bits = *font++; base[0] = (eb[bits >> 4] & fg) ^ bg; base[1] = (eb[bits & 0xf] & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } @@ -2026,3 +2500,4 @@ static unsigned char vga_font[cmapsz] = { }; #endif /* CONFIG_BOOTX_TEXT */ + diff --git a/arch/ppc/kernel/semaphore.c b/arch/ppc/kernel/semaphore.c index d630c80dc..f17bc16ce 100644 --- a/arch/ppc/kernel/semaphore.c +++ b/arch/ppc/kernel/semaphore.c @@ -137,3 +137,44 @@ int __down_trylock(struct semaphore * sem) { return waking_non_zero_trylock(sem); } + + +/* + * rw semaphores Ani Joshi <ajoshi@unixbox.com> + * based on alpha port by Andrea Arcangeli <andrea@suse.de> + */ + +void down_read_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->wait, &wait); + + do { + __set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_unlock_irq(&sem->lock); + schedule(); + spin_lock_irq(&sem->lock); + } while(sem->wr); + + remove_wait_queue(&sem->wait, &wait); +} + +void down_write_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->wait, &wait); + + do { + __set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_unlock_irq(&sem->lock); + schedule(); + spin_lock_irq(&sem->lock); + } while(sem->rd || sem->wr); + + remove_wait_queue(&sem->wait, &wait); +} + diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index dccb066ff..19ce0a25e 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -69,6 +69,13 @@ extern void gemini_init(unsigned long r3, unsigned long r6, unsigned long r7); +#ifdef CONFIG_BOOTX_TEXT +extern void map_bootx_text(void); +#endif +#ifdef CONFIG_XMON +extern void xmon_map_scc(void); +#endif + extern boot_infos_t *boot_infos; char saved_command_line[256]; unsigned char aux_device_present; @@ -261,7 +268,7 @@ int get_cpuinfo(char *buffer) } break; case 0x000C: - len += sprintf(len+buffer, "7400\n"); + len += sprintf(len+buffer, "7400 (G4)\n"); break; case 0x0020: len += sprintf(len+buffer, "403G"); @@ -292,7 +299,7 @@ int get_cpuinfo(char *buffer) * Assume here that all clock rates are the same in a * smp system. -- Cort */ -#ifndef CONFIG_8xx +#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) if ( have_of ) { struct device_node *cpu_node; @@ -316,7 +323,7 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "clock\t\t: %dMHz\n", *fp / 1000000); } -#endif +#endif /* !CONFIG_4xx && !CONFIG_8xx */ if (ppc_md.setup_residual != NULL) { @@ -410,8 +417,9 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { parse_bootinfo(); - + if ( ppc_md.progress ) ppc_md.progress("id mach(): start", 0x100); + #if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) #ifndef CONFIG_MACH_SPECIFIC /* if we didn't get any bootinfo telling us what we are... */ @@ -477,11 +485,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, char *p; #ifdef CONFIG_BLK_DEV_INITRD - if (r3 - KERNELBASE < 0x800000 - && r4 != 0 && r4 != 0xdeadbeef) { + if (r3 && r4 && r4 != 0xdeadbeef) + { initrd_start = r3; initrd_end = r3 + r4; ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; } #endif cmd_line[0] = 0; @@ -519,6 +528,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, default: printk("Unknown machine type in identify_machine!\n"); } + /* Check for nobats option (used in mapin_ram). */ if (strstr(cmd_line, "nobats")) { extern int __map_without_bats; @@ -567,9 +577,11 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, int parse_bootinfo(void) { struct bi_record *rec; - extern char _end[]; + extern char __bss_start[]; + extern char *sysmap; + extern unsigned long sysmap_size; - rec = (struct bi_record *)PAGE_ALIGN((ulong)_end); + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) { /* @@ -577,11 +589,10 @@ int parse_bootinfo(void) * we have the bootloader handle all the relocation and * prom calls -- Cort */ - rec = (struct bi_record *)PAGE_ALIGN((ulong)_end+0x10000); + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) return -1; } - for ( ; rec->tag != BI_LAST ; rec = (struct bi_record *)((ulong)rec + rec->size) ) { @@ -591,6 +602,11 @@ int parse_bootinfo(void) case BI_CMD_LINE: memcpy(cmd_line, (void *)data, rec->size); break; + case BI_SYSMAP: + sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : + (data[0]+KERNELBASE)); + sysmap_size = data[1]; + break; #ifdef CONFIG_BLK_DEV_INITRD case BI_INITRD: initrd_start = data[0]; @@ -603,7 +619,6 @@ int parse_bootinfo(void) have_of = data[1]; break; #endif /* CONFIG_MACH_SPECIFIC */ - } } @@ -613,7 +628,7 @@ int parse_bootinfo(void) /* Checks "l2cr=xxxx" command-line option */ void ppc_setup_l2cr(char *str, int *ints) { - if ( (_get_PVR() >> 16) == 8) + if ( ((_get_PVR() >> 16) == 8) || ((_get_PVR() >> 16) == 12) ) { unsigned long val = simple_strtoul(str, NULL, 0); printk(KERN_INFO "l2cr set to %lx\n", val); @@ -639,12 +654,21 @@ void __init setup_arch(char **cmdline_p) extern char *klimit; extern void do_init_bootmem(void); +#ifdef CONFIG_BOOTX_TEXT + map_bootx_text(); + prom_print("identify machine\n"); +#endif + #ifdef CONFIG_XMON - extern void xmon_map_scc(void); xmon_map_scc(); if (strstr(cmd_line, "xmon")) xmon(0); #endif /* CONFIG_XMON */ + if ( ppc_md.progress ) ppc_md.progress("setup_arch: enter", 0x3eab); +#if defined(CONFIG_KGDB) + set_debug_traps(); + breakpoint(); +#endif /* reboot on panic */ panic_timeout = 180; @@ -653,16 +677,16 @@ void __init setup_arch(char **cmdline_p) init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) klimit; - + /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); *cmdline_p = cmd_line; /* set up the bootmem stuff with available memory */ do_init_bootmem(); + if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab); ppc_md.setup_arch(); - /* clear the progress line */ if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); } diff --git a/arch/ppc/kernel/sleep.S b/arch/ppc/kernel/sleep.S index 3ead7bd28..b73acd6ce 100644 --- a/arch/ppc/kernel/sleep.S +++ b/arch/ppc/kernel/sleep.S @@ -171,6 +171,11 @@ _GLOBAL(low_sleep_handler) */ wake_up: + /* Flash inval the instruction cache */ + mfspr r3,HID0 + ori r3,r3, HID0_ICFI + mtspr HID0,r3 + isync /* Restore the HID0 register. This turns on the L1 caches. */ subi r1,r1,SL_PC lwz r3,SL_HID0(r1) diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index 30bed889b..e1a3fdcbb 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -252,9 +252,14 @@ asmlinkage int sys_pause(void) asmlinkage int sys_uname(struct old_utsname * name) { - if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) - return 0; - return -EFAULT; + int err; + + if (!name) + return -EFAULT; + down_read(&uts_sem); + err = copy_to_user(name, &system_utsname, sizeof (*name)); + up(&uts_sem); + return err ? -EFAULT : 0; } asmlinkage int sys_olduname(struct oldold_utsname * name) @@ -266,6 +271,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) return -EFAULT; + down_read(&uts_sem); error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); error -= __put_user(0,name->sysname+__OLD_UTS_LEN); error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); @@ -277,6 +283,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); error = __put_user(0,name->machine+__OLD_UTS_LEN); error = error ? -EFAULT : 0; + up(&uts_sem); return error; } diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 04b4e2d36..5cc34c5a5 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -128,6 +128,20 @@ MachineCheckException(struct pt_regs *regs) _exception(SIGSEGV, regs); } +void +SMIException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + { + debugger(regs); + return; + } +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("System Management Interrupt"); +} + #if defined(CONFIG_ALTIVEC) void AltiVecUnavailable(struct pt_regs *regs) diff --git a/arch/ppc/kernel/walnut_setup.c b/arch/ppc/kernel/walnut_setup.c new file mode 100644 index 000000000..284c732c1 --- /dev/null +++ b/arch/ppc/kernel/walnut_setup.c @@ -0,0 +1,295 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Module name: walnut_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GP "Walnut" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek + * <dan@net4x.com>. + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/threads.h> +#include <linux/interrupt.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/processor.h> +#include <asm/board.h> +#include <asm/machdep.h> +#include <asm/page.h> + +#include "local_irq.h" +#include "ppc4xx_pic.h" +#include "time.h" +#include "walnut_setup.h" + + +/* Function Prototypes */ + +extern void abort(void); + +/* Global Variables */ + +unsigned char __res[sizeof(bd_t)]; + + +/* + * void __init walnut_init() + * + * Description: + * This routine... + * + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +walnut_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); + } + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = walnut_setup_arch; + ppc_md.setup_residual = walnut_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = walnut_init_IRQ; + ppc_md.get_irq = walnut_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = walnut_restart; + ppc_md.power_off = walnut_power_off; + ppc_md.halt = walnut_halt; + + ppc_md.time_init = walnut_time_init; + ppc_md.set_rtc_time = walnut_set_rtc_time; + ppc_md.get_rtc_time = walnut_get_rtc_time; + ppc_md.calibrate_decr = walnut_calibrate_decr; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + + return; +} + +/* + * Document me. + */ +void __init +walnut_setup_arch(void) +{ + /* XXX - Implement me */ +} + +/* + * int walnut_setup_residual() + * + * Description: + * This routine pretty-prints the platform's internal CPU and bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + * + * Input(s): + * *buffer - Buffer into which CPU and bus clock frequencies are to be + * printed. + * + * Output(s): + * *buffer - Buffer with the CPU and bus clock frequencies. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +walnut_setup_residual(char *buffer) +{ + int len = 0; + bd_t *bp = (bd_t *)__res; + + len += sprintf(len + buffer, + "clock\t\t: %dMHz\n" + "bus clock\t\t: %dMHz\n", + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000); + + return (len); +} + +/* + * Document me. + */ +void __init +walnut_init_IRQ(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = ppc4xx_pic; + } + + return; +} + +/* + * Document me. + */ +int +walnut_get_irq(struct pt_regs *regs) +{ + return (ppc4xx_pic_get_irq(regs)); +} + +/* + * Document me. + */ +void +walnut_restart(char *cmd) +{ + abort(); +} + +/* + * Document me. + */ +void +walnut_power_off(void) +{ + walnut_restart(NULL); +} + +/* + * Document me. + */ +void +walnut_halt(void) +{ + walnut_restart(NULL); +} + +/* + * Document me. + */ +void __init +walnut_time_init(void) +{ + /* XXX - Implement me */ +} + +/* + * Document me. + */ +int __init +walnut_set_rtc_time(unsigned long time) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * Document me. + */ +unsigned long __init +walnut_get_rtc_time(void) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * void __init walnut_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 403 programmable interval timer (PIT) and sets + * it up for auto-reload. + * + * Input(s): + * N/A + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +walnut_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = (bd_t *)__res; + + freq = bip->bi_intfreq; + + decrementer_count = freq / HZ; + count_period_num = 1; + count_period_den = freq; + + /* Enable the PIT and set auto-reload of its value */ + + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); +} diff --git a/arch/ppc/kernel/walnut_setup.h b/arch/ppc/kernel/walnut_setup.h new file mode 100644 index 000000000..a6e905333 --- /dev/null +++ b/arch/ppc/kernel/walnut_setup.h @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Module name: walnut_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 405GP "Walnut" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek + * <dan@netx4.com>. + * + */ + +#ifndef __WALNUT_SETUP_H__ +#define __WALNUT_SETUP_H__ + +#include <asm/ptrace.h> +#include <asm/board.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned char __res[sizeof(bd_t)]; + +extern void walnut_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void walnut_setup_arch(void); +extern int walnut_setup_residual(char *buffer); +extern void walnut_init_IRQ(void); +extern int walnut_get_irq(struct pt_regs *regs); +extern void walnut_restart(char *cmd); +extern void walnut_power_off(void); +extern void walnut_halt(void); +extern void walnut_time_init(void); +extern int walnut_set_rtc_time(unsigned long now); +extern unsigned long walnut_get_rtc_time(void); +extern void walnut_calibrate_decr(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __WALNUT_SETUP_H__ */ |